sxn 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce0aa4443b381e4f3e6edb7f5131f5e7749ef37d76c751f6fd9119e12c6e6097
4
- data.tar.gz: 4458f97e8982e5dffad49023ad7dc918680f4507a69fe4a18c1c54a0c98a154a
3
+ metadata.gz: b9758f35bc9f122c019b15dd8a46f712cda58eb86495c6e5bb8c63791b10525e
4
+ data.tar.gz: 628449e55edf8b33ade3f83cdfab4369234bd22e26e5625cc3890b3c46a74702
5
5
  SHA512:
6
- metadata.gz: 7bb89bc46f2961d3041c685e0553819e7a32aea6f92f27be4c5f512df1900d3fd7fa85147b49c51bf813d625495b20792ae7ee0a0124aa44c3fff94359316b65
7
- data.tar.gz: aefb73b0c8072b5a234d6c4979b43de448dcc39c4521c970acf1e5a050b38adf408ce0a0e0f9136a8cdc853ce75ae3878e5df54df5e5f8d0e259a260cedde981
6
+ metadata.gz: 7970fbb77d1b7b21da3b93d566bb54a42219c787ca081b2319ec6c3dcf7b31dee35761339da42322ac2acf393232fa458325e3573dfebec546caa2e14f9da0eb
7
+ data.tar.gz: bd4f880ea4b2aa55b1a0de1d87d6e6bcc41a787218d29c104c644c87d07d2c43048b77217ba6ceae3f3df66a2da63c531a6b7f6cc96781d1454f0a2622232d1a
data/.parallel_rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
@@ -0,0 +1,3 @@
1
+ spec/unit
2
+ spec/integration
3
+ spec/performance
data/.rspec CHANGED
@@ -1,4 +1,5 @@
1
1
  --require spec_helper
2
2
  --color
3
3
  --format documentation
4
- --order random
4
+ --order random
5
+ --pattern '**/*_spec.rb'
data/Gemfile CHANGED
@@ -10,8 +10,9 @@ gem "rake", "~> 13.0"
10
10
 
11
11
  gem "rubocop", "~> 1.21"
12
12
 
13
- # Test coverage
13
+ # Test coverage and parallel testing
14
14
  group :test do
15
+ gem "parallel_tests", "~> 4.0"
15
16
  gem "simplecov", "~> 0.22", require: false
16
17
  gem "simplecov-console", require: false
17
18
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sxn (0.2.0)
4
+ sxn (0.2.2)
5
5
  async (~> 2.0)
6
6
  bcrypt (~> 3.1)
7
7
  dry-configurable (~> 1.0)
@@ -146,6 +146,8 @@ GEM
146
146
  openssl (3.3.0)
147
147
  ostruct (0.6.3)
148
148
  parallel (1.27.0)
149
+ parallel_tests (4.10.1)
150
+ parallel
149
151
  parser (3.3.9.0)
150
152
  ast (~> 2.4.1)
151
153
  racc
@@ -311,6 +313,7 @@ DEPENDENCIES
311
313
  faker (~> 3.2)
312
314
  irb
313
315
  memory_profiler (~> 1.0)
316
+ parallel_tests (~> 4.0)
314
317
  rake (~> 13.0)
315
318
  rbs (~> 3.4)
316
319
  rbs_rails (~> 0.12)
data/README.md CHANGED
@@ -223,3 +223,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/yourus
223
223
  ## License
224
224
 
225
225
  The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
226
+
227
+ ## Author's Note
228
+
229
+ This is a personal project that leverages Claude Code as the primary and active developer. As we continue to refine the development process and iron out any kinks, you can expect builds to gradually become more stable. Your patience and feedback are greatly appreciated as we evolve this tool together.
data/Rakefile CHANGED
@@ -13,6 +13,47 @@ rescue LoadError
13
13
  # RSpec not available
14
14
  end
15
15
 
