aircana 0.1.0 → 1.0.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.
@@ -23,6 +23,16 @@ module Aircana
23
23
  "Arch" => "pacman -S git",
24
24
  "Other" => "https://git-scm.com/downloads"
25
25
  }
26
+ },
27
+ "jq" => {
28
+ purpose: "JSON parsing for multi-root configuration",
29
+ install: {
30
+ "macOS" => "brew install jq",
31
+ "Ubuntu/Debian" => "apt install jq",
32
+ "Fedora/CentOS" => "dnf install jq",
33
+ "Arch" => "pacman -S jq",
34
+ "Other" => "https://jqlang.github.io/jq/download/"
35
+ }
26
36
  }
27
37
  }.freeze
28
38
 
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ # Bundle install hook generated by Aircana
3
+ # This hook runs bundle install when Gemfile changes are detected
4
+
5
+ # Get the tool details
6
+ TOOL_NAME="$1"
7
+ TOOL_PARAMS="$2"
8
+
9
+ # Only act on file modification tools
10
+ case "$TOOL_NAME" in
11
+ "Edit"|"Write"|"MultiEdit")
12
+ ;;
13
+ *)
14
+ exit 0
15
+ ;;
16
+ esac
17
+
18
+ # Check if Gemfile was modified
19
+ if ! echo "$TOOL_PARAMS" | grep -q "Gemfile"; then
20
+ exit 0
21
+ fi
22
+
23
+ # Check if this is a Ruby project
24
+ if [ ! -f "Gemfile" ]; then
25
+ exit 0
26
+ fi
27
+
28
+ echo "Gemfile modified, running bundle install..."
29
+
30
+ # Run bundle install
31
+ if command -v bundle >/dev/null 2>&1; then
32
+ bundle install
33
+ BUNDLE_EXIT_CODE=$?
34
+
35
+ if [ $BUNDLE_EXIT_CODE -eq 0 ]; then
36
+ cat << EOF
37
+ {
38
+ "type": "advanced",
39
+ "decision": "allow",
40
+ "message": "Bundle install completed successfully after Gemfile modification."
41
+ }
42
+ EOF
43
+ else
44
+ cat << EOF
45
+ {
46
+ "type": "advanced",
47
+ "decision": "allow",
48
+ "message": "Bundle install failed. You may need to run 'bundle install' manually.\n\nError code: $BUNDLE_EXIT_CODE"
49
+ }
50
+ EOF
51
+ fi
52
+ else
53
+ cat << EOF
54
+ {
55
+ "type": "advanced",
56
+ "decision": "allow",
57
+ "message": "Bundler not found. Please install bundler and run 'bundle install' manually."
58
+ }
59
+ EOF
60
+ fi
61
+
62
+ # Always allow the modification to proceed
63
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+ # Post-tool-use hook generated by Aircana
3
+ # This hook runs after a tool has been executed
4
+
5
+ # Get tool execution details
6
+ TOOL_NAME="$1"
7
+ TOOL_PARAMS="$2"
8
+ TOOL_RESULT="$3"
9
+ EXIT_CODE="$4"
10
+
11
+ # Log the tool execution
12
+ echo "$(date): Tool '$TOOL_NAME' completed with exit code: $EXIT_CODE" >> ~/.aircana/hooks.log
13
+
14
+ # Initialize context for response
15
+ ADDITIONAL_CONTEXT=""
16
+
17
+ # Handle specific tools
18
+ case "$TOOL_NAME" in
19
+ "Edit"|"Write")
20
+ # Auto-update relevant files context if code files were modified
21
+ if echo "$TOOL_PARAMS" | grep -E "\.(rb|js|py|ts)$" && [ "$EXIT_CODE" -eq 0 ]; then
22
+ ADDITIONAL_CONTEXT="Code file modified - consider updating relevant files context."
23
+ fi
24
+ ;;
25
+ "Bash")
26
+ # Handle bash command results
27
+ if [ "$EXIT_CODE" -ne 0 ]; then
28
+ echo "Bash command failed: $TOOL_PARAMS" >> ~/.aircana/hooks.log
29
+ ADDITIONAL_CONTEXT="Previous bash command failed with exit code $EXIT_CODE."
30
+ fi
31
+ ;;
32
+ esac
33
+
34
+ # Output appropriate JSON response
35
+ if [ -n "$ADDITIONAL_CONTEXT" ]; then
36
+ # Escape context for JSON
37
+ ESCAPED_CONTEXT=$(echo -n "$ADDITIONAL_CONTEXT" | sed 's/"/\\"/g')
38
+ cat << EOF
39
+ {
40
+ "hookSpecificOutput": {
41
+ "hookEventName": "PostToolUse",
42
+ "additionalContext": "$ESCAPED_CONTEXT"
43
+ }
44
+ }
45
+ EOF
46
+ else
47
+ # No additional context needed
48
+ echo "{}"
49
+ fi
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Pre-tool-use hook generated by Aircana
3
+ # This hook runs before any tool is executed
4
+
5
+ # Get the tool name and parameters
6
+ TOOL_NAME="$1"
7
+ TOOL_PARAMS="$2"
8
+
9
+ # Log the tool usage (optional)
10
+ echo "$(date): About to use tool: $TOOL_NAME" >> ~/.aircana/hooks.log
11
+
12
+ # Basic validation example
13
+ case "$TOOL_NAME" in
14
+ "Bash")
15
+ # Add validation for bash commands
16
+ if echo "$TOOL_PARAMS" | grep -q "rm -rf /"; then
17
+ cat << EOF
18
+ {
19
+ "hookSpecificOutput": {
20
+ "hookEventName": "PreToolUse",
21
+ "permissionDecision": "deny",
22
+ "permissionDecisionReason": "Dangerous command detected: rm -rf /"
23
+ }
24
+ }
25
+ EOF
26
+ exit 0
27
+ fi
28
+ ;;
29
+ "Edit"|"Write")
30
+ # Validate file operations
31
+ # Add custom validation logic here
32
+ ;;
33
+ esac
34
+
35
+ # Allow the tool to proceed
36
+ cat << EOF
37
+ {
38
+ "hookSpecificOutput": {
39
+ "hookEventName": "PreToolUse",
40
+ "permissionDecision": "allow"
41
+ }
42
+ }
43
+ EOF
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # RSpec test hook generated by Aircana
3
+ # This hook runs relevant tests when Ruby files are modified
4
+
5
+ # Get the tool details
6
+ TOOL_NAME="$1"
7
+ TOOL_PARAMS="$2"
8
+
9
+ # Only act on file modification tools
10
+ case "$TOOL_NAME" in
11
+ "Edit"|"Write"|"MultiEdit")
12
+ ;;
13
+ *)
14
+ exit 0
15
+ ;;
16
+ esac
17
+
18
+ # Check if this is a Ruby project with RSpec
19
+ if [ ! -f "Gemfile" ] || ! grep -q "rspec" Gemfile 2>/dev/null; then
20
+ exit 0
21
+ fi
22
+
23
+ # Extract file path from tool parameters (basic extraction)
24
+ MODIFIED_FILE=$(echo "$TOOL_PARAMS" | grep -o '"[^"]*\.rb"' | head -1 | tr -d '"')
25
+
26
+ if [ -z "$MODIFIED_FILE" ]; then
27
+ exit 0
28
+ fi
29
+
30
+ # Determine which specs to run based on the modified file
31
+ SPEC_FILE=""
32
+
33
+ # Check for corresponding spec file
34
+ if echo "$MODIFIED_FILE" | grep -q "^lib/"; then
35
+ # For lib files, look for spec file
36
+ SPEC_FILE="spec/$(echo "$MODIFIED_FILE" | sed 's|^lib/||' | sed 's|\.rb$|_spec.rb|')"
37
+ elif echo "$MODIFIED_FILE" | grep -q "^app/"; then
38
+ # For Rails app files
39
+ SPEC_FILE="spec/$(echo "$MODIFIED_FILE" | sed 's|^app/||' | sed 's|\.rb$|_spec.rb|')"
40
+ elif echo "$MODIFIED_FILE" | grep -q "_spec\.rb$"; then
41
+ # If the modified file is already a spec file
42
+ SPEC_FILE="$MODIFIED_FILE"
43
+ fi
44
+
45
+ # Check if spec file exists
46
+ if [ -n "$SPEC_FILE" ] && [ -f "$SPEC_FILE" ]; then
47
+ echo "Running tests for modified file: $MODIFIED_FILE"
48
+
49
+ # Run the specific test
50
+ if command -v bundle >/dev/null 2>&1; then
51
+ bundle exec rspec "$SPEC_FILE" --format progress
52
+ else
53
+ rspec "$SPEC_FILE" --format progress
54
+ fi
55
+
56
+ RSPEC_EXIT_CODE=$?
57
+
58
+ if [ $RSPEC_EXIT_CODE -ne 0 ]; then
59
+ cat << EOF
60
+ {
61
+ "type": "advanced",
62
+ "decision": "allow",
63
+ "message": "Tests failed for $SPEC_FILE. Consider fixing the failing tests.\n\nYou can run: bundle exec rspec $SPEC_FILE"
64
+ }
65
+ EOF
66
+ else
67
+ echo "Tests passed for $SPEC_FILE"
68
+ fi
69
+ fi
70
+
71
+ # Always allow the modification to proceed
72
+ exit 0
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # RuboCop pre-commit hook generated by Aircana
3
+ # This hook runs RuboCop before allowing commits
4
+
5
+ # Get the tool name and parameters
6
+ TOOL_NAME="$1"
7
+ TOOL_PARAMS="$2"
8
+
9
+ # Only act on Bash commands that look like git commits
10
+ if [ "$TOOL_NAME" != "Bash" ]; then
11
+ exit 0
12
+ fi
13
+
14
+ # Check if this is a git commit command
15
+ if ! echo "$TOOL_PARAMS" | grep -q "git commit"; then
16
+ exit 0
17
+ fi
18
+
19
+ # Check if we're in a Ruby project
20
+ if [ ! -f "Gemfile" ] && [ ! -f ".rubocop.yml" ]; then
21
+ exit 0
22
+ fi
23
+
24
+ # Get list of staged Ruby files
25
+ RUBY_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(rb)$' | tr '\n' ' ')
26
+
27
+ if [ -z "$RUBY_FILES" ]; then
28
+ # No Ruby files to check
29
+ exit 0
30
+ fi
31
+
32
+ echo "Running RuboCop on staged Ruby files..."
33
+
34
+ # Run RuboCop on staged files
35
+ if command -v bundle >/dev/null 2>&1 && [ -f "Gemfile" ]; then
36
+ bundle exec rubocop $RUBY_FILES
37
+ else
38
+ rubocop $RUBY_FILES
39
+ fi
40
+
41
+ RUBOCOP_EXIT_CODE=$?
42
+
43
+ if [ $RUBOCOP_EXIT_CODE -ne 0 ]; then
44
+ cat << EOF
45
+ {
46
+ "type": "advanced",
47
+ "decision": "deny",
48
+ "message": "RuboCop found style violations. Please fix them before committing.\n\nYou can run: bundle exec rubocop $RUBY_FILES --auto-correct\n\nOr fix them manually and try again."
49
+ }
50
+ EOF
51
+ exit 1
52
+ fi
53
+
54
+ # RuboCop passed, allow the commit
55
+ exit 0
@@ -0,0 +1,127 @@
1
+ #!/bin/bash
2
+ # Multi-root project support hook for Aircana
3
+ # This hook runs when a new Claude Code session starts
4
+
5
+ PROJECT_JSON=".aircana/project.json"
6
+ CLAUDE_AGENTS_DIR=".claude/agents"
7
+ AIRCANA_AGENTS_DIR=".aircana/agents"
8
+
9
+ # Create log directory if it doesn't exist
10
+ mkdir -p ~/.aircana
11
+
12
+ # Log session start
13
+ echo "$(date): New Claude Code session started in $(pwd)" >> ~/.aircana/hooks.log
14
+
15
+ # Check if jq is available
16
+ if ! command -v jq &> /dev/null; then
17
+ echo "$(date): Warning - jq not found. Multi-root support disabled." >> ~/.aircana/hooks.log
18
+ echo "{}"
19
+ exit 0
20
+ fi
21
+
22
+ # Check if project.json exists
23
+ if [ ! -f "$PROJECT_JSON" ]; then
24
+ echo "$(date): No project.json found, skipping multi-root setup" >> ~/.aircana/hooks.log
25
+ echo "{}"
26
+ exit 0
27
+ fi
28
+
29
+ echo "$(date): Processing multi-root configuration from $PROJECT_JSON" >> ~/.aircana/hooks.log
30
+
31
+ # Ensure directories exist
32
+ mkdir -p "$CLAUDE_AGENTS_DIR" 2>/dev/null
33
+ mkdir -p "$AIRCANA_AGENTS_DIR" 2>/dev/null
34
+
35
+ # Clean up existing symlinks (only remove symlinks, not real files)
36
+ find "$CLAUDE_AGENTS_DIR" -type l -delete 2>/dev/null
37
+ find "$AIRCANA_AGENTS_DIR" -type l -delete 2>/dev/null
38
+
39
+ # Parse folders from project.json
40
+ FOLDERS=$(jq -r '.folders[]?.path // empty' "$PROJECT_JSON" 2>/dev/null)
41
+
42
+ if [ -z "$FOLDERS" ]; then
43
+ echo "$(date): No folders configured in project.json" >> ~/.aircana/hooks.log
44
+ echo "{}"
45
+ exit 0
46
+ fi
47
+
48
+ # Track what we've linked for reporting
49
+ LINKED_AGENTS=0
50
+ LINKED_KNOWLEDGE=0
51
+
52
+ # Create symlinks for each configured folder
53
+ for folder in $FOLDERS; do
54
+ # Validate folder exists
55
+ if [ ! -d "$folder" ]; then
56
+ echo "$(date): Warning - folder '$folder' not found, skipping" >> ~/.aircana/hooks.log
57
+ continue
58
+ fi
59
+
60
+ echo "$(date): Processing folder: $folder" >> ~/.aircana/hooks.log
61
+
62
+ # Get folder name for prefix (replace slashes with underscores for nested paths)
63
+ PREFIX=$(echo "$folder" | tr '/' '_')
64
+
65
+ # Link agents from sub-folder .claude/agents
66
+ if [ -d "$folder/.claude/agents" ]; then
67
+ for agent_file in "$folder/.claude/agents"/*.md; do
68
+ if [ ! -f "$agent_file" ]; then
69
+ continue
70
+ fi
71
+
72
+ AGENT_NAME=$(basename "$agent_file" .md)
73
+ LINK_NAME="${PREFIX}_${AGENT_NAME}.md"
74
+ TARGET_PATH="$CLAUDE_AGENTS_DIR/$LINK_NAME"
75
+
76
+ # Create relative path from .claude/agents to the agent file
77
+ RELATIVE_PATH=$(realpath --relative-to="$CLAUDE_AGENTS_DIR" "$agent_file" 2>/dev/null)
78
+
79
+ if [ -n "$RELATIVE_PATH" ]; then
80
+ ln -sf "$RELATIVE_PATH" "$TARGET_PATH"
81
+ echo "$(date): Linked agent: $LINK_NAME -> $RELATIVE_PATH" >> ~/.aircana/hooks.log
82
+ ((LINKED_AGENTS++))
83
+ fi
84
+ done
85
+ fi
86
+
87
+ # Link knowledge from sub-folder .aircana/agents
88
+ if [ -d "$folder/.aircana/agents" ]; then
89
+ for agent_dir in "$folder/.aircana/agents"/*; do
90
+ if [ ! -d "$agent_dir" ]; then
91
+ continue
92
+ fi
93
+
94
+ AGENT_NAME=$(basename "$agent_dir")
95
+ LINK_NAME="${PREFIX}_${AGENT_NAME}"
96
+ TARGET_PATH="$AIRCANA_AGENTS_DIR/$LINK_NAME"
97
+
98
+ # Create relative path from .aircana/agents to the knowledge directory
99
+ RELATIVE_PATH=$(realpath --relative-to="$AIRCANA_AGENTS_DIR" "$agent_dir" 2>/dev/null)
100
+
101
+ if [ -n "$RELATIVE_PATH" ]; then
102
+ ln -sf "$RELATIVE_PATH" "$TARGET_PATH"
103
+ echo "$(date): Linked knowledge: $LINK_NAME -> $RELATIVE_PATH" >> ~/.aircana/hooks.log
104
+ ((LINKED_KNOWLEDGE++))
105
+ fi
106
+ done
107
+ fi
108
+ done
109
+
110
+ # Report results
111
+ echo "$(date): Multi-root setup complete - linked $LINKED_AGENTS agents and $LINKED_KNOWLEDGE knowledge bases" >> ~/.aircana/hooks.log
112
+
113
+ # Return success with optional context
114
+ if [ $LINKED_AGENTS -gt 0 ] || [ $LINKED_KNOWLEDGE -gt 0 ]; then
115
+ CONTEXT="Multi-root: Linked $LINKED_AGENTS agents and $LINKED_KNOWLEDGE knowledge bases from configured folders."
116
+ ESCAPED_CONTEXT=$(echo -n "$CONTEXT" | sed 's/"/\\"/g')
117
+ cat << EOF
118
+ {
119
+ "hookSpecificOutput": {
120
+ "hookEventName": "SessionStart",
121
+ "additionalContext": "$ESCAPED_CONTEXT"
122
+ }
123
+ }
124
+ EOF
125
+ else
126
+ echo "{}"
127
+ fi
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # User prompt submit hook generated by Aircana
3
+ # This hook runs when the user submits a prompt
4
+
5
+ # Get the user prompt
6
+ USER_PROMPT="$1"
7
+
8
+ # Add context information based on project state
9
+ CONTEXT_ADDITIONS=""
10
+
11
+ # Check if this is a Ruby project and add relevant info
12
+ if [ -f "Gemfile" ]; then
13
+ CONTEXT_ADDITIONS="${CONTEXT_ADDITIONS}\n\nProject Context: This is a Ruby project with Gemfile present."
14
+
15
+ # Check for common Ruby frameworks
16
+ if grep -q "rails" Gemfile 2>/dev/null; then
17
+ CONTEXT_ADDITIONS="${CONTEXT_ADDITIONS} Rails framework detected."
18
+ fi
19
+
20
+ if grep -q "rspec" Gemfile 2>/dev/null; then
21
+ CONTEXT_ADDITIONS="${CONTEXT_ADDITIONS} RSpec testing framework in use."
22
+ fi
23
+ fi
24
+
25
+ # Check for relevant files context
26
+ if [ -d ".aircana/relevant_files" ] && [ "$(ls -A .aircana/relevant_files 2>/dev/null)" ]; then
27
+ RELEVANT_COUNT=$(ls .aircana/relevant_files | wc -l)
28
+ CONTEXT_ADDITIONS="${CONTEXT_ADDITIONS}\n\nRelevant Files: $RELEVANT_COUNT files currently in context."
29
+ fi
30
+
31
+ # Output JSON response with additional context
32
+ if [ -n "$CONTEXT_ADDITIONS" ]; then
33
+ # Escape context for JSON
34
+ ESCAPED_CONTEXT=$(echo -n "$CONTEXT_ADDITIONS" | sed 's/"/\\"/g' | tr '\n' ' ' | sed 's/ */ /g')
35
+ cat << EOF
36
+ {
37
+ "hookSpecificOutput": {
38
+ "hookEventName": "UserPromptSubmit",
39
+ "additionalContext": "$ESCAPED_CONTEXT"
40
+ }
41
+ }
42
+ EOF
43
+ else
44
+ # No additional context needed
45
+ echo "{}"
46
+ fi
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aircana
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
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: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Weston Dransfield
@@ -93,9 +93,8 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0.9'
96
- description: A Ruby CLI utility for context management and Claude Codeintegration.
97
- Aircana helps manage relevant files for developmentsessions, create specialized
98
- Claude Code agents, and optionally syncknowledge from Confluence.
96
+ description: Aircana provides context management and workflow utilities for software
97
+ engineering with AI agents, including file organization and template generation.
99
98
  email:
