shai-cli 0.1.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.
data/lib/shai/ui.rb ADDED
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+ require "tty-prompt"
5
+ require "tty-spinner"
6
+ require "tty-table"
7
+
8
+ module Shai
9
+ class UI
10
+ def initialize(color: true)
11
+ @pastel = Pastel.new(enabled: color)
12
+ @prompt = TTY::Prompt.new
13
+ end
14
+
15
+ # Output helpers
16
+ def success(message)
17
+ puts @pastel.green("✓ #{message}")
18
+ end
19
+
20
+ def error(message)
21
+ puts @pastel.red("Error: #{message}")
22
+ end
23
+
24
+ def warning(message)
25
+ puts @pastel.yellow("Warning: #{message}")
26
+ end
27
+
28
+ def info(message)
29
+ puts message
30
+ end
31
+
32
+ def blank
33
+ puts
34
+ end
35
+
36
+ def header(title)
37
+ puts @pastel.bold(title)
38
+ end
39
+
40
+ def indent(text, spaces: 2)
41
+ prefix = " " * spaces
42
+ puts text.lines.map { |line| "#{prefix}#{line}" }.join
43
+ end
44
+
45
+ # Formatting
46
+ def dim(text)
47
+ @pastel.dim(text)
48
+ end
49
+
50
+ def bold(text)
51
+ @pastel.bold(text)
52
+ end
53
+
54
+ def cyan(text)
55
+ @pastel.cyan(text)
56
+ end
57
+
58
+ def yellow(text)
59
+ @pastel.yellow(text)
60
+ end
61
+
62
+ def green(text)
63
+ @pastel.green(text)
64
+ end
65
+
66
+ def red(text)
67
+ @pastel.red(text)
68
+ end
69
+
70
+ # Interactive prompts
71
+ def ask(question, **options)
72
+ @prompt.ask(question, **options)
73
+ end
74
+
75
+ def mask(question, **options)
76
+ @prompt.mask(question, **options)
77
+ end
78
+
79
+ def yes?(question, default: false)
80
+ require "io/console"
81
+
82
+ hint = default ? "(Y/n)" : "(y/N)"
83
+ print "#{question} #{hint} "
84
+ $stdout.flush
85
+
86
+ loop do
87
+ key = $stdin.getch
88
+ case key
89
+ when "y", "Y"
90
+ puts "y"
91
+ return true
92
+ when "n", "N"
93
+ puts "n"
94
+ return false
95
+ when "\r", "\n"
96
+ puts default ? "y" : "n"
97
+ return default
98
+ when "\u0003" # Ctrl+C
99
+ puts
100
+ raise Interrupt
101
+ end
102
+ # Ignore other keys, keep waiting
103
+ end
104
+ end
105
+
106
+ def select(question, choices, **options)
107
+ @prompt.select(question, choices, **options)
108
+ end
109
+
110
+ # Spinner for async operations
111
+ def spinner(message)
112
+ spinner = TTY::Spinner.new("[:spinner] #{message}", format: :dots)
113
+ spinner.auto_spin
114
+ result = yield
115
+ spinner.success
116
+ result
117
+ rescue => e
118
+ spinner.error
119
+ raise e
120
+ end
121
+
122
+ # Table output
123
+ def table(headers, rows)
124
+ table = TTY::Table.new(header: headers, rows: rows)
125
+ puts table.render(:unicode, padding: [0, 1])
126
+ end
127
+
128
+ # Diff output
129
+ def diff(text)
130
+ text.each_line do |line|
131
+ case line[0]
132
+ when "+"
133
+ puts @pastel.green(line)
134
+ when "-"
135
+ puts @pastel.red(line)
136
+ when "@"
137
+ puts @pastel.cyan(line)
138
+ else
139
+ puts line
140
+ end
141
+ end
142
+ end
143
+
144
+ # Configuration display
145
+ def display_configuration(config, detailed: false)
146
+ name = config["slug"] || config["name"]
147
+ # owner can be a string (username) or a hash with "username" key
148
+ owner = config["owner"]
149
+ owner = owner["username"] if owner.is_a?(Hash)
150
+ full_name = owner ? "#{owner}/#{name}" : name
151
+ visibility = config["visibility"]
152
+ stars = config["stars_count"] || 0
153
+ description = config["description"]
154
+
155
+ line = " #{full_name}"
156
+ line += " (#{visibility})" if visibility == "private"
157
+ line += " #{yellow("★")} #{stars}" if stars.positive?
158
+ puts line
159
+
160
+ if description && !description.empty?
161
+ puts " #{dim(description)}"
162
+ end
163
+
164
+ if detailed
165
+ tags = config["tags"] || []
166
+ puts " #{dim("Tags: #{tags.join(", ")}")}" if tags.any?
167
+ end
168
+ end
169
+
170
+ # Tree display
171
+ def display_tree(items, prefix: "")
172
+ items.each_with_index do |item, index|
173
+ is_last = index == items.length - 1
174
+ connector = is_last ? "└── " : "├── "
175
+ puts "#{prefix}#{connector}#{item}"
176
+ end
177
+ end
178
+
179
+ # File operation display
180
+ def display_file_operation(operation, path)
181
+ case operation
182
+ when :created
183
+ puts " #{green("Created")} #{path}"
184
+ when :modified
185
+ puts " #{yellow("Modified")} #{path}"
186
+ when :updated
187
+ puts " #{yellow("Updated")} #{path}"
188
+ when :deleted
189
+ puts " #{red("Deleted")} #{path}"
190
+ when :uploaded
191
+ puts " #{cyan("Uploading")} #{path}"
192
+ when :would_create
193
+ puts " #{path}"
194
+ when :would_update
195
+ puts " #{yellow("~")} #{path}"
196
+ when :conflict
197
+ puts " #{red(path)}"
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shai
4
+ VERSION = "0.1.0"
5
+ end
data/lib/shai.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "shai/version"
4
+ require_relative "shai/configuration"
5
+ require_relative "shai/credentials"
6
+ require_relative "shai/api_client"
7
+ require_relative "shai/cli"
8
+
9
+ module Shai
10
+ class Error < StandardError; end
11
+
12
+ class AuthenticationError < Error; end
13
+
14
+ class NotFoundError < Error; end
15
+
16
+ class PermissionDeniedError < Error; end
17
+
18
+ class NetworkError < Error; end
19
+
20
+ class InvalidConfigurationError < Error; end
21
+
22
+ # Exit codes as specified in tech spec
23
+ EXIT_SUCCESS = 0
24
+ EXIT_GENERAL_ERROR = 1
25
+ EXIT_AUTH_REQUIRED = 2
26
+ EXIT_PERMISSION_DENIED = 3
27
+ EXIT_NOT_FOUND = 4
28
+ EXIT_NETWORK_ERROR = 5
29
+ EXIT_INVALID_INPUT = 6
30
+
31
+ class << self
32
+ def configuration
33
+ @configuration ||= Configuration.new
34
+ end
35
+
36
+ def configure
37
+ yield(configuration)
38
+ end
39
+
40
+ def credentials
41
+ @credentials ||= Credentials.new
42
+ end
43
+
44
+ def api_client
45
+ @api_client ||= ApiClient.new
46
+ end
47
+
48
+ def reset!
49
+ @configuration = nil
50
+ @credentials = nil
51
+ @api_client = nil
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shai-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastian Jimenez
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: faraday
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.9'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.9'
40
+ - !ruby/object:Gem::Dependency
41
+ name: tty-prompt
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.23'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.23'
54
+ - !ruby/object:Gem::Dependency
55
+ name: tty-spinner
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.9'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.9'
68
+ - !ruby/object:Gem::Dependency
69
+ name: tty-table
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.12'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.12'
82
+ - !ruby/object:Gem::Dependency
83
+ name: pastel
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.8'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.8'
96
+ - !ruby/object:Gem::Dependency
97
+ name: diffy
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.4'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.4'
110
+ description: A command-line interface for shaicli.dev - download, share, and sync
111
+ AI agent configurations (Claude, Cursor, etc.) across projects and teams.
112
+ email:
113
+ - sebastian@infinitlab.co
114
+ executables:
115
+ - shai
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - LICENSE
120
+ - README.md
121
+ - bin/shai
122
+ - lib/shai.rb
123
+ - lib/shai/api_client.rb
124
+ - lib/shai/cli.rb
125
+ - lib/shai/commands/auth.rb
126
+ - lib/shai/commands/config.rb
127
+ - lib/shai/commands/configurations.rb
128
+ - lib/shai/commands/sync.rb
129
+ - lib/shai/configuration.rb
130
+ - lib/shai/credentials.rb
131
+ - lib/shai/ui.rb
132
+ - lib/shai/version.rb
133
+ homepage: https://shaicli.dev
134
+ licenses:
135
+ - MIT
136
+ metadata:
137
+ homepage_uri: https://shaicli.dev
138
+ source_code_uri: https://github.com/infinitlab/shai-cli
139
+ changelog_uri: https://github.com/infinitlab/shai-cli/blob/main/CHANGELOG.md
140
+ rubygems_mfa_required: 'true'
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 3.1.0
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubygems_version: 3.6.9
156
+ specification_version: 4
157
+ summary: CLI tool for managing shared AI agent configurations
158
+ test_files: []