16
+ # Parallel testing tasks
17
+ begin
18
+ require "parallel_tests/tasks"
19
+ rescue LoadError
20
+ # parallel_tests not available
21
+ end
22
+
23
+ namespace :parallel do
24
+ desc "Run specs in parallel"
25
+ task :spec do
26
+ sh "bundle exec parallel_rspec spec/unit spec/integration spec/performance --runtime-log tmp/parallel_runtime_rspec.log"
27
+ end
28
+
29
+ desc "Run specs in parallel with coverage"
30
+ task :spec_with_coverage do
31
+ ENV["ENABLE_SIMPLECOV"] = "true"
32
+ sh "bundle exec parallel_rspec spec/unit spec/integration spec/performance --runtime-log tmp/parallel_runtime_rspec.log"
33
+ end
34
+
35
+ desc "Setup parallel test databases (if needed)"
36
+ task :setup do
37
+ puts "No database setup needed for this project"
38
+ end
39
+
40
+ desc "Run specs in parallel with custom processor count"
41
+ task :spec_custom, [:processors] do |_t, args|
42
+ processors = args[:processors] || 4
43
+ sh "bundle exec parallel_rspec -n #{processors} spec/unit spec/integration spec/performance --runtime-log tmp/parallel_runtime_rspec.log"
44
+ end
45
+
46
+ desc "Generate parallel test runtime log"
47
+ task :generate_runtime do
48
+ puts "Generating runtime log for balanced test distribution..."
49
+ ENV["TEST_ENV_NUMBER"] = "1"
50
+ specs = "spec/unit spec/integration spec/performance"
51
+ sh "bundle exec rspec --format progress --format ParallelTests::RSpec::RuntimeLogger " \
52
+ "--out tmp/parallel_runtime_rspec.log #{specs}"
53
+ puts "Runtime log generated at tmp/parallel_runtime_rspec.log"
54
+ end
55
+ end
56
+
16
57
  # Type checking tasks
17
58
  namespace :rbs do
18
59
  desc "Validate RBS files syntax"
