aircana 1.5.0 → 2.0.0.rc1

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/README.md CHANGED
@@ -1,11 +1,27 @@
1
1
  # Aircana
2
2
 
3
- A Ruby CLI utility for context management and Claude Code integration. Aircana helps manage relevant files for development sessions, create specialized Claude Code agents, and optionally sync knowledge from Confluence.
4
-
5
3
  [![Ruby](https://github.com/westonkd/aircana/actions/workflows/main.yml/badge.svg)](https://github.com/westonkd/aircana/actions/workflows/main.yml)
6
4
  [![Gem Version](https://badge.fury.io/rb/aircana.svg)](https://badge.fury.io/rb/aircana)
7
5
 
8
- ## Installation
6
+ ## Intro
7
+
8
+ Aircana aims to be a "batteries-included" CLI for common needs of Instructure Engineering teams using agents in development. It provides:
9
+
10
+ Consistent artifact generation and updating of agent, slash command, and hook ERB templates.
11
+
12
+ Subagent generation for improved context window management.
13
+
14
+ Agent-accessible knowledge bases sourced from Confluence or public websites, backed by manifest files.
15
+
16
+ Development workflow with plan, record, and execute phases (record and execute phases are in progress).
17
+
18
+ SQS integration for features like Slack notifications and messages.
19
+
20
+ While Aircana includes features beneficial in many agentic contexts (like knowledge base syncing), its primary tools are built on "human-in-the-loop" principles.
21
+
22
+ ## How can I try it?
23
+
24
+ ### Installation
9
25
 
10
26
  Install the gem:
11
27
 
@@ -13,106 +29,108 @@ Install the gem:
13
29
  gem install aircana
14
30
  ```
15
31
 
16
- Or add to your Gemfile:
32
+ Verify installation and dependency setup:
17
33
 
18
- ```ruby
19
- gem 'aircana'
34
+ ```bash
35
+ aircana doctor
20
36
  ```
21
37
 
22
- Then run:
38
+ Install Aircana files into your repo:
23
39
 
24
40
  ```bash
25
- bundle install
41
+ cd ~/my/repo
42
+ aircana install # This is a no-op if the repo already uses .aircana
26
43
  ```
27
44
 
28
- Verify installation and check dependencies:
45
+ ### Take it for a spin
46
+
47
+ If your project previously had Aircana agents set up, run the following to populate each defined agent's knowledge base:
29
48
 
30
49
  ```bash
31
- aircana doctor
50
+ aircana agents refresh-all
32
51
  ```
33
52
 
34
- ## Prerequisites
53
+ To start using your agents with domain-specific knowledge, follow the agent workflow tutorial.
54
+
55
+ ### Things to try
56
+
57
+ - Configure the Confluence integration and create your own domain-specific subagent
58
+
59
+ - Launch Claude Code and view the hooks installed by Aircana (prefixed with air). The /air-ask-expert command is pretty handy once you set up some agents with knowledbases.
60
+
61
+ - Set up the SQS integration to receive Slack messages when Claude Code needs your attention (documentation coming soon).
35
62
 
36
- ### Required Dependencies
37
- - Ruby >= 3.3.0
38
- - `git` (version control operations)
39
- - `fzf` (interactive file selection)
63
+ - Explore other tools by running `aircana --help`
40
64
 
41
- ### Optional Dependencies
42
- - `bat` (enhanced file previews, falls back to `cat`)
43
- - `fd` (faster file searching, falls back to `find`)
65
+ ## Key Concepts
44
66
 
45
- ### For Confluence Integration (Optional)
46
- - Access to a Confluence instance
47
- - Confluence API token
48
- - Appropriate permissions to read pages and labels
67
+ ### Subagents
49
68
 
50
- ## Core Concepts
69
+ Subagents are domain-specific agents to whom the primary Claude Code agent can delegate tasks and questions.
51
70
 
52
- Aircana provides two main independent features:
71
+ Each subagent has its own context window. Effectively using subagents can keep the main context window and per-agent context windows smaller for longer, leading to much more usable results and reducing the need to remind agents of core principles and tasks.
53
72
 
54
- ### 1. Relevant Files Management
55
- Track and manage a curated set of "relevant files" - the current working set of important files for your development session. This context is automatically available to Claude Code sessions.
73
+ Claude Code can also run subagents in parallel. A "swarm" of appropriately designed subagents can expedite planning and execution tasks while considering a broader context.
56
74
 
57
- **This works completely independently from agents** - you can use relevant files without creating any agents.
75
+ Aircana allows easy generation of subagents and binds each to an agent-specific knowledge base with documents from Confluence or websites.
58
76
 
59
- ### 2. Specialized Agents (Optional)
60
- Create Claude Code agents with:
61
- - **Domain Knowledge**: Focused expertise in specific areas
62
- - **Confluence Integration**: Knowledge sync from labeled pages (requires Confluence setup)
63
- - **Customizable Models**: Choose from different Claude models and interface colors
77
+ ### Knowledge Bases
64
78
 
65
- **Agents work independently from relevant files** - you can create agents without managing relevant files.
79
+ Aircana provides each subagent access to a human-curated knowledge base. This access enables Aircana-managed subagents to yield more relevant results, minimizing back-and-forth between the human operator and the agent.
66
80
 
67
- ### Knowledge Sources
68
- - **Relevant Files**: Current working set managed by Aircana (independent feature)
69
- - **Confluence Pages**: Fetched based on agent labels (agent feature, requires setup)
70
- - **Web URLs**: Any web content added to agent knowledge bases (HTML converted to Markdown)
71
- - **Local Context**: Project-specific files and configurations
81
+ After initial agent creation, Aircana supports refreshing agents' knowledge bases with the latest versions of each source.
72
82
 
73
- ## What Aircana Does
83
+ #### Confluence
74
84
 
75
- - **File Context Management**: Track and manage relevant files for Claude Code sessions
76
- - **Agent Configuration**: Create and configure specialized Claude Code agents
77
- - **Confluence Integration**: Sync knowledge from Confluence pages to agents (optional)
78
- - **Claude Code Shortcuts**: Quick-launch Claude Code with pre-configured agents
79
- - **System Health Checks**: Validate dependencies and configuration
85
+ To add a Confluence page to an agent's knowledge base, label the desired page in Confluence, then run `aircana agent refresh <AGENT>`.
80
86
 
81
- ## Quick Start
87
+ Aircana will also pull any Confluence pages labeled with a matching agent name during initial agent creation (`aircana agent create`).
82
88
 
83
- 1. **Install and verify**:
84
- ```bash
85
- gem install aircana
86
- aircana doctor
87
- ```
89
+ See the Confluence setup guide or run `aircana doctor` for instructions on setting up Confluence integration.
88
90
 
89
- 2. **Set up your project**:
90
- ```bash
91
- cd your-project
92
- aircana generate
93
- aircana install # Set up Aircana integration in this project
94
- ```
91
+ #### Websites
95
92
 
96
- 3. **Add files to context**:
97
- ```bash
98
- aircana files add # Interactive selection
99
- ```
93
+ In addition to Confluence sources, Aircana allows adding arbitrary public websites to a knowledge base.
100
94
 
101
- Then in Claude Code, include them whenever you want to reload the files into the current context:
102
- ```
103
- /add-relevant-files
104
- ```
95
+ Websites are also refreshed when `aircana agent refresh <AGENT>` is used.
105
96
 
106
- 4. **Create an agent** (optional, but powerful with Confluence):
107
- ```bash
108
- aircana agents create # Tag Confluence pages with the agent's name before or after creation to pull that knowledge into the agent's knowledge base.
109
- ```
97
+ #### Structure
110
98
 
111
- 5. **View your current context**:
112
- ```bash
113
- aircana files list # See all tracked files
114
- aircana agents list # See all configured agents
115
- ```
99
+ Knowledge bases are stored in the .aircana directory of each project. For example:
100
+
101
+ ```
102
+ .aircana
103
+ ├── agents
104
+ │ ├── canvas-backend-account-expert
105
+ │ │ ├── knowledge
106
+ │ │ │ ├── Accounts.md
107
+ │ │ │ └── Subdomains-&-Request-Routing.md
108
+ │ │ └── manifest.json
109
+ ```
110
+
111
+ Agent files in the .claude/agents directory reference these files.
112
+
113
+ In many cases, adding the actual knowledge base to version control is undesirable because:
114
+
115
+ There may be numerous files in the knowledge base, bloating repository size.
116
+
117
+ Knowledge bases may contain sensitive information that should not be public in an open-source project.
118
+
119
+ Aircana manages a per-agent manifest.json file to address these concerns.
120
+
121
+ ### Consistent Artifacts
122
+
123
+ Aircana maintains a set of ERB templates for generating Claude Code agents, hooks, and slash commands consistently.
124
+
125
+ These templates promote best practices and help new users quickly create effective artifacts without extensive trial and error.
126
+
127
+ ### SQS Integration (Slack Integration at Instructure)
128
+
129
+ Aircana uses the "Notification" Claude Code hook to send messages to SQS.
130
+
131
+ At Instructure this means you can easily configure Claude Code to send you slack messages when it needs your attention via Aircana
132
+
133
+ (Instructions coming soon, send a message if you want help with this)
116
134
 
117
135
  ## Configuration (Optional)
118
136
 
@@ -257,14 +275,6 @@ This refreshes both Confluence pages and web URLs associated with the agent.
257
275
 
258
276
  ## All Commands
259
277
 
260
- ### File Management
261
- ```bash
262
- aircana files add # Interactively select files to add to context
263
- aircana files add-dir [PATH] # Add all files from directory to context
264
- aircana files clear # Clear current file context
265
- aircana files list # Show current relevant files
266
- ```
267
-
268
278
  ### Agent Management
269
279
  ```bash
270
280
  aircana agents create # Create new agent interactively
@@ -2,9 +2,6 @@
2
2
 
3
3
  require "thor"
4
4
 
5
- require_relative "commands/add_files"
6
- require_relative "commands/add_directory"
7
- require_relative "commands/clear_files"
8
5
  require_relative "commands/doctor"
9
6
  require_relative "commands/dump_context"
10
7
  require_relative "commands/generate"
@@ -15,7 +12,6 @@ require_relative "help_formatter"
15
12
  require_relative "commands/agents"
16
13
  require_relative "commands/hooks"
17
14
  require_relative "commands/project"
18
- require_relative "commands/files"
19
15
 
20
16
  module Aircana
21
17
  module CLI
@@ -49,28 +45,6 @@ module Aircana
49
45
  Install.run
50
46
  end
51
47
 
52
- class FilesSubcommand < Subcommand
53
- desc "add", "Interactively add files to current context"
54
- def add
55
- Files.add
56
- end
57
-
58
- desc "add-dir [DIRECTORY_PATH]", "Add all files from directory to context"
59
- def add_dir(directory_path)
60
- Files.add_dir(directory_path)
61
- end
62
-
63
- desc "clear", "Remove all files from current context"
64
- def clear
65
- Files.clear
66
- end
67
-
68
- desc "list", "Show current relevant files"
69
- def list
70
- Files.list
71
- end
72
- end
73
-
74
48
  class AgentsSubcommand < Subcommand
75
49
  desc "create", "Create a new agent"
76
50
  def create
@@ -91,6 +65,11 @@ module Aircana
91
65
  def add_url(agent, url)
92
66
  Agents.add_url(agent, url)
93
67
  end
68
+
69
+ desc "refresh-all", "Refresh knowledge for all configured agents"
70
+ def refresh_all
71
+ Agents.refresh_all
72
+ end
94
73
  end
95
74
 
96
75
  class HooksSubcommand < Subcommand
@@ -147,9 +126,6 @@ module Aircana
147
126
  end
148
127
  end
149
128
 
150
- desc "files", "Manage relevant files for context"
151
- subcommand "files", FilesSubcommand
152
-
153
129
  desc "agents", "Create and manage agents and their knowledgebases"
154
130
  subcommand "agents", AgentsSubcommand
155
131
 
@@ -101,6 +101,38 @@ module Aircana
101
101
  exit 1
102
102
  end
103
103
 
104
+ def refresh_all # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
105
+ agent_names = all_agents
106
+
107
+ if agent_names.empty?
108
+ Aircana.human_logger.info "No agents found to refresh."
109
+ return
110
+ end
111
+
112
+ Aircana.human_logger.info "Starting refresh for #{agent_names.size} agent(s)..."
113
+
114
+ results = {
115
+ total: agent_names.size,
116
+ successful: 0,
117
+ failed: 0,
118
+ total_pages: 0,
119
+ failed_agents: []
120
+ }
121
+
122
+ agent_names.each do |agent_name|
123
+ result = refresh_single_agent(agent_name)
124
+ if result[:success]
125
+ results[:successful] += 1
126
+ results[:total_pages] += result[:pages_count]
127
+ else
128
+ results[:failed] += 1
129
+ results[:failed_agents] << { name: agent_name, error: result[:error] }
130
+ end
131
+ end
132
+
133
+ print_refresh_all_summary(results)
134
+ end
135
+
104
136
  private
105
137
 
106
138
  def perform_refresh(normalized_agent)
@@ -329,6 +361,52 @@ module Aircana
329
361
  def find_available_editor
330
362
  %w[code subl atom nano vim vi].find { |cmd| system("which #{cmd} > /dev/null 2>&1") }
331
363
  end
364
+
365
+ def all_agents
366
+ agent_dir = Aircana.configuration.agent_knowledge_dir
367
+ return [] unless Dir.exist?(agent_dir)
368
+
369
+ find_agent_folders(agent_dir)
370
+ end
371
+
372
+ def refresh_single_agent(agent_name) # rubocop:disable Metrics/MethodLength
373
+ Aircana.human_logger.info "Refreshing agent '#{agent_name}'..."
374
+
375
+ begin
376
+ result = perform_manifest_aware_refresh(agent_name)
377
+ {
378
+ success: true,
379
+ pages_count: result[:pages_count],
380
+ sources: result[:sources]
381
+ }
382
+ rescue Aircana::Error => e
383
+ Aircana.human_logger.error "Failed to refresh agent '#{agent_name}': #{e.message}"
384
+ {
385
+ success: false,
386
+ pages_count: 0,
387
+ sources: [],
388
+ error: e.message
389
+ }
390
+ end
391
+ end
392
+
393
+ def print_refresh_all_summary(results) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
394
+ Aircana.human_logger.info ""
395
+ Aircana.human_logger.info "=== Refresh All Summary ==="
396
+ Aircana.human_logger.success "✓ Successful: #{results[:successful]}/#{results[:total]} agents"
397
+ Aircana.human_logger.success "✓ Total pages refreshed: #{results[:total_pages]}"
398
+
399
+ if results[:failed].positive?
400
+ Aircana.human_logger.error "✗ Failed: #{results[:failed]} agents"
401
+ Aircana.human_logger.info ""
402
+ Aircana.human_logger.info "Failed agents:"
403
+ results[:failed_agents].each do |failed_agent|
404
+ Aircana.human_logger.error " - #{failed_agent[:name]}: #{failed_agent[:error]}"
405
+ end
406
+ end
407
+
408
+ Aircana.human_logger.info ""
409
+ end
332
410
  end
333
411
  end
334
412
  end
@@ -59,7 +59,6 @@ module Aircana
59
59
  check_directory("~/.aircana", "Global Aircana directory")
60
60
  check_directory(".aircana", "Project Aircana directory")
61
61
  check_agents_status
62
- check_relevant_files_status
63
62
  end
64
63
 
65
64
  def check_agents_status
@@ -74,17 +73,6 @@ module Aircana
74
73
  log_remedy("Create agents with: aircana agents create")
75
74
  end
76
75
  end
77
-
78
- def check_relevant_files_status
79
- relevant_files_dir = File.join(Dir.pwd, ".aircana", "relevant_files")
80
- if Dir.exist?(relevant_files_dir) && !Dir.empty?(relevant_files_dir)
81
- file_count = Dir.glob(File.join(relevant_files_dir, "*")).size
82
- log_success("relevant_files", "#{file_count} file(s) in context")
83
- else
84
- log_info("relevant_files", "No relevant files added yet")
85
- log_remedy("Add files with: aircana add-files")
86
- end
87
- end
88
76
  end
89
77
 
90
78
  module OptionalIntegrations
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../shell_command"
4
- require_relative "../../contexts/relevant_files"
5
4
 
6
5
  module Aircana
7
6
  module CLI
8
7
  module DumpContext
9
8
  class << self
10
- def run(_agent_name:, verbose: true)
9
+ def run(_agent_name:, _verbose: true)
11
10
  Aircana.logger.level = Logger::ERROR
12
- Contexts::RelevantFiles.print(verbose:)
11
+ Aircana.human_logger.info("Context dumping functionality has been removed.")
13
12
  end
14
13
 
15
14
  private
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../generators/relevant_files_command_generator"
4
- require_relative "../../generators/relevant_files_verbose_results_generator"
5
3
  require_relative "../../generators/plan_command_generator"
6
4
  require_relative "../../generators/write_plan_command_generator"
5
+ require_relative "../../generators/ask_expert_command_generator"
7
6
  require_relative "../../generators/agents_generator"
8
7
  require_relative "../../generators/hooks_generator"
9
8
  require_relative "../../generators/project_config_generator"
@@ -14,10 +13,9 @@ module Aircana
14
13
  class << self
15
14
  def generators
16
15
  @generators ||= [
17
- Aircana::Generators::RelevantFilesVerboseResultsGenerator.new,
18
- Aircana::Generators::RelevantFilesCommandGenerator.new,
19
16
  Aircana::Generators::PlanCommandGenerator.new,
20
- Aircana::Generators::WritePlanCommandGenerator.new
17
+ Aircana::Generators::WritePlanCommandGenerator.new,
18
+ Aircana::Generators::AskExpertCommandGenerator.new
21
19
  ]
22
20
  end
23
21
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Aircana
4
4
  class Configuration
5
- attr_accessor :global_dir, :project_dir, :relevant_project_files_dir, :stream, :output_dir,
5
+ attr_accessor :global_dir, :project_dir, :stream, :output_dir,
6
6
  :claude_code_config_path, :claude_code_project_config_path, :agent_knowledge_dir,
7
7
  :hooks_dir, :confluence_base_url, :confluence_username, :confluence_api_token
8
8
 
@@ -18,7 +18,6 @@ module Aircana
18
18
  def setup_directory_paths
19
19
  @global_dir = File.join(Dir.home, ".aircana")
20
20
  @project_dir = Dir.pwd
21
- @relevant_project_files_dir = File.join(@project_dir, ".aircana", "relevant_files")
22
21
  @output_dir = File.join(@global_dir, "aircana.out")
23
22
  @agent_knowledge_dir = File.join(@project_dir, ".aircana", "agents")
24
23
  @hooks_dir = File.join(@project_dir, ".aircana", "hooks")
@@ -98,9 +98,7 @@ module Aircana
98
98
 
99
99
  def extract_page_metadata(page)
100
100
  {
101
- "id" => page["id"],
102
- "title" => page["title"],
103
- "last_updated" => page.dig("version", "when") || Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
101
+ "id" => page["id"]
104
102
  }
105
103
  end
106
104
 
@@ -27,10 +27,7 @@ module Aircana
27
27
 
28
28
  if File.exist?(manifest_path)
29
29
  existing_data = JSON.parse(File.read(manifest_path))
30
- manifest_data = existing_data.merge({
31
- "last_updated" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
32
- "sources" => sources
33
- })
30
+ manifest_data = existing_data.merge({ "sources" => sources })
34
31
  else
35
32
  manifest_data = build_manifest_data(agent, sources)
36
33
  end
@@ -87,13 +84,9 @@ module Aircana
87
84
  end
88
85
 
89
86
  def build_manifest_data(agent, sources)
90
- timestamp = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
91
-
92
87
  {
93
88
  "version" => "1.0",
94
89
  "agent" => agent,
95
- "created" => timestamp,
96
- "last_updated" => timestamp,
97
90
  "sources" => sources
98
91
  }
99
92
  end
@@ -157,8 +150,6 @@ module Aircana
157
150
  raise ManifestError, "Each URL entry must be a hash" unless url_entry.is_a?(Hash)
158
151
 
159
152
  raise ManifestError, "URL entry missing required field: url" unless url_entry.key?("url")
160
-
161
- raise ManifestError, "URL entry missing required field: title" unless url_entry.key?("title")
162
153
  end
163
154
  end
164
155
  end
@@ -210,9 +210,7 @@ module Aircana
210
210
 
211
211
  def build_url_metadata(page_data)
212
212
  {
213
- "url" => page_data[:url],
214
- "title" => page_data[:title],
215
- "last_fetched" => page_data[:last_fetched]
213
+ "url" => page_data[:url]
216
214
  }
217
215
  end
218
216
 
@@ -7,7 +7,7 @@ module Aircana
7
7
  class AgentsGenerator < BaseGenerator
8
8
  attr_reader :agent_name, :short_description, :description, :model, :color, :default_agent
9
9
 
10
- AVAILABLE_DEFAULT_AGENTS = %w[planner jira].freeze
10
+ AVAILABLE_DEFAULT_AGENTS = %w[planner jira sub-agent-coordinator].freeze
11
11
 
12
12
  class << self
13
13
  def create_default_agent(agent_name)
@@ -44,7 +44,7 @@ module Aircana
44
44
 
45
45
  def locals
46
46
  super.merge({
47
- relevant_project_files_path:, agent_name:, short_description:, description:,
47
+ agent_name:, short_description:, description:,
48
48
  model:, color:, knowledge_path:
49
49
  })
50
50
  end
@@ -63,10 +63,6 @@ module Aircana
63
63
  File.join(Aircana.configuration.claude_code_project_config_path, "agents", "#{agent_name}.md")
64
64
  end
65
65
 
66
- def relevant_project_files_path
67
- File.join(Aircana.configuration.relevant_project_files_dir, "relevant_files.md")
68
- end
69
-
70
66
  def knowledge_path
71
67
  ".aircana/agents/#{agent_name}/knowledge/"
72
68
  end
@@ -4,7 +4,7 @@ require_relative "../generators"
4
4
 
5
5
  module Aircana
6
6
  module Generators
7
- class RelevantFilesCommandGenerator < BaseGenerator
7
+ class AskExpertCommandGenerator < BaseGenerator
8
8
  def initialize(file_in: nil, file_out: nil)
9
9
  super(
10
10
  file_in: file_in || default_template_path,
@@ -12,24 +12,14 @@ module Aircana
12
12
  )
13
13
  end
14
14
 
15
- protected
16
-
17
- def locals
18
- super.merge({ relevant_project_files_path: })
19
- end
20
-
21
15
  private
22
16
 
23
17
  def default_template_path
24
- File.join(File.dirname(__FILE__), "..", "templates", "commands", "add_relevant_files.erb")
18
+ File.join(File.dirname(__FILE__), "..", "templates", "commands", "ask_expert.erb")
25
19
  end
26
20
 
27
21
  def default_output_path
28
- File.join(Aircana.configuration.output_dir, "commands", "air-add-relevant-files.md")
29
- end
30
-
31
- def relevant_project_files_path
32
- File.join(Aircana.configuration.relevant_project_files_dir, "relevant_files.md")
22
+ File.join(Aircana.configuration.output_dir, "commands", "air-ask-expert.md")
33
23
  end
34
24
  end
35
25
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "generators/base_generator"
4
- require_relative "generators/relevant_files_command_generator"
5
- require_relative "generators/relevant_files_verbose_results_generator"
6
4
 
7
5
  module Aircana
8
6
  module Generators