aircana 3.2.1 → 4.0.0.rc2
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/.rspec_status +168 -153
- data/CHANGELOG.md +55 -0
- data/CLAUDE.md +50 -39
- data/README.md +70 -76
- data/lib/aircana/cli/app.rb +16 -21
- data/lib/aircana/cli/commands/generate.rb +0 -12
- data/lib/aircana/cli/commands/kb.rb +590 -0
- data/lib/aircana/cli/help_formatter.rb +5 -4
- data/lib/aircana/configuration.rb +13 -28
- data/lib/aircana/contexts/confluence.rb +55 -24
- data/lib/aircana/contexts/confluence_content.rb +48 -5
- data/lib/aircana/contexts/local.rb +8 -9
- data/lib/aircana/contexts/manifest.rb +46 -34
- data/lib/aircana/contexts/web.rb +47 -17
- data/lib/aircana/generators/skills_generator.rb +194 -0
- data/lib/aircana/templates/skills/base_skill.erb +12 -0
- data/lib/aircana/version.rb +1 -1
- metadata +4 -18
- data/lib/aircana/cli/commands/agents.rb +0 -733
- data/lib/aircana/generators/agents_generator.rb +0 -79
- data/lib/aircana/templates/agents/base_agent.erb +0 -31
- data/lib/aircana/templates/agents/defaults/apply_feedback.erb +0 -91
- data/lib/aircana/templates/agents/defaults/executor.erb +0 -84
- data/lib/aircana/templates/agents/defaults/jira.erb +0 -45
- data/lib/aircana/templates/agents/defaults/planner.erb +0 -63
- data/lib/aircana/templates/agents/defaults/reviewer.erb +0 -94
- data/lib/aircana/templates/agents/defaults/sub-agent-coordinator.erb +0 -90
- data/lib/aircana/templates/hooks/refresh_agents.erb +0 -66
- data/lib/aircana/templates/hooks/sync_local_knowledge.erb +0 -86
- data/spec_target_1760656566_428/agents/test-agent/manifest.json +0 -16
- data/spec_target_1760656588_38/agents/test-agent/manifest.json +0 -16
- data/spec_target_1760656647_612/agents/test-agent/manifest.json +0 -16
- data/spec_target_1760656660_113/agents/test-agent/manifest.json +0 -16
- data/spec_target_1760656689_268/agents/test-agent/manifest.json +0 -16
- data/spec_target_1760656710_387/agents/test-agent/manifest.json +0 -16
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../generators"
|
4
|
+
require_relative "../contexts/manifest"
|
5
|
+
|
6
|
+
module Aircana
|
7
|
+
module Generators
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
9
|
+
class SkillsGenerator < BaseGenerator
|
10
|
+
attr_reader :kb_name, :short_description, :skill_description, :knowledge_files
|
11
|
+
|
12
|
+
# rubocop:disable Metrics/ParameterLists
|
13
|
+
def initialize(kb_name:, short_description: nil, skill_description: nil, knowledge_files: [], file_in: nil,
|
14
|
+
file_out: nil)
|
15
|
+
@kb_name = kb_name
|
16
|
+
@short_description = short_description
|
17
|
+
@skill_description = skill_description || generate_skill_description
|
18
|
+
@knowledge_files = knowledge_files
|
19
|
+
|
20
|
+
super(
|
21
|
+
file_in: file_in || default_template_path,
|
22
|
+
file_out: file_out || default_output_path
|
23
|
+
)
|
24
|
+
end
|
25
|
+
# rubocop:enable Metrics/ParameterLists
|
26
|
+
|
27
|
+
# Generate SKILL.md based on manifest data
|
28
|
+
# rubocop:disable Metrics/MethodLength
|
29
|
+
def self.from_manifest(kb_name)
|
30
|
+
manifest = Contexts::Manifest.read_manifest(kb_name)
|
31
|
+
raise Error, "No manifest found for knowledge base '#{kb_name}'" unless manifest
|
32
|
+
|
33
|
+
knowledge_files = extract_knowledge_files_from_manifest(manifest)
|
34
|
+
|
35
|
+
# Warn if no knowledge files were found
|
36
|
+
if knowledge_files.empty?
|
37
|
+
Aircana.human_logger.warn "No knowledge files found for KB '#{kb_name}'. " \
|
38
|
+
"SKILL.md will be generated but will be empty. " \
|
39
|
+
"Run 'aircana kb refresh #{kb_name}' to fetch knowledge."
|
40
|
+
end
|
41
|
+
|
42
|
+
skill_description = generate_skill_description_from_manifest(manifest, kb_name)
|
43
|
+
|
44
|
+
new(
|
45
|
+
kb_name: kb_name,
|
46
|
+
skill_description: skill_description,
|
47
|
+
knowledge_files: knowledge_files
|
48
|
+
)
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/MethodLength
|
51
|
+
|
52
|
+
# Class methods for manifest processing
|
53
|
+
def self.extract_knowledge_files_from_manifest(manifest)
|
54
|
+
kb_name = manifest["name"]
|
55
|
+
|
56
|
+
# If kb_dir exists, scan actual files on disk (preferred method)
|
57
|
+
if kb_name && Aircana.configuration.kb_knowledge_dir
|
58
|
+
kb_dir = Aircana.configuration.kb_path(kb_name)
|
59
|
+
|
60
|
+
return extract_files_from_disk(manifest, kb_dir) if Dir.exist?(kb_dir)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fallback: extract from manifest metadata (for tests or before files are created)
|
64
|
+
extract_files_from_manifest_metadata(manifest)
|
65
|
+
end
|
66
|
+
|
67
|
+
# rubocop:disable Metrics/MethodLength
|
68
|
+
def self.extract_files_from_disk(manifest, kb_dir)
|
69
|
+
actual_files = Dir.glob(File.join(kb_dir, "*.md"))
|
70
|
+
.reject { |f| File.basename(f) == "SKILL.md" }
|
71
|
+
.sort
|
72
|
+
|
73
|
+
# Build file list with summaries from manifest
|
74
|
+
actual_files.map do |filepath|
|
75
|
+
filename = File.basename(filepath)
|
76
|
+
summary = find_summary_for_file(manifest, filename)
|
77
|
+
|
78
|
+
{
|
79
|
+
summary: summary || File.basename(filename, ".md").tr("-", " ").capitalize,
|
80
|
+
filename: filename
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# rubocop:enable Metrics/MethodLength
|
85
|
+
|
86
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
87
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
88
|
+
def self.extract_files_from_manifest_metadata(manifest)
|
89
|
+
files = []
|
90
|
+
|
91
|
+
manifest["sources"]&.each do |source|
|
92
|
+
case source["type"]
|
93
|
+
when "confluence"
|
94
|
+
source["pages"]&.each do |page|
|
95
|
+
files << {
|
96
|
+
summary: page["summary"] || "Documentation",
|
97
|
+
filename: "#{sanitize_filename_from_id(page["id"])}.md"
|
98
|
+
}
|
99
|
+
end
|
100
|
+
when "web"
|
101
|
+
source["urls"]&.each do |url_entry|
|
102
|
+
files << {
|
103
|
+
summary: url_entry["summary"] || "Web resource",
|
104
|
+
filename: "#{sanitize_filename_from_url(url_entry["url"])}.md"
|
105
|
+
}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
files
|
111
|
+
end
|
112
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
113
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
114
|
+
|
115
|
+
# Find the summary for a given filename from the manifest
|
116
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
117
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
118
|
+
def self.find_summary_for_file(manifest, filename)
|
119
|
+
manifest["sources"]&.each do |source|
|
120
|
+
case source["type"]
|
121
|
+
when "confluence"
|
122
|
+
source["pages"]&.each do |page|
|
123
|
+
return page["summary"] if filename.include?(page["id"])
|
124
|
+
end
|
125
|
+
when "web"
|
126
|
+
source["urls"]&.each do |url_entry|
|
127
|
+
sanitized_url_part = sanitize_filename_from_url(url_entry["url"])
|
128
|
+
return url_entry["summary"] if filename.include?(sanitized_url_part)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
136
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
137
|
+
|
138
|
+
def self.generate_skill_description_from_manifest(manifest, kb_name)
|
139
|
+
# Generate a description optimized for Claude's skill discovery
|
140
|
+
source_count = manifest["sources"]&.size || 0
|
141
|
+
"Discover critical context for #{kb_name.split("-").join(" ")} from #{source_count} knowledge sources"
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.sanitize_filename_from_id(page_id)
|
145
|
+
"page_#{page_id}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.sanitize_filename_from_url(url)
|
149
|
+
# Extract meaningful name from URL
|
150
|
+
uri = URI.parse(url)
|
151
|
+
path_segments = uri.path.split("/").reject(&:empty?)
|
152
|
+
|
153
|
+
if path_segments.any?
|
154
|
+
path_segments.last.gsub(/[^a-z0-9\-_]/i, "-").gsub(/-+/, "-").downcase
|
155
|
+
else
|
156
|
+
uri.host.gsub(/[^a-z0-9\-_]/i, "-").gsub(/-+/, "-").downcase
|
157
|
+
end
|
158
|
+
rescue URI::InvalidURIError
|
159
|
+
"web_resource"
|
160
|
+
end
|
161
|
+
# rubocop:enable Metrics/ClassLength
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
def locals
|
166
|
+
super.merge({
|
167
|
+
kb_name:,
|
168
|
+
skill_description:,
|
169
|
+
knowledge_files:
|
170
|
+
})
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def default_template_path
|
176
|
+
File.join(File.dirname(__FILE__), "..", "templates", "skills", "base_skill.erb")
|
177
|
+
end
|
178
|
+
|
179
|
+
def default_output_path
|
180
|
+
File.join(Aircana.configuration.skills_dir, kb_name, "SKILL.md")
|
181
|
+
end
|
182
|
+
|
183
|
+
def generate_skill_description
|
184
|
+
return @skill_description if @skill_description
|
185
|
+
|
186
|
+
if @short_description
|
187
|
+
"Discover critical context for #{short_description}"
|
188
|
+
else
|
189
|
+
"Discover critical context for #{kb_name.split("-").join(" ")}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
---
|
2
|
+
name: Learn <%= kb_name.split("-").map(&:capitalize).join(" ") %>
|
3
|
+
description: <%= skill_description %>
|
4
|
+
---
|
5
|
+
|
6
|
+
# Learn <%= kb_name.split("-").map(&:capitalize).join(" ") %>
|
7
|
+
|
8
|
+
## Instructions
|
9
|
+
Choose a sub-topic most relevant to your current task:
|
10
|
+
<% knowledge_files.each do |file| %>
|
11
|
+
* <%= file[:summary] %>: [<%= file[:filename] %>](<%= file[:filename] %>)
|
12
|
+
<% end %>
|
data/lib/aircana/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aircana
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Weston Dransfield
|
@@ -127,7 +127,6 @@ files:
|
|
127
127
|
- lib/aircana.rb
|
128
128
|
- lib/aircana/cli.rb
|
129
129
|
- lib/aircana/cli/app.rb
|
130
|
-
- lib/aircana/cli/commands/agents.rb
|
131
130
|
- lib/aircana/cli/commands/doctor.rb
|
132
131
|
- lib/aircana/cli/commands/doctor_checks.rb
|
133
132
|
- lib/aircana/cli/commands/doctor_helpers.rb
|
@@ -135,6 +134,7 @@ files:
|
|
135
134
|
- lib/aircana/cli/commands/generate.rb
|
136
135
|
- lib/aircana/cli/commands/hooks.rb
|
137
136
|
- lib/aircana/cli/commands/init.rb
|
137
|
+
- lib/aircana/cli/commands/kb.rb
|
138
138
|
- lib/aircana/cli/commands/plugin.rb
|
139
139
|
- lib/aircana/cli/help_formatter.rb
|
140
140
|
- lib/aircana/cli/shell_command.rb
|
@@ -150,7 +150,6 @@ files:
|
|
150
150
|
- lib/aircana/contexts/web.rb
|
151
151
|
- lib/aircana/fzf_helper.rb
|
152
152
|
- lib/aircana/generators.rb
|
153
|
-
- lib/aircana/generators/agents_generator.rb
|
154
153
|
- lib/aircana/generators/apply_feedback_command_generator.rb
|
155
154
|
- lib/aircana/generators/ask_expert_command_generator.rb
|
156
155
|
- lib/aircana/generators/base_generator.rb
|
@@ -160,6 +159,7 @@ files:
|
|
160
159
|
- lib/aircana/generators/plan_command_generator.rb
|
161
160
|
- lib/aircana/generators/record_command_generator.rb
|
162
161
|
- lib/aircana/generators/review_command_generator.rb
|
162
|
+
- lib/aircana/generators/skills_generator.rb
|
163
163
|
- lib/aircana/hooks_manifest.rb
|
164
164
|
- lib/aircana/human_logger.rb
|
165
165
|
- lib/aircana/initializers.rb
|
@@ -167,13 +167,6 @@ files:
|
|
167
167
|
- lib/aircana/plugin_manifest.rb
|
168
168
|
- lib/aircana/progress_tracker.rb
|
169
169
|
- lib/aircana/system_checker.rb
|
170
|
-
- lib/aircana/templates/agents/base_agent.erb
|
171
|
-
- lib/aircana/templates/agents/defaults/apply_feedback.erb
|
172
|
-
- lib/aircana/templates/agents/defaults/executor.erb
|
173
|
-
- lib/aircana/templates/agents/defaults/jira.erb
|
174
|
-
- lib/aircana/templates/agents/defaults/planner.erb
|
175
|
-
- lib/aircana/templates/agents/defaults/reviewer.erb
|
176
|
-
- lib/aircana/templates/agents/defaults/sub-agent-coordinator.erb
|
177
170
|
- lib/aircana/templates/commands/apply_feedback.erb
|
178
171
|
- lib/aircana/templates/commands/ask_expert.erb
|
179
172
|
- lib/aircana/templates/commands/execute.erb
|
@@ -184,20 +177,13 @@ files:
|
|
184
177
|
- lib/aircana/templates/hooks/notification_sqs.erb
|
185
178
|
- lib/aircana/templates/hooks/post_tool_use.erb
|
186
179
|
- lib/aircana/templates/hooks/pre_tool_use.erb
|
187
|
-
- lib/aircana/templates/hooks/refresh_agents.erb
|
188
180
|
- lib/aircana/templates/hooks/rspec_test.erb
|
189
181
|
- lib/aircana/templates/hooks/rubocop_pre_commit.erb
|
190
182
|
- lib/aircana/templates/hooks/session_start.erb
|
191
|
-
- lib/aircana/templates/hooks/sync_local_knowledge.erb
|
192
183
|
- lib/aircana/templates/hooks/user_prompt_submit.erb
|
184
|
+
- lib/aircana/templates/skills/base_skill.erb
|
193
185
|
- lib/aircana/version.rb
|
194
186
|
- sig/aircana.rbs
|
195
|
-
- spec_target_1760656566_428/agents/test-agent/manifest.json
|
196
|
-
- spec_target_1760656588_38/agents/test-agent/manifest.json
|
197
|
-
- spec_target_1760656647_612/agents/test-agent/manifest.json
|
198
|
-
- spec_target_1760656660_113/agents/test-agent/manifest.json
|
199
|
-
- spec_target_1760656689_268/agents/test-agent/manifest.json
|
200
|
-
- spec_target_1760656710_387/agents/test-agent/manifest.json
|
201
187
|
homepage: https://github.com/westonkd/aircana
|
202
188
|
licenses:
|
203
189
|
- MIT
|