@@ -0,0 +1,124 @@
1
+ # Parallel Testing Guide
2
+
3
+ ## Overview
4
+
5
+ The sxn project uses `parallel_tests` gem to run RSpec tests in parallel, significantly reducing test execution time from ~35 seconds to ~11 seconds.
6
+
7
+ ## Configuration
8
+
9
+ ### `.parallel_rspec` File
10
+
11
+ This file configures RSpec formatters for parallel execution:
12
+
13
+ ```
14
+ --format progress
15
+ --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
16
+ ```
17
+
18
+ The RuntimeLogger records execution times for each test file, enabling balanced test distribution across parallel processes.
19
+
20
+ ### Runtime-Based Test Distribution
21
+
22
+ Tests are distributed across parallel processes based on their historical execution times, ensuring all processes finish at roughly the same time.
23
+
24
+ ## Local Usage
25
+
26
+ ### Run Tests in Parallel
27
+
28
+ ```bash
29
+ # Default parallel execution (uses all available CPUs)
30
+ bundle exec rake parallel:spec
31
+
32
+ # With custom processor count
33
+ bundle exec rake parallel:spec_custom[2] # Use 2 processes
34
+
35
+ # With coverage
36
+ bundle exec rake parallel:spec_with_coverage
37
+ ```
38
+
39
+ ### Generate/Update Runtime Log
40
+
41
+ ```bash
42
+ # Generate initial runtime log or update existing one
43
+ bundle exec rake parallel:generate_runtime
44
+ ```
45
+
46
+ The runtime log is stored at `tmp/parallel_runtime_rspec.log` and contains execution times for each spec file.
47
+
48
+ ### Direct parallel_rspec Command
49
+
50
+ ```bash
51
+ # Run with runtime balancing
52
+ bundle exec parallel_rspec spec/unit spec/integration spec/performance --runtime-log tmp/parallel_runtime_rspec.log
53
+
54
+ # Verbose output to see how tests are distributed
55
+ bundle exec parallel_rspec spec --verbose-command --runtime-log tmp/parallel_runtime_rspec.log
56
+ ```
57
+
58
+ ## CI/CD Integration
59
+
60
+ GitHub Actions is configured to:
61
+
62
+ 1. **Cache Runtime Log**: The runtime log is cached between runs using the spec files' hash as the cache key
63
+ 2. **Parallel Matrix Jobs**: Tests run across 4 parallel jobs
64
+ 3. **Automatic Generation**: If no cached runtime log exists, CI generates one automatically
65
+ 4. **Result Aggregation**: Test results from all parallel jobs are collected and summarized
66
+
67
+ ### CI Configuration
68
+
69
+ The workflow uses a matrix strategy to run tests in parallel:
70
+
71
+ ```yaml
72
+ matrix:
73
+ ruby: ['3.4.5']
74
+ ci_node_total: [4]
75
+ ci_node_index: [0, 1, 2, 3]
76
+ ```
77
+
78
+ Each job runs a subset of tests based on the runtime log distribution.
79
+
80
+ ## Benefits
81
+
82
+ - **Speed**: ~3x faster test execution (35s → 11s)
83
+ - **Balanced Distribution**: Tests are distributed based on execution time, not file count
84
+ - **CI Optimization**: Parallel jobs in CI reduce overall build time
85
+ - **Coverage Support**: SimpleCov properly merges results from parallel processes
86
+
87
+ ## Troubleshooting
88
+
89
+ ### Unbalanced Test Distribution
90
+
91
+ If tests aren't evenly distributed:
92
+
93
+ 1. Regenerate the runtime log: `bundle exec rake parallel:generate_runtime`
94
+ 2. Check the log file exists: `ls -la tmp/parallel_runtime_rspec.log`
95
+ 3. Verify the format (should be `path/to/spec.rb:execution_time`)
96
+
97
+ ### Missing Runtime Log
98
+
99
+ The system will fall back to file-size-based distribution if no runtime log is found. To ensure runtime-based distribution:
100
+
101
+ ```bash
102
+ # Generate if missing
103
+ [ -f tmp/parallel_runtime_rspec.log ] || bundle exec rake parallel:generate_runtime
104
+ ```
105
+
106
+ ### Coverage Issues
107
+
108
+ Ensure SimpleCov is configured for parallel tests in `spec/spec_helper.rb`:
109
+
110
+ ```ruby
111
+ if ENV["TEST_ENV_NUMBER"]
112
+ SimpleCov.command_name "RSpec_#{ENV['TEST_ENV_NUMBER']}"
113
+ SimpleCov.merge_timeout 3600
114
+ end
115
+ ```
116
+
117
+ ## Performance Metrics
118
+
119
+ Current performance with 4 parallel processes:
120
+ - Sequential execution: ~35 seconds
121
+ - Parallel execution: ~11 seconds
122
+ - Speedup: ~3.2x
123
+
124
+ The runtime-based distribution ensures all processes finish within 1-2 seconds of each other, maximizing efficiency.
data/lib/sxn/CLI.rb CHANGED
@@ -32,16 +32,8 @@ module Sxn
32
32
  # steep:ignore:start - Thor dynamic argument validation handled at runtime
33
33
  # Thor framework uses metaprogramming for argument parsing that can't be statically typed.
34
34
  # Runtime validation ensures type safety through Thor's built-in validation.
35
- # Validate arguments, filtering out nil values for optional arguments
36
- args_for_validation = [folder].compact
37
- expected_arg_count = folder.nil? ? 0 : 1
38
-
39
- RuntimeValidations.validate_thor_arguments("init", args_for_validation, options, {
40
- args: { count: [expected_arg_count], types: [String] },
41
- options: { force: :boolean, auto_detect: :boolean, quiet: :boolean }
42
- })
43
-
44
35
  Commands::Init.new.init(folder)
36
+ # steep:ignore:end
45
37
  rescue Sxn::Error => e
46
38
  handle_error(e)
47
39
  end
