sxn 0.2.3 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/Gemfile.lock +1 -1
- data/lib/sxn/CLI.rb +107 -4
- data/lib/sxn/commands/init.rb +136 -0
- data/lib/sxn/commands/sessions.rb +307 -7
- data/lib/sxn/commands/templates.rb +230 -0
- data/lib/sxn/commands/worktrees.rb +17 -1
- data/lib/sxn/commands.rb +1 -0
- data/lib/sxn/config/templates_config.rb +153 -0
- data/lib/sxn/config.rb +1 -0
- data/lib/sxn/core/config_manager.rb +4 -0
- data/lib/sxn/core/project_manager.rb +19 -2
- data/lib/sxn/core/rules_manager.rb +61 -3
- data/lib/sxn/core/session_config.rb +96 -0
- data/lib/sxn/core/session_manager.rb +29 -3
- data/lib/sxn/core/template_manager.rb +187 -0
- data/lib/sxn/core/worktree_manager.rb +38 -11
- data/lib/sxn/core.rb +2 -0
- data/lib/sxn/errors.rb +34 -2
- data/lib/sxn/ui/prompt.rb +4 -0
- data/lib/sxn/ui/table.rb +18 -1
- data/lib/sxn/version.rb +1 -1
- metadata +5 -1
|
@@ -14,7 +14,7 @@ module Sxn
|
|
|
14
14
|
@project_manager = ProjectManager.new(@config_manager)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def add_worktree(project_name, branch = nil, session_name: nil)
|
|
17
|
+
def add_worktree(project_name, branch = nil, session_name: nil, verbose: false)
|
|
18
18
|
# Use current session if not specified
|
|
19
19
|
session_name ||= @config_manager.current_session
|
|
20
20
|
raise Sxn::NoActiveSessionError, "No active session. Use 'sxn use <session>' first." unless session_name
|
|
@@ -26,10 +26,12 @@ module Sxn
|
|
|
26
26
|
raise Sxn::ProjectNotFoundError, "Project '#{project_name}' not found" unless project
|
|
27
27
|
|
|
28
28
|
# Determine branch name
|
|
29
|
-
# If no branch specified, use session
|
|
29
|
+
# If no branch specified, use session's default branch from .sxnrc, then fallback to session name
|
|
30
30
|
# If branch starts with "remote:", handle remote branch tracking
|
|
31
31
|
if branch.nil?
|
|
32
|
-
|
|
32
|
+
session_config = SessionConfig.new(session[:path])
|
|
33
|
+
branch = session_config.default_branch if session_config.exists?
|
|
34
|
+
branch ||= session_name
|
|
33
35
|
elsif branch.start_with?("remote:")
|
|
34
36
|
remote_branch = branch.sub("remote:", "")
|
|
35
37
|
# Fetch the remote branch first
|
|
@@ -67,7 +69,7 @@ module Sxn
|
|
|
67
69
|
handle_orphaned_worktree(project[:path], worktree_path)
|
|
68
70
|
|
|
69
71
|
# Create the worktree
|
|
70
|
-
create_git_worktree(project[:path], worktree_path, branch)
|
|
72
|
+
create_git_worktree(project[:path], worktree_path, branch, verbose: verbose)
|
|
71
73
|
|
|
72
74
|
# Register worktree with session
|
|
73
75
|
@session_manager.add_worktree_to_session(session_name, project_name, worktree_path, branch)
|
|
@@ -81,6 +83,11 @@ module Sxn
|
|
|
81
83
|
rescue StandardError => e
|
|
82
84
|
# Clean up on failure
|
|
83
85
|
FileUtils.rm_rf(worktree_path)
|
|
86
|
+
|
|
87
|
+
# If it's already our error with details, re-raise it
|
|
88
|
+
raise e if e.is_a?(Sxn::WorktreeCreationError)
|
|
89
|
+
|
|
90
|
+
# Otherwise wrap it
|
|
84
91
|
raise Sxn::WorktreeCreationError, "Failed to create worktree: #{e.message}"
|
|
85
92
|
end
|
|
86
93
|
end
|
|
@@ -237,7 +244,7 @@ module Sxn
|
|
|
237
244
|
end
|
|
238
245
|
end
|
|
239
246
|
|
|
240
|
-
def create_git_worktree(project_path, worktree_path, branch)
|
|
247
|
+
def create_git_worktree(project_path, worktree_path, branch, verbose: false)
|
|
241
248
|
Dir.chdir(project_path) do
|
|
242
249
|
# Check if branch exists
|
|
243
250
|
branch_exists = system("git show-ref --verify --quiet refs/heads/#{Shellwords.escape(branch)}",
|
|
@@ -274,15 +281,35 @@ module Sxn
|
|
|
274
281
|
error_msg = error_msg.lines.grep(/fatal:/).first&.strip || error_msg
|
|
275
282
|
end
|
|
276
283
|
|
|
277
|
-
|
|
284
|
+
details = []
|
|
285
|
+
details << "Command: #{cmd.join(" ")}"
|
|
286
|
+
details << "Working directory: #{project_path}"
|
|
287
|
+
details << "Target path: #{worktree_path}"
|
|
288
|
+
details << "Branch: #{branch}"
|
|
289
|
+
details << ""
|
|
290
|
+
details << "Git output:"
|
|
291
|
+
details << "STDOUT: #{stdout.strip.empty? ? "(empty)" : stdout}"
|
|
292
|
+
details << "STDERR: #{stderr.strip.empty? ? "(empty)" : stderr}"
|
|
293
|
+
details << "Exit status: #{status.exitstatus}"
|
|
294
|
+
|
|
295
|
+
# Check for common issues
|
|
296
|
+
if !File.directory?(project_path)
|
|
297
|
+
details << "\n⚠️ Project directory does not exist: #{project_path}"
|
|
298
|
+
elsif !File.directory?(File.join(project_path, ".git"))
|
|
299
|
+
details << "\n⚠️ Not a git repository: #{project_path}"
|
|
300
|
+
details << " This might be a git submodule. Try:"
|
|
301
|
+
details << " 1. Ensure the project path points to the submodule directory"
|
|
302
|
+
details << " 2. Check if 'git submodule update --init' has been run"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
details << "\n⚠️ Target path already exists: #{worktree_path}" if File.exist?(worktree_path)
|
|
306
|
+
|
|
307
|
+
if ENV["SXN_DEBUG"] || verbose
|
|
278
308
|
puts "[DEBUG] Git worktree command failed:"
|
|
279
|
-
puts "
|
|
280
|
-
puts " Directory: #{project_path}"
|
|
281
|
-
puts " STDOUT: #{stdout}"
|
|
282
|
-
puts " STDERR: #{stderr}"
|
|
309
|
+
details.each { |line| puts " #{line}" }
|
|
283
310
|
end
|
|
284
311
|
|
|
285
|
-
raise error_msg
|
|
312
|
+
raise Sxn::WorktreeCreationError.new(error_msg, details: details.join("\n"))
|
|
286
313
|
end
|
|
287
314
|
end
|
|
288
315
|
end
|
data/lib/sxn/core.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "core/config_manager"
|
|
4
|
+
require_relative "core/session_config"
|
|
4
5
|
require_relative "core/session_manager"
|
|
5
6
|
require_relative "core/project_manager"
|
|
6
7
|
require_relative "core/worktree_manager"
|
|
7
8
|
require_relative "core/rules_manager"
|
|
9
|
+
require_relative "core/template_manager"
|
|
8
10
|
|
|
9
11
|
module Sxn
|
|
10
12
|
# Core business logic namespace
|
data/lib/sxn/errors.rb
CHANGED
|
@@ -37,7 +37,16 @@ module Sxn
|
|
|
37
37
|
class WorktreeError < GitError; end
|
|
38
38
|
class WorktreeExistsError < WorktreeError; end
|
|
39
39
|
class WorktreeNotFoundError < WorktreeError; end
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
class WorktreeCreationError < WorktreeError
|
|
42
|
+
attr_reader :details
|
|
43
|
+
|
|
44
|
+
def initialize(message, details: nil)
|
|
45
|
+
super(message)
|
|
46
|
+
@details = details
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
class WorktreeRemovalError < WorktreeError; end
|
|
42
51
|
class BranchError < GitError; end
|
|
43
52
|
|
|
@@ -59,11 +68,34 @@ module Sxn
|
|
|
59
68
|
class ApplicationError < Error; end
|
|
60
69
|
class RollbackError < Error; end
|
|
61
70
|
|
|
62
|
-
# Template processing errors
|
|
71
|
+
# Template processing errors (Liquid templates)
|
|
63
72
|
class TemplateError < Error; end
|
|
64
73
|
class TemplateNotFoundError < TemplateError; end
|
|
65
74
|
class TemplateProcessingError < TemplateError; end
|
|
66
75
|
|
|
76
|
+
# Session template errors (worktree templates)
|
|
77
|
+
class SessionTemplateError < Error; end
|
|
78
|
+
|
|
79
|
+
class SessionTemplateNotFoundError < SessionTemplateError
|
|
80
|
+
def initialize(name, available: [])
|
|
81
|
+
message = "Session template '#{name}' not found"
|
|
82
|
+
message += ". Available templates: #{available.join(", ")}" if available.any?
|
|
83
|
+
super(message)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class SessionTemplateValidationError < SessionTemplateError
|
|
88
|
+
def initialize(name, message)
|
|
89
|
+
super("Invalid session template '#{name}': #{message}")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class SessionTemplateApplicationError < SessionTemplateError
|
|
94
|
+
def initialize(template_name, message)
|
|
95
|
+
super("Failed to apply template '#{template_name}': #{message}. Session has been rolled back.")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
67
99
|
# Database errors
|
|
68
100
|
class DatabaseError < Error; end
|
|
69
101
|
class DatabaseConnectionError < DatabaseError; end
|
data/lib/sxn/ui/prompt.rb
CHANGED
|
@@ -68,6 +68,10 @@ module Sxn
|
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
def default_branch(session_name:)
|
|
72
|
+
branch_name("Default branch for worktrees:", default: session_name)
|
|
73
|
+
end
|
|
74
|
+
|
|
71
75
|
def confirm_deletion(item_name, item_type = "item")
|
|
72
76
|
ask_yes_no("Are you sure you want to delete #{item_type} '#{item_name}'? This action cannot be undone.",
|
|
73
77
|
default: false)
|
data/lib/sxn/ui/table.rb
CHANGED
|
@@ -89,6 +89,21 @@ module Sxn
|
|
|
89
89
|
render_table(headers, rows)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
def templates(templates)
|
|
93
|
+
return empty_table("No templates defined") if templates.empty?
|
|
94
|
+
|
|
95
|
+
headers = %w[Name Description Projects]
|
|
96
|
+
rows = templates.map do |template|
|
|
97
|
+
[
|
|
98
|
+
template[:name],
|
|
99
|
+
template[:description] || "-",
|
|
100
|
+
template[:project_count].to_s
|
|
101
|
+
]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
render_table(headers, rows)
|
|
105
|
+
end
|
|
106
|
+
|
|
92
107
|
# Add a header to the table output
|
|
93
108
|
def header(title)
|
|
94
109
|
puts "\n#{@pastel.bold.underline(title)}"
|
|
@@ -99,7 +114,9 @@ module Sxn
|
|
|
99
114
|
|
|
100
115
|
def render_table(headers, rows)
|
|
101
116
|
table = TTY::Table.new(header: headers, rows: rows)
|
|
102
|
-
|
|
117
|
+
# Use basic renderer to avoid terminal width detection issues
|
|
118
|
+
renderer = $stdout.tty? ? :unicode : :basic
|
|
119
|
+
puts table.render(renderer, padding: [0, 1])
|
|
103
120
|
end
|
|
104
121
|
|
|
105
122
|
def empty_table(message)
|
data/lib/sxn/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ernest Sim
|
|
@@ -509,16 +509,20 @@ files:
|
|
|
509
509
|
- lib/sxn/commands/projects.rb
|
|
510
510
|
- lib/sxn/commands/rules.rb
|
|
511
511
|
- lib/sxn/commands/sessions.rb
|
|
512
|
+
- lib/sxn/commands/templates.rb
|
|
512
513
|
- lib/sxn/commands/worktrees.rb
|
|
513
514
|
- lib/sxn/config.rb
|
|
514
515
|
- lib/sxn/config/config_cache.rb
|
|
515
516
|
- lib/sxn/config/config_discovery.rb
|
|
516
517
|
- lib/sxn/config/config_validator.rb
|
|
518
|
+
- lib/sxn/config/templates_config.rb
|
|
517
519
|
- lib/sxn/core.rb
|
|
518
520
|
- lib/sxn/core/config_manager.rb
|
|
519
521
|
- lib/sxn/core/project_manager.rb
|
|
520
522
|
- lib/sxn/core/rules_manager.rb
|
|
523
|
+
- lib/sxn/core/session_config.rb
|
|
521
524
|
- lib/sxn/core/session_manager.rb
|
|
525
|
+
- lib/sxn/core/template_manager.rb
|
|
522
526
|
- lib/sxn/core/worktree_manager.rb
|
|
523
527
|
- lib/sxn/database.rb
|
|
524
528
|
- lib/sxn/database/errors.rb
|