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.
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