@@ -78,6 +70,30 @@ module Sxn
78
70
  handle_error(e)
79
71
  end
80
72
 
73
+ desc "remove [SESSION_NAME]", "Remove a session (shortcut for 'sxn sessions remove')"
74
+ option :force, type: :boolean, aliases: "-f", desc: "Force removal even with uncommitted changes"
75
+ def remove(session_name = nil)
76
+ cmd = Commands::Sessions.new
77
+ cmd.options = options
78
+ cmd.remove(session_name)
79
+ rescue Sxn::Error => e
80
+ handle_error(e)
81
+ end
82
+
83
+ desc "archive [SESSION_NAME]", "Archive a session (shortcut for 'sxn sessions archive')"
84
+ def archive(session_name = nil)
85
+ Commands::Sessions.new.archive(session_name)
86
+ rescue Sxn::Error => e
87
+ handle_error(e)
88
+ end
89
+
90
+ desc "activate [SESSION_NAME]", "Activate an archived session (shortcut for 'sxn sessions activate')"
91
+ def activate(session_name = nil)
92
+ Commands::Sessions.new.activate(session_name)
93
+ rescue Sxn::Error => e
94
+ handle_error(e)
95
+ end
96
+
81
97
  desc "projects SUBCOMMAND", "Manage project configurations"
82
98
  def projects(subcommand = nil, *args)
83
99
  Commands::Projects.start([subcommand, *args].compact)
@@ -200,18 +216,14 @@ module Sxn
200
216
 
201
217
  # steep:ignore:start - Safe integer to string coercion for UI display
202
218
  # These integer values are safely converted to strings for display purposes.
203
- # Runtime validation ensures proper type handling.
204
- @ui.key_value("Total Sessions",
205
- RuntimeValidations.validate_and_coerce_type(sessions.size, String, "session count display"))
206
- @ui.key_value("Total Projects",
207
- RuntimeValidations.validate_and_coerce_type(projects.size, String, "project count display"))
219
+ @ui.key_value("Total Sessions", sessions.size.to_s)
220
+ @ui.key_value("Total Projects", projects.size.to_s)
208
221
 
209
222
  # Active worktrees
210
223
  if current_session
211
224
  worktree_manager = Sxn::Core::WorktreeManager.new(config_manager, session_manager)
212
225
  worktrees = worktree_manager.list_worktrees(session_name: current_session)
213
- @ui.key_value("Active Worktrees",
214
- RuntimeValidations.validate_and_coerce_type(worktrees.size, String, "worktree count display"))
226
+ @ui.key_value("Active Worktrees", worktrees.size.to_s)
215
227
  end
216
228
 
217
229
  @ui.newline
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
+ require "time"
4
5
 
5
6
  module Sxn
6
7
  module Commands
@@ -79,7 +80,8 @@ module Sxn
79
80
  name = @prompt.select("Select session to remove:", choices)
80
81
  end
81
82
 
82
- unless @prompt.confirm_deletion(name, "session")
83
+ # Skip confirmation if force flag is used
84
+ if !options[:force] && !@prompt.confirm_deletion(name, "session")
83
85
  @ui.info("Cancelled")
84
86
  return
85
87
  end
@@ -259,8 +261,8 @@ module Sxn
259
261
 
260
262
  @ui.key_value("Linear Task", session[:linear_task]) if session[:linear_task]
261
263
 
262
- @ui.key_value("Created", session[:created_at] || "Unknown")
263
- @ui.key_value("Updated", session[:updated_at] || "Unknown")
264
+ @ui.key_value("Created", format_timestamp(session[:created_at]))
265
+ @ui.key_value("Updated", format_timestamp(session[:updated_at]))
264
266
 
265
267
  if verbose && session[:projects]&.any?
266
268
  @ui.newline
@@ -295,6 +297,19 @@ module Sxn
295
297
  @ui.newline
296
298
  @ui.recovery_suggestion("Create your first session with 'sxn add <session-name>'")
297
299
  end