100
99
  - weston@dransfield.dev
101
100
  executables:
@@ -127,10 +126,14 @@ files:
127
126
  - lib/aircana/cli/commands/doctor_checks.rb
128
127
  - lib/aircana/cli/commands/doctor_helpers.rb
129
128
  - lib/aircana/cli/commands/dump_context.rb
129
+ - lib/aircana/cli/commands/files.rb
130
130
  - lib/aircana/cli/commands/generate.rb
131
+ - lib/aircana/cli/commands/hooks.rb
131
132
  - lib/aircana/cli/commands/install.rb
132
133
  - lib/aircana/cli/commands/plan.rb
134
+ - lib/aircana/cli/commands/project.rb
133
135
  - lib/aircana/cli/commands/work.rb
136
+ - lib/aircana/cli/help_formatter.rb
134
137
  - lib/aircana/cli/shell_command.rb
135
138
  - lib/aircana/cli/subcommand.rb
136
139
  - lib/aircana/configuration.rb
@@ -146,17 +149,27 @@ files:
146
149
  - lib/aircana/generators/agents_generator.rb
147
150
  - lib/aircana/generators/base_generator.rb
148
151
  - lib/aircana/generators/helpers.rb
152
+ - lib/aircana/generators/hooks_generator.rb
153
+ - lib/aircana/generators/project_config_generator.rb
149
154
  - lib/aircana/generators/relevant_files_command_generator.rb
