agentic 0.1.0 → 0.2.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 +4 -4
- data/.agentic.yml +2 -0
- data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
- data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
- data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
- data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
- data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
- data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
- data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
- data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
- data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
- data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
- data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
- data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
- data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
- data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
- data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
- data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
- data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
- data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
- data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
- data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
- data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
- data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
- data/.architecture/decisions/capability_tools_distinction.md +150 -0
- data/.architecture/decisions/cli_command_structure.md +61 -0
- data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
- data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
- data/.architecture/members.yml +187 -0
- data/.architecture/planning/self_implementation_exercise.md +295 -0
- data/.architecture/planning/session_compaction_rule.md +43 -0
- data/.architecture/planning/streaming_observability_feature.md +223 -0
- data/.architecture/principles.md +151 -0
- data/.architecture/recalibration/0-2-0.md +92 -0
- data/.architecture/recalibration/agent_self_assembly.md +238 -0
- data/.architecture/recalibration/cli_command_structure.md +91 -0
- data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
- data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
- data/.architecture/recalibration_process.md +127 -0
- data/.architecture/reviews/0-2-0.md +181 -0
- data/.architecture/reviews/cli_command_duplication.md +98 -0
- data/.architecture/templates/adr.md +105 -0
- data/.architecture/templates/implementation_roadmap.md +125 -0
- data/.architecture/templates/progress_tracking.md +89 -0
- data/.architecture/templates/recalibration_plan.md +70 -0
- data/.architecture/templates/version_comparison.md +124 -0
- data/.claude/settings.local.json +13 -0
- data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
- data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
- data/.claude-sessions/architecture-governance-implementation.md +37 -0
- data/.claude-sessions/architecture-review-session.md +27 -0
- data/ArchitecturalFeatureBuilder.md +136 -0
- data/ArchitectureConsiderations.md +229 -0
- data/CHANGELOG.md +57 -2
- data/CLAUDE.md +111 -0
- data/CONTRIBUTING.md +286 -0
- data/MAINTAINING.md +301 -0
- data/README.md +582 -28
- data/docs/agent_capabilities_api.md +259 -0
- data/docs/artifact_extension_points.md +757 -0
- data/docs/artifact_generation_architecture.md +323 -0
- data/docs/artifact_implementation_plan.md +596 -0
- data/docs/artifact_integration_points.md +345 -0
- data/docs/artifact_verification_strategies.md +581 -0
- data/docs/streaming_observability_architecture.md +510 -0
- data/exe/agentic +6 -1
- data/lefthook.yml +5 -0
- data/lib/agentic/adaptation_engine.rb +124 -0
- data/lib/agentic/agent.rb +181 -4
- data/lib/agentic/agent_assembly_engine.rb +442 -0
- data/lib/agentic/agent_capability_registry.rb +260 -0
- data/lib/agentic/agent_config.rb +63 -0
- data/lib/agentic/agent_specification.rb +46 -0
- data/lib/agentic/capabilities/examples.rb +530 -0
- data/lib/agentic/capabilities.rb +14 -0
- data/lib/agentic/capability_provider.rb +146 -0
- data/lib/agentic/capability_specification.rb +118 -0
- data/lib/agentic/cli/agent.rb +31 -0
- data/lib/agentic/cli/capabilities.rb +191 -0
- data/lib/agentic/cli/config.rb +134 -0
- data/lib/agentic/cli/execution_observer.rb +796 -0
- data/lib/agentic/cli.rb +1068 -0
- data/lib/agentic/default_agent_provider.rb +35 -0
- data/lib/agentic/errors/llm_error.rb +184 -0
- data/lib/agentic/execution_plan.rb +53 -0
- data/lib/agentic/execution_result.rb +91 -0
- data/lib/agentic/expected_answer_format.rb +46 -0
- data/lib/agentic/extension/domain_adapter.rb +109 -0
- data/lib/agentic/extension/plugin_manager.rb +163 -0
- data/lib/agentic/extension/protocol_handler.rb +116 -0
- data/lib/agentic/extension.rb +45 -0
- data/lib/agentic/factory_methods.rb +9 -1
- data/lib/agentic/generation_stats.rb +61 -0
- data/lib/agentic/learning/README.md +84 -0
- data/lib/agentic/learning/capability_optimizer.rb +613 -0
- data/lib/agentic/learning/execution_history_store.rb +251 -0
- data/lib/agentic/learning/pattern_recognizer.rb +500 -0
- data/lib/agentic/learning/strategy_optimizer.rb +706 -0
- data/lib/agentic/learning.rb +131 -0
- data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
- data/lib/agentic/llm_client.rb +215 -15
- data/lib/agentic/llm_config.rb +65 -1
- data/lib/agentic/llm_response.rb +163 -0
- data/lib/agentic/logger.rb +1 -1
- data/lib/agentic/observable.rb +51 -0
- data/lib/agentic/persistent_agent_store.rb +385 -0
- data/lib/agentic/plan_execution_result.rb +129 -0
- data/lib/agentic/plan_orchestrator.rb +464 -0
- data/lib/agentic/plan_orchestrator_config.rb +57 -0
- data/lib/agentic/retry_config.rb +63 -0
- data/lib/agentic/retry_handler.rb +125 -0
- data/lib/agentic/structured_outputs.rb +1 -1
- data/lib/agentic/task.rb +193 -0
- data/lib/agentic/task_definition.rb +39 -0
- data/lib/agentic/task_execution_result.rb +92 -0
- data/lib/agentic/task_failure.rb +66 -0
- data/lib/agentic/task_output_schemas.rb +112 -0
- data/lib/agentic/task_planner.rb +54 -19
- data/lib/agentic/task_result.rb +48 -0
- data/lib/agentic/ui.rb +244 -0
- data/lib/agentic/verification/critic_framework.rb +116 -0
- data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
- data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
- data/lib/agentic/verification/verification_hub.rb +62 -0
- data/lib/agentic/verification/verification_result.rb +50 -0
- data/lib/agentic/verification/verification_strategy.rb +26 -0
- data/lib/agentic/version.rb +1 -1
- data/lib/agentic.rb +74 -2
- data/plugins/README.md +41 -0
- metadata +245 -6
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Agentic
|
4
|
+
# Defines the specification for an agent capability
|
5
|
+
# @attr_reader [String] name The name of the capability
|
6
|
+
# @attr_reader [String] description Description of the capability
|
7
|
+
# @attr_reader [String] version The version of the capability
|
8
|
+
# @attr_reader [Hash] inputs The required inputs for the capability
|
9
|
+
# @attr_reader [Hash] outputs The expected outputs from the capability
|
10
|
+
# @attr_reader [Array<Hash>] dependencies The dependencies of the capability
|
11
|
+
class CapabilitySpecification
|
12
|
+
attr_reader :name, :description, :version, :inputs, :outputs, :dependencies
|
13
|
+
|
14
|
+
# Initialize a new capability specification
|
15
|
+
# @param name [String] The name of the capability
|
16
|
+
# @param description [String] Description of the capability
|
17
|
+
# @param version [String] The version of the capability
|
18
|
+
# @param inputs [Hash] The required inputs for the capability
|
19
|
+
# @param outputs [Hash] The expected outputs from the capability
|
20
|
+
# @param dependencies [Array<Hash>] The dependencies of the capability
|
21
|
+
def initialize(name:, description:, version:, inputs: {}, outputs: {}, dependencies: [])
|
22
|
+
@name = name
|
23
|
+
@description = description
|
24
|
+
@version = version
|
25
|
+
@inputs = inputs
|
26
|
+
@outputs = outputs
|
27
|
+
@dependencies = dependencies
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if this capability is compatible with another capability
|
31
|
+
# @param other [CapabilitySpecification] The other capability
|
32
|
+
# @return [Boolean] True if compatible
|
33
|
+
def compatible_with?(other)
|
34
|
+
return false unless other.is_a?(CapabilitySpecification)
|
35
|
+
return false unless name == other.name
|
36
|
+
|
37
|
+
# Compare versions using semantic versioning rules
|
38
|
+
# For now, just check for exact match or higher minor version
|
39
|
+
return true if version == other.version
|
40
|
+
|
41
|
+
begin
|
42
|
+
my_parts = version.split(".").map(&:to_i)
|
43
|
+
other_parts = other.version.split(".").map(&:to_i)
|
44
|
+
|
45
|
+
# Major version must match
|
46
|
+
return false unless my_parts[0] == other_parts[0]
|
47
|
+
|
48
|
+
# Our minor version should be >= other's minor version
|
49
|
+
my_parts[1] >= other_parts[1]
|
50
|
+
rescue
|
51
|
+
# If version parsing fails, require exact match
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Convert to a hash representation
|
57
|
+
# @return [Hash] The hash representation
|
58
|
+
def to_h
|
59
|
+
{
|
60
|
+
name: @name,
|
61
|
+
description: @description,
|
62
|
+
version: @version,
|
63
|
+
inputs: @inputs,
|
64
|
+
outputs: @outputs,
|
65
|
+
dependencies: @dependencies
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Create from a hash representation
|
70
|
+
# @param hash [Hash] The hash representation
|
71
|
+
# @return [CapabilitySpecification] The capability specification
|
72
|
+
def self.from_h(hash)
|
73
|
+
new(
|
74
|
+
name: hash[:name] || hash["name"],
|
75
|
+
description: hash[:description] || hash["description"],
|
76
|
+
version: hash[:version] || hash["version"],
|
77
|
+
inputs: hash[:inputs] || hash["inputs"] || {},
|
78
|
+
outputs: hash[:outputs] || hash["outputs"] || {},
|
79
|
+
dependencies: hash[:dependencies] || hash["dependencies"] || []
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get the capability requirements as a human-readable string
|
84
|
+
# @return [String] The capability requirements
|
85
|
+
def requirements_description
|
86
|
+
result = "Capability: #{name} (v#{version})\n"
|
87
|
+
result += "Description: #{description}\n"
|
88
|
+
|
89
|
+
unless inputs.empty?
|
90
|
+
result += "\nInputs:\n"
|
91
|
+
inputs.each do |name, spec|
|
92
|
+
result += " #{name}: #{spec[:type] || "any"}"
|
93
|
+
result += " (required)" if spec[:required]
|
94
|
+
result += " - #{spec[:description]}" if spec[:description]
|
95
|
+
result += "\n"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
unless outputs.empty?
|
100
|
+
result += "\nOutputs:\n"
|
101
|
+
outputs.each do |name, spec|
|
102
|
+
result += " #{name}: #{spec[:type] || "any"}"
|
103
|
+
result += " - #{spec[:description]}" if spec[:description]
|
104
|
+
result += "\n"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
unless dependencies.empty?
|
109
|
+
result += "\nDependencies:\n"
|
110
|
+
dependencies.each do |dep|
|
111
|
+
result += " #{dep[:name]} (v#{dep[:version] || "any"})\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
result
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Agentic
|
4
|
+
class CLI < Thor
|
5
|
+
# CLI commands for managing agents
|
6
|
+
class Agent < Thor
|
7
|
+
desc "list", "List available agents"
|
8
|
+
def list
|
9
|
+
puts "Available agents:"
|
10
|
+
# In a future implementation, this would list agents from a registry
|
11
|
+
puts " - No custom agents registered yet"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "create NAME", "Create a new agent"
|
15
|
+
option :role, type: :string, required: true, desc: "Role of the agent"
|
16
|
+
option :instructions, type: :string, required: true, desc: "Instructions for the agent"
|
17
|
+
def create(name)
|
18
|
+
puts "Creating agent: #{name}"
|
19
|
+
# In a future implementation, this would create and register an agent
|
20
|
+
puts "Agent created successfully."
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "delete NAME", "Delete an agent"
|
24
|
+
def delete(name)
|
25
|
+
puts "Deleting agent: #{name}"
|
26
|
+
# In a future implementation, this would delete an agent from a registry
|
27
|
+
puts "Agent deleted successfully."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Agentic
|
4
|
+
class CLI < Thor
|
5
|
+
# Command-line interface for managing capabilities
|
6
|
+
class Capabilities < Thor
|
7
|
+
desc "list", "List available capabilities"
|
8
|
+
option :detailed, type: :boolean, aliases: "-d",
|
9
|
+
desc: "Show detailed information"
|
10
|
+
def list
|
11
|
+
# Initialize agent assembly system
|
12
|
+
Agentic.initialize_agent_assembly
|
13
|
+
registry = Agentic.agent_capability_registry
|
14
|
+
|
15
|
+
capabilities = registry.list(include_providers: options[:detailed])
|
16
|
+
|
17
|
+
if capabilities.empty?
|
18
|
+
puts UI.box(
|
19
|
+
"Available Capabilities",
|
20
|
+
"No capabilities registered yet.\n\n" \
|
21
|
+
"You can register capabilities programmatically via Agentic.register_capability.",
|
22
|
+
padding: [1, 2, 1, 2],
|
23
|
+
style: {border: {fg: :blue}}
|
24
|
+
)
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
output = ""
|
29
|
+
capabilities.each do |name, info|
|
30
|
+
output += "#{UI.colorize(name, :blue)}:\n"
|
31
|
+
output += " Available versions: #{info[:versions].join(", ")}\n"
|
32
|
+
output += " Latest version: #{UI.colorize(info[:latest], :green)}\n"
|
33
|
+
|
34
|
+
if options[:detailed] && info[:providers]
|
35
|
+
# Get a capability to show more details
|
36
|
+
capability = registry.get(name, info[:latest])
|
37
|
+
if capability
|
38
|
+
output += " Description: #{capability.description}\n"
|
39
|
+
|
40
|
+
unless capability.inputs.empty?
|
41
|
+
output += " Inputs:\n"
|
42
|
+
capability.inputs.each do |input_name, input_spec|
|
43
|
+
required = input_spec[:required] ? " (required)" : ""
|
44
|
+
output += " - #{input_name}#{required}: #{input_spec[:type] || "any"}\n"
|
45
|
+
output += " #{input_spec[:description]}\n" if input_spec[:description]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
unless capability.outputs.empty?
|
50
|
+
output += " Outputs:\n"
|
51
|
+
capability.outputs.each do |output_name, output_spec|
|
52
|
+
output += " - #{output_name}: #{output_spec[:type] || "any"}\n"
|
53
|
+
output += " #{output_spec[:description]}\n" if output_spec[:description]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
unless capability.dependencies.empty?
|
58
|
+
output += " Dependencies:\n"
|
59
|
+
capability.dependencies.each do |dep|
|
60
|
+
output += " - #{dep[:name]} (#{dep[:version] || "any version"})\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
output += "\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
puts UI.box(
|
70
|
+
"Available Capabilities",
|
71
|
+
output,
|
72
|
+
padding: [1, 2, 1, 2],
|
73
|
+
style: {border: {fg: :blue}}
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "show NAME", "Show details of a specific capability"
|
78
|
+
option :version, type: :string, aliases: "-v",
|
79
|
+
desc: "Capability version (defaults to latest)"
|
80
|
+
def show(name)
|
81
|
+
# Initialize agent assembly system
|
82
|
+
Agentic.initialize_agent_assembly
|
83
|
+
registry = Agentic.agent_capability_registry
|
84
|
+
|
85
|
+
capability = registry.get(name, options[:version])
|
86
|
+
|
87
|
+
unless capability
|
88
|
+
available = registry.list.keys.join(", ")
|
89
|
+
available_text = available.empty? ? "No capabilities registered yet." : "Available: #{available}"
|
90
|
+
|
91
|
+
puts UI.box(
|
92
|
+
"Error",
|
93
|
+
"Capability '#{UI.colorize(name, :yellow)}' not found.\n\n#{available_text}",
|
94
|
+
padding: [1, 2, 1, 2],
|
95
|
+
style: {border: {fg: :red}}
|
96
|
+
)
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
output = ""
|
101
|
+
output += "Name: #{UI.colorize(capability.name, :blue)}\n"
|
102
|
+
output += "Version: #{UI.colorize(capability.version, :green)}\n"
|
103
|
+
output += "Description: #{capability.description}\n\n"
|
104
|
+
|
105
|
+
unless capability.inputs.empty?
|
106
|
+
output += "Inputs:\n"
|
107
|
+
capability.inputs.each do |input_name, input_spec|
|
108
|
+
required = input_spec[:required] ? " (required)" : ""
|
109
|
+
output += " - #{UI.colorize(input_name, :yellow)}#{required}: #{input_spec[:type] || "any"}\n"
|
110
|
+
output += " #{input_spec[:description]}\n" if input_spec[:description]
|
111
|
+
end
|
112
|
+
output += "\n"
|
113
|
+
end
|
114
|
+
|
115
|
+
unless capability.outputs.empty?
|
116
|
+
output += "Outputs:\n"
|
117
|
+
capability.outputs.each do |output_name, output_spec|
|
118
|
+
output += " - #{UI.colorize(output_name, :yellow)}: #{output_spec[:type] || "any"}\n"
|
119
|
+
output += " #{output_spec[:description]}\n" if output_spec[:description]
|
120
|
+
end
|
121
|
+
output += "\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
unless capability.dependencies.empty?
|
125
|
+
output += "Dependencies:\n"
|
126
|
+
capability.dependencies.each do |dep|
|
127
|
+
output += " - #{UI.colorize(dep[:name], :magenta)} (#{dep[:version] || "any version"})\n"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
puts UI.box(
|
132
|
+
"Capability Details",
|
133
|
+
output,
|
134
|
+
padding: [1, 2, 1, 2],
|
135
|
+
style: {border: {fg: :blue}}
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "search QUERY", "Search for capabilities"
|
140
|
+
def search(query)
|
141
|
+
# Initialize agent assembly system
|
142
|
+
Agentic.initialize_agent_assembly
|
143
|
+
registry = Agentic.agent_capability_registry
|
144
|
+
|
145
|
+
# Search by name and description
|
146
|
+
capabilities = registry.list
|
147
|
+
results = {}
|
148
|
+
|
149
|
+
capabilities.each do |name, info|
|
150
|
+
# Check if query matches capability name
|
151
|
+
if name.downcase.include?(query.downcase)
|
152
|
+
results[name] = info
|
153
|
+
next
|
154
|
+
end
|
155
|
+
|
156
|
+
# Check if query matches capability description
|
157
|
+
capability = registry.get(name, info[:latest])
|
158
|
+
if capability && capability.description.downcase.include?(query.downcase)
|
159
|
+
results[name] = info
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
if results.empty?
|
164
|
+
puts UI.box(
|
165
|
+
"Search Results",
|
166
|
+
"No capabilities found matching '#{UI.colorize(query, :yellow)}'.",
|
167
|
+
padding: [1, 2, 1, 2],
|
168
|
+
style: {border: {fg: :blue}}
|
169
|
+
)
|
170
|
+
return
|
171
|
+
end
|
172
|
+
|
173
|
+
output = ""
|
174
|
+
results.each do |name, info|
|
175
|
+
capability = registry.get(name, info[:latest])
|
176
|
+
|
177
|
+
output += "#{UI.colorize(name, :blue)}:\n"
|
178
|
+
output += " Latest version: #{UI.colorize(info[:latest], :green)}\n"
|
179
|
+
output += " Description: #{capability.description}\n\n"
|
180
|
+
end
|
181
|
+
|
182
|
+
puts UI.box(
|
183
|
+
"Search Results",
|
184
|
+
output,
|
185
|
+
padding: [1, 2, 1, 2],
|
186
|
+
style: {border: {fg: :blue}}
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module Agentic
|
7
|
+
class CLI < Thor
|
8
|
+
# CLI commands for managing configuration
|
9
|
+
class Config < Thor
|
10
|
+
CONFIG_FILE_NAME = ".agentic.yml"
|
11
|
+
USER_CONFIG_PATH = File.join(Dir.home, CONFIG_FILE_NAME)
|
12
|
+
PROJECT_CONFIG_PATH = File.join(Dir.pwd, CONFIG_FILE_NAME)
|
13
|
+
|
14
|
+
desc "list", "List configuration settings"
|
15
|
+
def list
|
16
|
+
user_config = load_config(USER_CONFIG_PATH)
|
17
|
+
project_config = load_config(PROJECT_CONFIG_PATH)
|
18
|
+
|
19
|
+
puts "User configuration (#{USER_CONFIG_PATH}):"
|
20
|
+
print_config(user_config)
|
21
|
+
|
22
|
+
puts "\nProject configuration (#{PROJECT_CONFIG_PATH}):"
|
23
|
+
print_config(project_config)
|
24
|
+
|
25
|
+
puts "\nActive configuration:"
|
26
|
+
print_config(active_config)
|
27
|
+
|
28
|
+
puts "\nEnvironment variables:"
|
29
|
+
puts " OPENAI_ACCESS_TOKEN: #{ENV["OPENAI_ACCESS_TOKEN"] ? "[SET]" : "[NOT SET]"}"
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "get KEY", "Get a configuration setting"
|
33
|
+
def get(key)
|
34
|
+
config = active_config
|
35
|
+
value = config[key]
|
36
|
+
|
37
|
+
if value
|
38
|
+
puts value
|
39
|
+
else
|
40
|
+
puts "Key '#{key}' not found in configuration"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "set KEY=VALUE", "Set a configuration setting"
|
46
|
+
option :global, type: :boolean, aliases: "-g",
|
47
|
+
desc: "Set in global user config instead of project config"
|
48
|
+
def set(key_value)
|
49
|
+
key, value = key_value.split("=", 2)
|
50
|
+
|
51
|
+
unless value
|
52
|
+
puts "Error: Invalid format. Use KEY=VALUE"
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
path = options[:global] ? USER_CONFIG_PATH : PROJECT_CONFIG_PATH
|
57
|
+
config = load_config(path) || {}
|
58
|
+
|
59
|
+
# Convert string values to appropriate types
|
60
|
+
value = case value.downcase
|
61
|
+
when "true" then true
|
62
|
+
when "false" then false
|
63
|
+
when /^\d+$/ then value.to_i
|
64
|
+
when /^\d+\.\d+$/ then value.to_f
|
65
|
+
else value
|
66
|
+
end
|
67
|
+
|
68
|
+
config[key] = value
|
69
|
+
save_config(path, config)
|
70
|
+
|
71
|
+
puts "Configuration updated successfully."
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "init", "Initialize configuration"
|
75
|
+
option :global, type: :boolean, aliases: "-g",
|
76
|
+
desc: "Initialize global user config instead of project config"
|
77
|
+
def init
|
78
|
+
path = options[:global] ? USER_CONFIG_PATH : PROJECT_CONFIG_PATH
|
79
|
+
|
80
|
+
if File.exist?(path)
|
81
|
+
puts "Configuration already exists at #{path}"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
config = {
|
86
|
+
"model" => "gpt-4o-mini"
|
87
|
+
# Add other default configuration options here
|
88
|
+
}
|
89
|
+
|
90
|
+
save_config(path, config)
|
91
|
+
puts "Configuration initialized at #{path}"
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def load_config(path)
|
97
|
+
return unless File.exist?(path)
|
98
|
+
|
99
|
+
begin
|
100
|
+
YAML.load_file(path)
|
101
|
+
rescue => e
|
102
|
+
puts "Error loading configuration from #{path}: #{e.message}"
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def save_config(path, config)
|
108
|
+
# Create directory if it doesn't exist
|
109
|
+
dir = File.dirname(path)
|
110
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
111
|
+
|
112
|
+
File.write(path, YAML.dump(config))
|
113
|
+
end
|
114
|
+
|
115
|
+
def active_config
|
116
|
+
# Combine user and project configs, with project taking precedence
|
117
|
+
user_config = load_config(USER_CONFIG_PATH) || {}
|
118
|
+
project_config = load_config(PROJECT_CONFIG_PATH) || {}
|
119
|
+
|
120
|
+
user_config.merge(project_config)
|
121
|
+
end
|
122
|
+
|
123
|
+
def print_config(config)
|
124
|
+
if config.nil? || config.empty?
|
125
|
+
puts " [empty]"
|
126
|
+
else
|
127
|
+
config.each do |key, value|
|
128
|
+
puts " #{key}: #{value}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|