300
+
301
+ def format_timestamp(timestamp)
302
+ return "Unknown" if timestamp.nil? || timestamp.empty?
303
+
304
+ # Parse the ISO8601 timestamp and convert to local time
305
+ time = Time.parse(timestamp)
306
+ local_time = time.localtime
307
+
308
+ # Format as "YYYY-MM-DD HH:MM:SS AM/PM Timezone"
309
+ local_time.strftime("%Y-%m-%d %I:%M:%S %p %Z")
310
+ rescue ArgumentError
311
+ timestamp # Return original if parsing fails
312
+ end
298
313
  end
299
314
  end
300
315
  end
@@ -25,7 +25,7 @@ module Sxn
25
25
  # - Bulk operations: < 100ms for 100 sessions
26
26
  class SessionDatabase
27
27
  # Current database schema version for migrations
28
- SCHEMA_VERSION = 1
28
+ SCHEMA_VERSION = 2
29
29
 
30
30
  # Default database path relative to sxn config directory
31
31
  DEFAULT_DB_PATH = ".sxn/sessions.db"
@@ -122,7 +122,7 @@ module Sxn
122
122
  filters ||= {}
123
123
  limit = limit.to_i if limit
124
124
  offset = offset.to_i if offset
125
-
125
+
126
126
  query_parts = ["SELECT * FROM sessions"]
127
127
  params = []
128
128
 
@@ -420,6 +420,19 @@ module Sxn
420
420
  def setup_database
421
421
  current_version = get_schema_version
422
422
 
423
+ # Check if this is an old database without version info
424
+ # If sessions table exists but version is 0, it's likely an old database
425
+ if current_version.zero? && table_exists?("sessions")
426
+ # Check if it's the old schema (without worktrees/projects columns)
427
+ columns = connection.execute("PRAGMA table_info(sessions)").map { |col| col["name"] }
428
+ if !columns.include?("worktrees") || !columns.include?("projects")
429
+ # This is an old database, set it to version 1 so we can migrate it
430
+ Sxn.logger.info "Detected old database schema without version info, treating as version 1"
431
+ set_schema_version(1)
432
+ current_version = 1
433
+ end
434
+ end
435
+
423
436
  if current_version.zero?
424
437
  create_initial_schema
425
438
  set_schema_version(SCHEMA_VERSION)
@@ -428,6 +441,14 @@ module Sxn
428
441
  end
429
442
  end
430
443
 
444
+ # Check if a table exists
445
+ def table_exists?(table_name)
446
+ result = connection.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", table_name)
447
+ !result.empty?
448
+ rescue SQLite3::Exception
449
+ false
450
+ end
451
+
431
452
  # Get current schema version from database
432
453
  def get_schema_version
433
454
  result = connection.execute("PRAGMA user_version").first
@@ -501,12 +522,41 @@ module Sxn
501
522
  end
502
523
 
503
524
  # Run database migrations from old version to current
504
- def run_migrations(_from_version)
505
- # Future migrations will be implemented here
506
- # For now, we only have version 1
525
+ def run_migrations(from_version)
526
+ Sxn.logger.info "Running database migrations from version #{from_version} to #{SCHEMA_VERSION}"
527
+
528
+ # Run each migration in sequence
529
+ migrate_to_v1 if from_version < 1
530
+
531
+ migrate_to_v2 if from_version < 2
532
+
507
533
  set_schema_version(SCHEMA_VERSION)
508
534
  end
509
535
 
536
+ # Migration: Initial schema (v1)
537
+ def migrate_to_v1
538
+ # This is handled by create_initial_schema
539
+ create_initial_schema
540
+ end
541
+
542
+ # Migration: Add worktrees and projects columns (v2)
543
+ def migrate_to_v2
544
+ Sxn.logger.info "Migrating database to version 2: Adding worktrees and projects columns"
545
+
546
+ # Check if columns already exist
547
+ columns = connection.execute("PRAGMA table_info(sessions)").map { |col| col["name"] }
548
+
549
+ unless columns.include?("worktrees")
550
+ connection.execute("ALTER TABLE sessions ADD COLUMN worktrees TEXT")
551
+ Sxn.logger.info "Added worktrees column to sessions table"
552
+ end
553
+
554
+ return if columns.include?("projects")
555
+
556
+ connection.execute("ALTER TABLE sessions ADD COLUMN projects TEXT")
557
+ Sxn.logger.info "Added projects column to sessions table"
558
+ end
559
+
510
560
  # Generate secure, unique session ID