150
155
  - lib/aircana/generators/relevant_files_verbose_results_generator.rb
151
156
  - lib/aircana/human_logger.rb
152
157
  - lib/aircana/initializers.rb
153
158
  - lib/aircana/llm/claude_client.rb
154
159
  - lib/aircana/progress_tracker.rb
160
+ - lib/aircana/symlink_manager.rb
155
161
  - lib/aircana/system_checker.rb
156
162
  - lib/aircana/templates/agents/base_agent.erb
157
163
  - lib/aircana/templates/agents/defaults/planner.erb
158
164
  - lib/aircana/templates/agents/defaults/worker.erb
159
165
  - lib/aircana/templates/commands/add_relevant_files.erb
166
+ - lib/aircana/templates/hooks/bundle_install.erb
167
+ - lib/aircana/templates/hooks/post_tool_use.erb
168
+ - lib/aircana/templates/hooks/pre_tool_use.erb
169
+ - lib/aircana/templates/hooks/rspec_test.erb
170
+ - lib/aircana/templates/hooks/rubocop_pre_commit.erb
171
+ - lib/aircana/templates/hooks/session_start.erb
172
+ - lib/aircana/templates/hooks/user_prompt_submit.erb
160
173
  - lib/aircana/templates/relevant_files_verbose_results.erb
161
174
  - lib/aircana/version.rb
162
175
  - sig/aircana.rbs
@@ -185,5 +198,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
198
  requirements: []
186
199
  rubygems_version: 3.6.9
187
200
  specification_version: 4
188
- summary: Workflow and context utilities for engineering with agents
201
+ summary: Humble workflow and context utilities for engineering with agents
189
202
  test_files: []