aircana 4.0.0.rc1 → 4.0.0.rc3
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 +185 -201
- data/CHANGELOG.md +31 -0
- data/CLAUDE.md +7 -8
- data/README.md +4 -13
- data/lib/aircana/cli/app.rb +0 -31
- data/lib/aircana/cli/commands/generate.rb +19 -14
- data/lib/aircana/cli/commands/init.rb +3 -31
- data/lib/aircana/cli/commands/kb.rb +21 -10
- data/lib/aircana/cli/help_formatter.rb +1 -2
- data/lib/aircana/contexts/confluence_content.rb +44 -1
- data/lib/aircana/generators/hooks_generator.rb +2 -2
- data/lib/aircana/generators/skills_generator.rb +69 -1
- data/lib/aircana/templates/hooks/refresh_skills.erb +121 -0
- data/lib/aircana/version.rb +1 -1
- metadata +4 -11
- data/commands/apply-feedback.md +0 -15
- data/commands/ask-expert.md +0 -42
- data/commands/execute.md +0 -13
- data/commands/plan.md +0 -33
- data/commands/record.md +0 -17
- data/commands/review.md +0 -12
- data/hooks/hooks.json +0 -31
- data/lib/aircana/cli/commands/hooks.rb +0 -276
data/commands/plan.md
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
INSTRUCTIONS : First, ask the user to specify relevant files, directories, or areas of the codebase to examine for this planning task.
|
2
|
-
|
3
|
-
Then use the Task tool with subagent_type 'planner' to invoke the planner agent with the following explicit instructions:
|
4
|
-
|
5
|
-
STEP 1: CREATE TODO LIST FILE
|
6
|
-
First, create a todo list file with the following tasks enumerated in order:
|
7
|
-
|
8
|
-
1. Ask user for relevant files and context (if not already provided)
|
9
|
-
2. Use Task tool with subagent_type 'jira' to verify Jira ticket information (or ask user to create/provide ticket)
|
10
|
-
3. Perform targeted initial research on user-specified files
|
11
|
-
4. Consult relevant sub-agents with research context (run in parallel when possible)
|
12
|
-
5. Create high-level strategic implementation plan
|
13
|
-
6. Iterate with user feedback
|
14
|
-
7. Suggest user runs '/air-record' command to save the plan to Jira ticket
|
15
|
-
|
16
|
-
STEP 2: EXECUTE EACH TASK IN ORDER
|
17
|
-
Work through each task in the todo list sequentially:
|
18
|
-
- Mark each task as 'in_progress' when you start it
|
19
|
-
- Mark each task as 'completed' when finished
|
20
|
-
- Continue until all tasks are done
|
21
|
-
|
22
|
-
IMPORTANT CONTEXT-SHARING PROTOCOL:
|
23
|
-
- When consulting sub-agents, explicitly provide: files already searched, files already read, key findings, and specific focus area
|
24
|
-
- This prevents sub-agents from duplicating research work
|
25
|
-
|
26
|
-
IMPORTANT PLAN CONSTRAINTS:
|
27
|
-
- Focus on strategic, high-level implementation guidance
|
28
|
-
- NO rollout plans, effort estimates, or exhaustive code implementations
|
29
|
-
- Small code examples (5-10 lines) are OK to illustrate concepts
|
30
|
-
|
31
|
-
User specified relevant files/areas: [User will specify]
|
32
|
-
|
33
|
-
Ask the user to provide a Jira ticket or task description.
|
data/commands/record.md
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
INSTRUCTIONS : Use the Task tool with subagent_type 'jira' to append the implementation plan to the Jira ticket description using Jira MCP tools.
|
2
|
-
|
3
|
-
INSTRUCTIONS FOR JIRA AGENT:
|
4
|
-
1. Read the implementation plan from the current context (look for recent planning output)
|
5
|
-
2. Get the current ticket description using mcp__jira__getJiraIssue with fields=["description"]
|
6
|
-
3. Append the plan to the existing description under a new "## Implementation Plan" heading
|
7
|
-
4. Update the ticket using mcp__jira__editJiraIssue with the combined description
|
8
|
-
5. Format the appended plan section with:
|
9
|
-
- Consulted sub-agents
|
10
|
-
- Relevant files analyzed
|
11
|
-
- Planning timestamp
|
12
|
-
- Implementation steps as todo list using `[ ]` format
|
13
|
-
6. Provide the ticket URL for easy access
|
14
|
-
|
15
|
-
CRITICAL: The jira sub-agent must append to the ticket description using mcp__jira__editJiraIssue (NOT create files or comments).
|
16
|
-
|
17
|
-
IMPORTANT: Delegate all Jira operations to the 'jira' sub-agent using Task tool with subagent_type 'jira'. The Jira ticket key/ID is already in context.
|
data/commands/review.md
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
INSTRUCTIONS : Use the Task tool with subagent_type 'reviewer' to conduct an adversarial code review of the HEAD commit.
|
2
|
-
|
3
|
-
The reviewer agent will:
|
4
|
-
1. Get HEAD commit details and changes
|
5
|
-
2. Analyze changed files to identify technical domains
|
6
|
-
3. Use the sub-agent-coordinator to select relevant expert agents
|
7
|
-
4. Present changes to experts in parallel for review
|
8
|
-
5. Synthesize feedback organized by severity
|
9
|
-
6. Store review output for the apply-feedback command
|
10
|
-
7. Suggest running '/air-apply-feedback' to apply recommended changes
|
11
|
-
|
12
|
-
IMPORTANT: The review agent will automatically review the HEAD commit. No arguments needed.
|
data/hooks/hooks.json
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"PostToolUse": [
|
3
|
-
{
|
4
|
-
"hooks": [
|
5
|
-
{
|
6
|
-
"type": "command",
|
7
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_tool_use.sh"
|
8
|
-
}
|
9
|
-
]
|
10
|
-
}
|
11
|
-
],
|
12
|
-
"PreToolUse": [
|
13
|
-
{
|
14
|
-
"hooks": [
|
15
|
-
{
|
16
|
-
"type": "command",
|
17
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_tool_use.sh"
|
18
|
-
}
|
19
|
-
]
|
20
|
-
},
|
21
|
-
{
|
22
|
-
"hooks": [
|
23
|
-
{
|
24
|
-
"type": "command",
|
25
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/rubocop_pre_commit.sh"
|
26
|
-
}
|
27
|
-
],
|
28
|
-
"matcher": "Bash"
|
29
|
-
}
|
30
|
-
]
|
31
|
-
}
|
@@ -1,276 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "tty-prompt"
|
5
|
-
require_relative "../../generators/hooks_generator"
|
6
|
-
require_relative "init"
|
7
|
-
|
8
|
-
module Aircana
|
9
|
-
module CLI
|
10
|
-
module Hooks
|
11
|
-
class << self
|
12
|
-
def list
|
13
|
-
available_hooks = Aircana::Generators::HooksGenerator.all_available_hooks
|
14
|
-
installed_hooks_list = installed_hooks
|
15
|
-
|
16
|
-
if available_hooks.empty?
|
17
|
-
Aircana.human_logger.info "No hooks available."
|
18
|
-
return
|
19
|
-
end
|
20
|
-
|
21
|
-
Aircana.human_logger.info "Available Hooks:"
|
22
|
-
available_hooks.each do |hook_name|
|
23
|
-
status = installed_hooks_list.include?(hook_name) ? "[INSTALLED]" : "[AVAILABLE]"
|
24
|
-
description = hook_description(hook_name)
|
25
|
-
is_default = Aircana::Generators::HooksGenerator.available_default_hooks.include?(hook_name)
|
26
|
-
default_marker = is_default ? " (default)" : ""
|
27
|
-
Aircana.human_logger.info " #{status} #{hook_name} - #{description}#{default_marker}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def enable(hook_name)
|
32
|
-
unless Aircana::Generators::HooksGenerator.all_available_hooks.include?(hook_name)
|
33
|
-
Aircana.human_logger.error "Hook '#{hook_name}' is not available."
|
34
|
-
available_hooks_list = Aircana::Generators::HooksGenerator.all_available_hooks.join(", ")
|
35
|
-
Aircana.human_logger.info "Available hooks: #{available_hooks_list}"
|
36
|
-
return
|
37
|
-
end
|
38
|
-
|
39
|
-
# Generate the hook if it doesn't exist
|
40
|
-
Aircana::Generators::HooksGenerator.create_default_hook(hook_name)
|
41
|
-
|
42
|
-
# Install hooks to Claude settings
|
43
|
-
Init.run
|
44
|
-
|
45
|
-
Aircana.human_logger.success "Hook '#{hook_name}' has been enabled."
|
46
|
-
end
|
47
|
-
|
48
|
-
def disable(hook_name)
|
49
|
-
hook_file = File.join(Aircana.configuration.hooks_dir, "#{hook_name}.sh")
|
50
|
-
|
51
|
-
unless File.exist?(hook_file)
|
52
|
-
Aircana.human_logger.warn "Hook '#{hook_name}' is not currently enabled."
|
53
|
-
return
|
54
|
-
end
|
55
|
-
|
56
|
-
File.delete(hook_file)
|
57
|
-
|
58
|
-
# Reinstall remaining hooks to update Claude settings
|
59
|
-
Init.run
|
60
|
-
|
61
|
-
Aircana.human_logger.success "Hook '#{hook_name}' has been disabled."
|
62
|
-
end
|
63
|
-
|
64
|
-
def create
|
65
|
-
prompt = TTY::Prompt.new
|
66
|
-
|
67
|
-
hook_name = prompt.ask("Hook name (lowercase, no spaces):")
|
68
|
-
hook_name = hook_name.strip.downcase.gsub(" ", "_")
|
69
|
-
|
70
|
-
hook_event = prompt.select("Select hook event:", %w[
|
71
|
-
pre_tool_use
|
72
|
-
post_tool_use
|
73
|
-
user_prompt_submit
|
74
|
-
session_start
|
75
|
-
])
|
76
|
-
|
77
|
-
description = prompt.ask("Brief description of what this hook does:")
|
78
|
-
|
79
|
-
# Ensure custom hook names include the event type for proper mapping
|
80
|
-
# unless the name already contains it
|
81
|
-
hook_name = "#{hook_name}_#{hook_event}" unless hook_name.include?(hook_event.gsub("_", ""))
|
82
|
-
|
83
|
-
create_custom_hook(hook_name, hook_event, description)
|
84
|
-
end
|
85
|
-
|
86
|
-
def status
|
87
|
-
settings_file = File.join(Aircana.configuration.claude_code_project_config_path, "settings.local.json")
|
88
|
-
|
89
|
-
unless File.exist?(settings_file)
|
90
|
-
Aircana.human_logger.info "No Claude settings file found at #{settings_file}"
|
91
|
-
return
|
92
|
-
end
|
93
|
-
|
94
|
-
begin
|
95
|
-
settings = JSON.parse(File.read(settings_file))
|
96
|
-
hooks_config = settings["hooks"]
|
97
|
-
|
98
|
-
if hooks_config.nil? || hooks_config.empty?
|
99
|
-
Aircana.human_logger.info "No hooks configured in Claude settings."
|
100
|
-
else
|
101
|
-
Aircana.human_logger.info "Configured hooks in Claude settings:"
|
102
|
-
hooks_config.each do |event, configs|
|
103
|
-
configs = [configs] unless configs.is_a?(Array)
|
104
|
-
configs.each do |config|
|
105
|
-
next unless config["hooks"] && config["hooks"][0] && config["hooks"][0]["command"]
|
106
|
-
|
107
|
-
command_path = config["hooks"][0]["command"]
|
108
|
-
script_name = File.basename(command_path, ".sh")
|
109
|
-
matcher_info = config["matcher"] ? " (matcher: #{config["matcher"]})" : ""
|
110
|
-
Aircana.human_logger.info " #{event}: #{script_name}#{matcher_info}"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
rescue JSON::ParserError => e
|
115
|
-
Aircana.human_logger.error "Invalid JSON in settings file: #{e.message}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
def installed_hooks
|
122
|
-
return [] unless Dir.exist?(Aircana.configuration.hooks_dir)
|
123
|
-
|
124
|
-
Dir.glob("#{Aircana.configuration.hooks_dir}/*.sh").map do |file|
|
125
|
-
File.basename(file, ".sh")
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def hook_description(hook_name)
|
130
|
-
descriptions = {
|
131
|
-
"pre_tool_use" => "General pre-tool validation hook",
|
132
|
-
"post_tool_use" => "General post-tool processing hook",
|
133
|
-
"user_prompt_submit" => "Add context to user prompts",
|
134
|
-
"session_start" => "Initialize session with project context",
|
135
|
-
"rubocop_pre_commit" => "Run RuboCop before git commits",
|
136
|
-
"rspec_test" => "Run RSpec tests when Ruby files are modified",
|
137
|
-
"bundle_install" => "Run bundle install when Gemfile changes"
|
138
|
-
}
|
139
|
-
descriptions[hook_name] || "Custom hook"
|
140
|
-
end
|
141
|
-
|
142
|
-
def create_custom_hook(hook_name, hook_event, description)
|
143
|
-
template_content = generate_custom_hook_template(hook_event, description)
|
144
|
-
|
145
|
-
hook_file = File.join(Aircana.configuration.hooks_dir, "#{hook_name}.sh")
|
146
|
-
Aircana.create_dir_if_needed(File.dirname(hook_file))
|
147
|
-
|
148
|
-
File.write(hook_file, template_content)
|
149
|
-
File.chmod(0o755, hook_file)
|
150
|
-
|
151
|
-
Aircana.human_logger.success "Custom hook created at #{hook_file}"
|
152
|
-
Aircana.human_logger.info "You may need to customize the hook script for your specific needs."
|
153
|
-
|
154
|
-
# Install hooks to Claude settings
|
155
|
-
Init.run
|
156
|
-
Aircana.human_logger.success "Hook installed to Claude settings"
|
157
|
-
|
158
|
-
# Optionally offer to open in editor
|
159
|
-
prompt = TTY::Prompt.new
|
160
|
-
return unless prompt.yes?("Would you like to edit the hook file now?")
|
161
|
-
|
162
|
-
open_file_in_editor(hook_file)
|
163
|
-
end
|
164
|
-
|
165
|
-
def generate_custom_hook_template(hook_event, description)
|
166
|
-
case hook_event
|
167
|
-
when "pre_tool_use"
|
168
|
-
generate_pre_tool_use_template(description)
|
169
|
-
when "post_tool_use"
|
170
|
-
generate_post_tool_use_template(description)
|
171
|
-
when "user_prompt_submit"
|
172
|
-
generate_user_prompt_submit_template(description)
|
173
|
-
when "session_start"
|
174
|
-
generate_session_start_template(description)
|
175
|
-
else
|
176
|
-
generate_basic_template(hook_event, description)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def generate_pre_tool_use_template(description)
|
181
|
-
<<~SCRIPT
|
182
|
-
#!/bin/bash
|
183
|
-
# Custom pre-tool-use hook: #{description}
|
184
|
-
|
185
|
-
TOOL_NAME="$1"
|
186
|
-
TOOL_PARAMS="$2"
|
187
|
-
|
188
|
-
# Add your custom validation logic here
|
189
|
-
# Return exit code 0 to allow, exit code 1 to deny
|
190
|
-
|
191
|
-
echo "Pre-tool validation: $TOOL_NAME"
|
192
|
-
|
193
|
-
# Allow by default
|
194
|
-
exit 0
|
195
|
-
SCRIPT
|
196
|
-
end
|
197
|
-
|
198
|
-
def generate_post_tool_use_template(description)
|
199
|
-
<<~SCRIPT
|
200
|
-
#!/bin/bash
|
201
|
-
# Custom post-tool-use hook: #{description}
|
202
|
-
|
203
|
-
TOOL_NAME="$1"
|
204
|
-
TOOL_PARAMS="$2"
|
205
|
-
TOOL_RESULT="$3"
|
206
|
-
EXIT_CODE="$4"
|
207
|
-
|
208
|
-
# Add your custom post-processing logic here
|
209
|
-
|
210
|
-
echo "Post-tool processing: $TOOL_NAME (exit code: $EXIT_CODE)"
|
211
|
-
|
212
|
-
# Always allow result to proceed
|
213
|
-
exit 0
|
214
|
-
SCRIPT
|
215
|
-
end
|
216
|
-
|
217
|
-
def generate_user_prompt_submit_template(description)
|
218
|
-
<<~SCRIPT
|
219
|
-
#!/bin/bash
|
220
|
-
# Custom user prompt submit hook: #{description}
|
221
|
-
|
222
|
-
USER_PROMPT="$1"
|
223
|
-
|
224
|
-
# Add custom context or modify the prompt here
|
225
|
-
# Output JSON for advanced control or simple exit code
|
226
|
-
|
227
|
-
echo "Processing user prompt"
|
228
|
-
|
229
|
-
# Simple allow - no modifications
|
230
|
-
exit 0
|
231
|
-
SCRIPT
|
232
|
-
end
|
233
|
-
|
234
|
-
def generate_session_start_template(description)
|
235
|
-
<<~SCRIPT
|
236
|
-
#!/bin/bash
|
237
|
-
# Custom session start hook: #{description}
|
238
|
-
|
239
|
-
# Add session initialization logic here
|
240
|
-
|
241
|
-
echo "Session started in $(pwd)"
|
242
|
-
|
243
|
-
# Simple allow
|
244
|
-
exit 0
|
245
|
-
SCRIPT
|
246
|
-
end
|
247
|
-
|
248
|
-
def generate_basic_template(hook_event, description)
|
249
|
-
<<~SCRIPT
|
250
|
-
#!/bin/bash
|
251
|
-
# Custom #{hook_event} hook: #{description}
|
252
|
-
|
253
|
-
# Add your custom hook logic here
|
254
|
-
|
255
|
-
exit 0
|
256
|
-
SCRIPT
|
257
|
-
end
|
258
|
-
|
259
|
-
def open_file_in_editor(file_path)
|
260
|
-
editor = ENV["EDITOR"] || find_available_editor
|
261
|
-
|
262
|
-
if editor
|
263
|
-
Aircana.human_logger.info "Opening #{file_path} in #{editor}..."
|
264
|
-
system("#{editor} '#{file_path}'")
|
265
|
-
else
|
266
|
-
Aircana.human_logger.warn "No editor found. Please edit #{file_path} manually."
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
def find_available_editor
|
271
|
-
%w[code subl atom nano vim vi].find { |cmd| system("which #{cmd} > /dev/null 2>&1") }
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|