511
561
  def generate_session_id
512
562
  SecureRandom.hex(16) # 32 character hex string
@@ -636,7 +686,7 @@ module Sxn
636
686
  param.to_s
637
687
  end
638
688
  end
639
-
689
+
640
690
  connection.execute(sql, sanitized_params)
641
691
  rescue SQLite3::MismatchException => e
642
692
  # Log the error with details for debugging
@@ -6,6 +6,10 @@ module Sxn
6
6
  class << self
7
7
  # Validate Thor command arguments at runtime
8
8
  def validate_thor_arguments(command_name, args, options, validations)
9
+ # Ensure args and options are not nil
10
+ args ||= []
11
+ options ||= {}
12
+
9
13
  # Validate argument count
10
14
  if validations[:args]
11
15
  count_range = validations[:args][:count]
@@ -23,7 +27,7 @@ module Sxn
23
27
  end
24
28
 
25
29
  # Validate options
26
- if validations[:options]
30
+ if validations[:options] && options.respond_to?(:each)
27
31
  options.each do |key, value|
28
32
  validate_option_type(command_name, key, value, validations[:options][key.to_sym]) if validations[:options][key.to_sym]
29
33
  end
data/lib/sxn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sxn
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
data/sig/sxn/cli.rbs CHANGED
@@ -21,6 +21,12 @@ module Sxn
21
21
 
22
22
  def current: () -> void
23
23
 
24
+ def remove: (?String? session_name) -> void
25
+
26
+ def archive: (?String? session_name) -> void
27
+
28
+ def activate: (?String? session_name) -> void
29
+
24
30
  def projects: (?String? subcommand, *String args) -> void
25
31
 
26
32
  def sessions: (?String? subcommand, *String args) -> void
@@ -57,6 +57,10 @@ module Sxn
57
57
 
58
58
  # Suggest creating a session
59
59
  def suggest_create_session: () -> void
60
+
61
+ # Format a timestamp string to local timezone
62
+ # @param timestamp ISO8601 timestamp string
63
+ def format_timestamp: (String? timestamp) -> String
60
64
  end
61
65
  end
62
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sxn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernest Sim
@@ -488,6 +488,8 @@ files:
488
488
  - ".gem_rbs_collection/sqlite3/2.0/.rbs_meta.yaml"
489
489
  - ".gem_rbs_collection/sqlite3/2.0/database.rbs"
490
490
  - ".gem_rbs_collection/sqlite3/2.0/pragmas.rbs"
491
+ - ".parallel_rspec"
492
+ - ".parallel_rspec_options"
491
493
  - ".rspec"
492
494
  - ".rubocop.yml"
493
495
  - ".simplecov"
@@ -499,6 +501,7 @@ files:
499
501
  - Rakefile
500
502
  - Steepfile
501
503
  - bin/sxn
504
+ - docs/parallel_testing.md
502
505
  - lib/sxn.rb
503
506
  - lib/sxn/CLI.rb
504
507
  - lib/sxn/commands.rb
@@ -606,7 +609,6 @@ files:
606
609
  - sig/sxn/ui/prompt.rbs
607
610
  - sig/sxn/ui/table.rbs
608
611
  - sig/sxn/version.rbs
609
- - sxn.gemspec
610
612
  homepage: https://github.com/idl3/sxn
611
613
  licenses:
612
614
  - MIT
data/sxn.gemspec DELETED
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/sxn/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "sxn"
7
- spec.version = Sxn::VERSION
8
- spec.authors = ["Ernest Sim"]
9
- spec.email = ["ernest.codes@gmail.com"]
10
-
11
- spec.summary = "Session management for multi-repository development"
12
- spec.description = "Sxn simplifies git worktree management with intelligent project rules and secure automation"
13
- spec.homepage = "https://github.com/idl3/sxn"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.2.0"
16
-
17
- spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = spec.homepage
20
- spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
21
- spec.metadata["rubygems_mfa_required"] = "true"
22
-
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
- `git ls-files -z`.split("\x0").reject do |f|
27
- (f == __FILE__) ||
28
- f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) ||
29
- f.match(/\.db-(?:shm|wal)\z/) || # Exclude SQLite temp files
30
- f.match(/\.gem\z/) # Exclude gem files
31
- end
32
- end
33
-
34
- spec.bindir = "bin"
35
- spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
36
- spec.require_paths = ["lib"]
37
-
38
- # Core CLI dependencies
39
- spec.add_dependency "pastel", "~> 0.8" # Terminal colors
40
- spec.add_dependency "thor", "~> 1.3" # CLI framework
41
- spec.add_dependency "tty-progressbar", "~> 0.18" # Progress bars
42
- spec.add_dependency "tty-prompt", "~> 0.23" # Interactive prompts
43
- spec.add_dependency "tty-table", "~> 0.12" # Table formatting
44
-
45
- # Configuration and data management
46
- spec.add_dependency "dry-configurable", "~> 1.0" # Configuration management
47
- spec.add_dependency "sqlite3", "~> 1.6" # Session database
48
- spec.add_dependency "zeitwerk", "~> 2.6" # Code loading
49
-
50
- # Template engine (secure, sandboxed)
51
- spec.add_dependency "liquid", "~> 5.4" # Safe template processing
52
-
53
- # MCP server dependencies
54
- spec.add_dependency "async", "~> 2.0" # Async operations
55
- spec.add_dependency "json-schema", "~> 4.0" # Schema validation
56
-
57
- # Security and encryption
58
- spec.add_dependency "bcrypt", "~> 3.1" # Password hashing
59
- spec.add_dependency "openssl", ">= 3.0" # Encryption support
60
- spec.add_dependency "ostruct" # OpenStruct for Ruby 3.5+ compatibility
61
-
62
- # File system operations
63
- spec.add_dependency "listen", "~> 3.8" # File watching for config cache
64
- spec.add_dependency "parallel", "~> 1.23" # Parallel execution
65
-
66
- # Development dependencies
67
- spec.add_development_dependency "aruba", "~> 2.1" # CLI testing
68
- spec.add_development_dependency "benchmark" # Benchmark for Ruby 3.5+ compatibility
69
- spec.add_development_dependency "benchmark-ips", "~> 2.12" # Performance benchmarking
70
- spec.add_development_dependency "bundler", "~> 2.4"
71
- spec.add_development_dependency "climate_control", "~> 1.2" # Environment variable testing
72
- spec.add_development_dependency "faker", "~> 3.2" # Test data generation
73
- spec.add_development_dependency "memory_profiler", "~> 1.0" # Memory profiling
74
- spec.add_development_dependency "rake", "~> 13.0"
75
- spec.add_development_dependency "rspec", "~> 3.12"
76
- spec.add_development_dependency "rubocop", "~> 1.50" # Code linting
77
- spec.add_development_dependency "rubocop-performance", "~> 1.16"
78
- spec.add_development_dependency "rubocop-rspec", "~> 2.19"
79
- spec.add_development_dependency "simplecov", "~> 0.22" # Code coverage
80
- spec.add_development_dependency "vcr", "~> 6.2" # HTTP interaction recording
81
- spec.add_development_dependency "webmock", "~> 3.19" # HTTP mocking for MCP tests
82
-
83
- # For more information and examples about making a new gem, check out our
84
- # guide at: https://bundler.io/guides/creating_gem.html
85
- end