makit 0.0.139 → 0.0.140

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -41
  3. data/exe/makit +5 -5
  4. data/lib/makit/apache.rb +28 -28
  5. data/lib/makit/auto.rb +48 -48
  6. data/lib/makit/cli/build_commands.rb +500 -500
  7. data/lib/makit/cli/generators/base_generator.rb +74 -74
  8. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  9. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  10. data/lib/makit/cli/generators/node_generator.rb +50 -50
  11. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  12. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  13. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  14. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  15. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  16. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
  17. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  18. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  19. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  20. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  21. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  22. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  23. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  24. data/lib/makit/cli/main.rb +69 -67
  25. data/lib/makit/cli/project_commands.rb +868 -868
  26. data/lib/makit/cli/repository_commands.rb +661 -661
  27. data/lib/makit/cli/strategy_commands.rb +51 -0
  28. data/lib/makit/cli/utility_commands.rb +521 -521
  29. data/lib/makit/commands/factory.rb +359 -359
  30. data/lib/makit/commands/middleware/base.rb +73 -73
  31. data/lib/makit/commands/middleware/cache.rb +248 -248
  32. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  33. data/lib/makit/commands/middleware/validator.rb +269 -269
  34. data/lib/makit/commands/request.rb +316 -316
  35. data/lib/makit/commands/result.rb +323 -323
  36. data/lib/makit/commands/runner.rb +385 -385
  37. data/lib/makit/commands/strategies/base.rb +171 -171
  38. data/lib/makit/commands/strategies/child_process.rb +1 -1
  39. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  40. data/lib/makit/commands.rb +50 -50
  41. data/lib/makit/configuration/dotnet_project.rb +12 -12
  42. data/lib/makit/configuration/gitlab_helper.rb +58 -58
  43. data/lib/makit/configuration/project.rb +168 -168
  44. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  45. data/lib/makit/configuration/step.rb +34 -34
  46. data/lib/makit/configuration/timeout.rb +74 -0
  47. data/lib/makit/configuration.rb +15 -14
  48. data/lib/makit/content/default_gitignore.rb +7 -7
  49. data/lib/makit/content/default_gitignore.txt +225 -225
  50. data/lib/makit/content/default_rakefile.rb +13 -13
  51. data/lib/makit/content/gem_rakefile.rb +16 -16
  52. data/lib/makit/context.rb +1 -1
  53. data/lib/makit/data.rb +49 -49
  54. data/lib/makit/directories.rb +140 -140
  55. data/lib/makit/directory.rb +262 -262
  56. data/lib/makit/docs/files.rb +89 -89
  57. data/lib/makit/docs/rake.rb +102 -102
  58. data/lib/makit/dotnet/cli.rb +69 -69
  59. data/lib/makit/dotnet/project.rb +217 -217
  60. data/lib/makit/dotnet/solution.rb +38 -38
  61. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  62. data/lib/makit/dotnet/solution_console.rb +264 -264
  63. data/lib/makit/dotnet/solution_maui.rb +354 -354
  64. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  65. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  66. data/lib/makit/dotnet.rb +102 -102
  67. data/lib/makit/email.rb +90 -90
  68. data/lib/makit/environment.rb +142 -142
  69. data/lib/makit/examples/runner.rb +370 -370
  70. data/lib/makit/exceptions.rb +45 -45
  71. data/lib/makit/fileinfo.rb +24 -24
  72. data/lib/makit/files.rb +43 -43
  73. data/lib/makit/gems.rb +40 -40
  74. data/lib/makit/git/cli.rb +54 -54
  75. data/lib/makit/git/repository.rb +90 -90
  76. data/lib/makit/git.rb +98 -98
  77. data/lib/makit/gitlab_runner.rb +59 -59
  78. data/lib/makit/humanize.rb +137 -137
  79. data/lib/makit/indexer.rb +47 -47
  80. data/lib/makit/logging/configuration.rb +308 -308
  81. data/lib/makit/logging/format_registry.rb +84 -84
  82. data/lib/makit/logging/formatters/base.rb +39 -39
  83. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  84. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  85. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  86. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  87. data/lib/makit/logging/log_request.rb +119 -119
  88. data/lib/makit/logging/logger.rb +199 -199
  89. data/lib/makit/logging/sinks/base.rb +91 -91
  90. data/lib/makit/logging/sinks/console.rb +72 -72
  91. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  92. data/lib/makit/logging/sinks/structured.rb +123 -123
  93. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  94. data/lib/makit/logging.rb +565 -565
  95. data/lib/makit/markdown.rb +75 -75
  96. data/lib/makit/mp/basic_object_mp.rb +17 -17
  97. data/lib/makit/mp/command_mp.rb +13 -13
  98. data/lib/makit/mp/command_request.mp.rb +17 -17
  99. data/lib/makit/mp/project_mp.rb +199 -199
  100. data/lib/makit/mp/string_mp.rb +191 -191
  101. data/lib/makit/nuget.rb +74 -74
  102. data/lib/makit/port.rb +32 -32
  103. data/lib/makit/process.rb +163 -163
  104. data/lib/makit/protoc.rb +107 -107
  105. data/lib/makit/rake/cli.rb +196 -196
  106. data/lib/makit/rake.rb +80 -80
  107. data/lib/makit/ruby/cli.rb +185 -185
  108. data/lib/makit/ruby.rb +25 -25
  109. data/lib/makit/secrets.rb +51 -51
  110. data/lib/makit/serializer.rb +130 -130
  111. data/lib/makit/services/builder.rb +186 -186
  112. data/lib/makit/services/error_handler.rb +226 -226
  113. data/lib/makit/services/repository_manager.rb +231 -231
  114. data/lib/makit/services/validator.rb +112 -112
  115. data/lib/makit/setup/classlib.rb +101 -101
  116. data/lib/makit/setup/gem.rb +268 -268
  117. data/lib/makit/setup/razorclasslib.rb +101 -101
  118. data/lib/makit/setup/runner.rb +54 -54
  119. data/lib/makit/setup.rb +5 -5
  120. data/lib/makit/show.rb +110 -110
  121. data/lib/makit/storage.rb +126 -126
  122. data/lib/makit/symbols.rb +170 -170
  123. data/lib/makit/task_info.rb +130 -130
  124. data/lib/makit/tasks/at_exit.rb +15 -15
  125. data/lib/makit/tasks/build.rb +22 -22
  126. data/lib/makit/tasks/clean.rb +13 -13
  127. data/lib/makit/tasks/configure.rb +10 -10
  128. data/lib/makit/tasks/format.rb +10 -10
  129. data/lib/makit/tasks/hook_manager.rb +443 -443
  130. data/lib/makit/tasks/init.rb +49 -49
  131. data/lib/makit/tasks/integrate.rb +29 -29
  132. data/lib/makit/tasks/pull_incoming.rb +13 -13
  133. data/lib/makit/tasks/setup.rb +13 -13
  134. data/lib/makit/tasks/sync.rb +17 -17
  135. data/lib/makit/tasks/tag.rb +16 -16
  136. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  137. data/lib/makit/tasks/test.rb +22 -22
  138. data/lib/makit/tasks/update.rb +18 -18
  139. data/lib/makit/tasks.rb +20 -20
  140. data/lib/makit/test_cache.rb +239 -239
  141. data/lib/makit/tree.rb +37 -37
  142. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  143. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  144. data/lib/makit/version.rb +99 -99
  145. data/lib/makit/version_util.rb +21 -21
  146. data/lib/makit/wix.rb +95 -95
  147. data/lib/makit/yaml.rb +29 -29
  148. data/lib/makit/zip.rb +17 -17
  149. data/lib/makit copy.rb +44 -44
  150. data/lib/makit.rb +42 -42
  151. metadata +2 -1
@@ -1,868 +1,868 @@
1
- # frozen_string_literal: true
2
-
3
- require "clamp"
4
- require "fileutils"
5
- require "erb"
6
-
7
- module Makit
8
- module Cli
9
- # Consolidated project management commands
10
- # Combines: new, init, setup, work functionality
11
- class ProjectCommand < Clamp::Command
12
- self.description = <<~DESC
13
- Manage project creation, initialization, and workspace setup.
14
-
15
- Available operations:
16
- new - Create new project from template
17
- init - Initialize existing directory as project
18
- setup - Setup project dependencies and configuration#{" "}
19
- work - Open project in IDE or navigate to directory
20
- DESC
21
- end
22
-
23
- # Create new project from template
24
- class NewProjectCommand < Clamp::Command
25
- self.description = <<~DESC
26
- Create a new project from available templates.
27
-
28
- Examples:
29
- makit project new my-app --type ruby
30
- makit project new WebApp --type dotnet --framework net8.0
31
- makit project new api-service --type node
32
- DESC
33
-
34
- parameter "PROJECT_NAME", "Name of the new project", attribute_name: :project_name, required: true
35
- option ["--type", "-t"], "TYPE", "Project type (ruby, dotnet, node, rust)", default: "ruby"
36
- option ["--framework"], "FRAMEWORK", "Target framework (for .NET projects)", default: "net8.0"
37
- option ["--template"], "TEMPLATE", "Specific template to use"
38
- option ["--directory", "-d"], "DIR", "Parent directory for new project", default: Dir.pwd
39
- option ["--dry-run"], :flag, "Show what would be created without actually creating"
40
- option ["--verbose"], :flag, "Show verbose output"
41
-
42
- def execute
43
- validate_project_name
44
- validate_project_type
45
-
46
- project_dir = File.join(directory, project_name)
47
-
48
- if Dir.exist?(project_dir)
49
- puts "Error: Project directory already exists: #{project_dir}"
50
- exit 1
51
- end
52
-
53
- puts "Creating #{type} project: #{project_name}"
54
- puts "Location: #{project_dir}"
55
- puts "Template: #{selected_template}" if verbose?
56
-
57
- return if dry_run?
58
-
59
- create_project_structure(project_dir)
60
- puts "✅ Successfully created #{type} project: #{project_name}"
61
- puts "📁 Project location: #{project_dir}"
62
- puts "\nNext steps:"
63
- puts " cd #{project_name}"
64
- puts " makit project setup # Setup dependencies"
65
- puts " makit project work # Open in IDE"
66
- end
67
-
68
- private
69
-
70
- def validate_project_name
71
- if project_name.nil? || project_name.strip.empty?
72
- puts "Error: Project name cannot be empty"
73
- exit 1
74
- end
75
-
76
- # Check for valid project name characters
77
- return if project_name.match?(/\A[a-zA-Z0-9][a-zA-Z0-9._-]*\z/)
78
-
79
- puts "Error: Project name must start with letter/number and contain only letters, numbers, dots, hyphens, and underscores"
80
- exit 1
81
- end
82
-
83
- def validate_project_type
84
- valid_types = %w[ruby dotnet node rust]
85
- return if valid_types.include?(type)
86
-
87
- puts "Error: Invalid project type '#{type}'. Valid types: #{valid_types.join(", ")}"
88
- exit 1
89
- end
90
-
91
- def selected_template
92
- return template if template
93
-
94
- case type
95
- when "ruby" then "ruby-gem"
96
- when "dotnet" then "dotnet-console"
97
- when "node" then "node-express"
98
- when "rust" then "rust-binary"
99
- else "basic"
100
- end
101
- end
102
-
103
- def create_project_structure(project_dir)
104
- FileUtils.mkdir_p(project_dir)
105
-
106
- case type
107
- when "ruby"
108
- create_ruby_project(project_dir)
109
- when "dotnet"
110
- create_dotnet_project(project_dir)
111
- when "node"
112
- create_node_project(project_dir)
113
- when "rust"
114
- create_rust_project(project_dir)
115
- end
116
- end
117
-
118
- def create_ruby_project(project_dir)
119
- # Create basic Ruby gem structure
120
- FileUtils.mkdir_p(File.join(project_dir, "lib", project_name.tr("-", "_")))
121
- FileUtils.mkdir_p(File.join(project_dir, "test"))
122
- FileUtils.mkdir_p(File.join(project_dir, "bin"))
123
-
124
- # Create main library file
125
- lib_file = File.join(project_dir, "lib", "#{project_name.tr("-", "_")}.rb")
126
- write_template_file(lib_file, ruby_main_template)
127
-
128
- # Create version file
129
- version_file = File.join(project_dir, "lib", project_name.tr("-", "_"), "version.rb")
130
- write_template_file(version_file, ruby_version_template)
131
-
132
- # Create gemspec
133
- gemspec_file = File.join(project_dir, "#{project_name}.gemspec")
134
- write_template_file(gemspec_file, ruby_gemspec_template)
135
-
136
- # Create Gemfile
137
- gemfile = File.join(project_dir, "Gemfile")
138
- write_template_file(gemfile, ruby_gemfile_template)
139
-
140
- # Create Rakefile
141
- rakefile = File.join(project_dir, "Rakefile")
142
- write_template_file(rakefile, ruby_rakefile_template)
143
-
144
- # Create test file
145
- test_file = File.join(project_dir, "test", "test_#{project_name.tr("-", "_")}.rb")
146
- write_template_file(test_file, ruby_test_template)
147
-
148
- # Create README
149
- readme_file = File.join(project_dir, "README.md")
150
- write_template_file(readme_file, readme_template)
151
-
152
- puts " ✅ Created Ruby gem structure" if verbose?
153
- end
154
-
155
- def create_dotnet_project(project_dir)
156
- # Create .NET console application structure
157
- FileUtils.mkdir_p(File.join(project_dir, "src"))
158
-
159
- # Create project file
160
- csproj_file = File.join(project_dir, "src", "#{project_name}.csproj")
161
- write_template_file(csproj_file, dotnet_csproj_template)
162
-
163
- # Create Program.cs
164
- program_file = File.join(project_dir, "src", "Program.cs")
165
- write_template_file(program_file, dotnet_program_template)
166
-
167
- # Create solution file
168
- sln_file = File.join(project_dir, "#{project_name}.sln")
169
- write_template_file(sln_file, dotnet_solution_template)
170
-
171
- # Create README
172
- readme_file = File.join(project_dir, "README.md")
173
- write_template_file(readme_file, readme_template)
174
-
175
- puts " ✅ Created .NET #{framework} project structure" if verbose?
176
- end
177
-
178
- def create_node_project(project_dir)
179
- # Create Node.js project structure
180
- FileUtils.mkdir_p(File.join(project_dir, "src"))
181
- FileUtils.mkdir_p(File.join(project_dir, "test"))
182
-
183
- # Create package.json
184
- package_file = File.join(project_dir, "package.json")
185
- write_template_file(package_file, node_package_template)
186
-
187
- # Create main application file
188
- main_file = File.join(project_dir, "src", "index.js")
189
- write_template_file(main_file, node_main_template)
190
-
191
- # Create test file
192
- test_file = File.join(project_dir, "test", "index.test.js")
193
- write_template_file(test_file, node_test_template)
194
-
195
- # Create README
196
- readme_file = File.join(project_dir, "README.md")
197
- write_template_file(readme_file, readme_template)
198
-
199
- puts " ✅ Created Node.js project structure" if verbose?
200
- end
201
-
202
- def create_rust_project(project_dir)
203
- # Create Rust project structure
204
- FileUtils.mkdir_p(File.join(project_dir, "src"))
205
-
206
- # Create Cargo.toml
207
- cargo_file = File.join(project_dir, "Cargo.toml")
208
- write_template_file(cargo_file, rust_cargo_template)
209
-
210
- # Create main.rs
211
- main_file = File.join(project_dir, "src", "main.rs")
212
- write_template_file(main_file, rust_main_template)
213
-
214
- # Create README
215
- readme_file = File.join(project_dir, "README.md")
216
- write_template_file(readme_file, readme_template)
217
-
218
- puts " ✅ Created Rust project structure" if verbose?
219
- end
220
-
221
- def write_template_file(file_path, template_content)
222
- FileUtils.mkdir_p(File.dirname(file_path))
223
- processed_content = ERB.new(template_content).result(binding)
224
- File.write(file_path, processed_content)
225
- puts " Created: #{file_path}" if verbose?
226
- end
227
-
228
- # Template methods
229
- def ruby_main_template
230
- <<~RUBY
231
- # frozen_string_literal: true
232
-
233
- require_relative "<%= project_name.tr("-", "_") %>/version"
234
-
235
- module <%= project_name.split(/[-_]/).map(&:capitalize).join %>
236
- class Error < StandardError; end
237
- #{" "}
238
- def self.hello
239
- "Hello from <%= project_name %>!"
240
- end
241
- end
242
- RUBY
243
- end
244
-
245
- def ruby_version_template
246
- <<~RUBY
247
- # frozen_string_literal: true
248
-
249
- module <%= project_name.split(/[-_]/).map(&:capitalize).join %>
250
- VERSION = "0.1.0"
251
- end
252
- RUBY
253
- end
254
-
255
- def ruby_gemspec_template
256
- <<~RUBY
257
- # frozen_string_literal: true
258
-
259
- require_relative "lib/<%= project_name.tr("-", "_") %>/version"
260
-
261
- Gem::Specification.new do |spec|
262
- spec.name = "<%= project_name %>"
263
- spec.version = <%= project_name.split(/[-_]/).map(&:capitalize).join %>::VERSION
264
- spec.authors = ["Your Name"]
265
- spec.email = ["your.email@example.com"]
266
-
267
- spec.summary = "A new Ruby gem"
268
- spec.description = "A longer description of your new gem"
269
- spec.homepage = "https://github.com/yourusername/<%= project_name %>"
270
- spec.license = "MIT"
271
-
272
- spec.required_ruby_version = ">= 3.0.0"
273
-
274
- spec.files = Dir.glob("lib/**/*") + ["README.md"]
275
- spec.require_paths = ["lib"]
276
-
277
- spec.add_development_dependency "minitest", "~> 5.0"
278
- spec.add_development_dependency "rake", "~> 13.0"
279
- end
280
- RUBY
281
- end
282
-
283
- def ruby_gemfile_template
284
- <<~RUBY
285
- # frozen_string_literal: true
286
-
287
- source "https://rubygems.org"
288
-
289
- gemspec
290
- RUBY
291
- end
292
-
293
- def ruby_rakefile_template
294
- <<~RUBY
295
- # frozen_string_literal: true
296
-
297
- require "bundler/gem_tasks"
298
- require "rake/testtask"
299
-
300
- Rake::TestTask.new(:test) do |t|
301
- t.libs << "test"
302
- t.libs << "lib"
303
- t.test_files = FileList["test/**/test_*.rb"]
304
- end
305
-
306
- task default: :test
307
- RUBY
308
- end
309
-
310
- def ruby_test_template
311
- <<~RUBY
312
- # frozen_string_literal: true
313
-
314
- require "test_helper"
315
- require "<%= project_name.tr("-", "_") %>"
316
-
317
- class Test<%= project_name.split(/[-_]/).map(&:capitalize).join %> < Minitest::Test
318
- def test_version
319
- refute_nil <%= project_name.split(/[-_]/).map(&:capitalize).join %>::VERSION
320
- end
321
-
322
- def test_hello
323
- assert_equal "Hello from <%= project_name %>!", <%= project_name.split(/[-_]/).map(&:capitalize).join %>.hello
324
- end
325
- end
326
- RUBY
327
- end
328
-
329
- def dotnet_csproj_template
330
- <<~XML
331
- <Project Sdk="Microsoft.NET.Sdk">
332
-
333
- <PropertyGroup>
334
- <OutputType>Exe</OutputType>
335
- <TargetFramework><%= framework %></TargetFramework>
336
- <ImplicitUsings>enable</ImplicitUsings>
337
- <Nullable>enable</Nullable>
338
- </PropertyGroup>
339
-
340
- </Project>
341
- XML
342
- end
343
-
344
- def dotnet_program_template
345
- <<~CSHARP
346
- namespace <%= project_name.split(/[-_]/).map(&:capitalize).join %>;
347
-
348
- class Program
349
- {
350
- static void Main(string[] args)
351
- {
352
- Console.WriteLine("Hello from <%= project_name %>!");
353
- }
354
- }
355
- CSHARP
356
- end
357
-
358
- def dotnet_solution_template
359
- <<~TEXT
360
- Microsoft Visual Studio Solution File, Format Version 12.00
361
- # Visual Studio Version 17
362
- VisualStudioVersion = 17.0.31903.59
363
- MinimumVisualStudioVersion = 10.0.40219.1
364
- Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "<%= project_name %>", "src\\<%= project_name %>.csproj", "{<%= SecureRandom.uuid.upcase %>}"
365
- EndProject
366
- Global
367
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
368
- Debug|Any CPU = Debug|Any CPU
369
- Release|Any CPU = Release|Any CPU
370
- EndGlobalSection
371
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
372
- {<%= SecureRandom.uuid.upcase %>}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
373
- {<%= SecureRandom.uuid.upcase %>}.Debug|Any CPU.Build.0 = Debug|Any CPU
374
- {<%= SecureRandom.uuid.upcase %>}.Release|Any CPU.ActiveCfg = Release|Any CPU
375
- {<%= SecureRandom.uuid.upcase %>}.Release|Any CPU.Build.0 = Release|Any CPU
376
- EndGlobalSection
377
- EndGlobal
378
- TEXT
379
- end
380
-
381
- def node_package_template
382
- <<~JSON
383
- {
384
- "name": "<%= project_name %>",
385
- "version": "1.0.0",
386
- "description": "A new Node.js project",
387
- "main": "src/index.js",
388
- "scripts": {
389
- "start": "node src/index.js",
390
- "test": "jest",
391
- "dev": "nodemon src/index.js"
392
- },
393
- "keywords": [],
394
- "author": "Your Name",
395
- "license": "MIT",
396
- "dependencies": {
397
- "express": "^4.18.0"
398
- },
399
- "devDependencies": {
400
- "jest": "^29.0.0",
401
- "nodemon": "^2.0.0"
402
- }
403
- }
404
- JSON
405
- end
406
-
407
- def node_main_template
408
- <<~JAVASCRIPT
409
- const express = require('express');
410
- const app = express();
411
- const port = process.env.PORT || 3000;
412
-
413
- app.get('/', (req, res) => {
414
- res.json({ message: 'Hello from <%= project_name %>!' });
415
- });
416
-
417
- app.listen(port, () => {
418
- console.log(`<%= project_name %> listening on port ${port}`);
419
- });
420
-
421
- module.exports = app;
422
- JAVASCRIPT
423
- end
424
-
425
- def node_test_template
426
- <<~JAVASCRIPT
427
- const request = require('supertest');
428
- const app = require('../src/index');
429
-
430
- describe('GET /', () => {
431
- it('should return hello message', async () => {
432
- const res = await request(app)
433
- .get('/')
434
- .expect('Content-Type', /json/)
435
- .expect(200);
436
- #{" "}
437
- expect(res.body.message).toBe('Hello from <%= project_name %>!');
438
- });
439
- });
440
- JAVASCRIPT
441
- end
442
-
443
- def rust_cargo_template
444
- <<~TOML
445
- [package]
446
- name = "<%= project_name %>"
447
- version = "0.1.0"
448
- edition = "2021"
449
- authors = ["Your Name <your.email@example.com>"]
450
- description = "A new Rust project"
451
- license = "MIT"
452
-
453
- [dependencies]
454
- TOML
455
- end
456
-
457
- def rust_main_template
458
- <<~RUST
459
- fn main() {
460
- println!("Hello from <%= project_name %>!");
461
- }
462
-
463
- #[cfg(test)]
464
- mod tests {
465
- use super::*;
466
-
467
- #[test]
468
- fn it_works() {
469
- assert_eq!(2 + 2, 4);
470
- }
471
- }
472
- RUST
473
- end
474
-
475
- def readme_template
476
- <<~MARKDOWN
477
- # <%= project_name.split(/[-_]/).map(&:capitalize).join(" ") %>
478
-
479
- A new <%= type %> project created with Makit.
480
-
481
- ## Getting Started
482
-
483
- <% case type %>
484
- <% when "ruby" %>
485
- ```bash
486
- # Install dependencies
487
- bundle install
488
-
489
- # Run tests
490
- rake test
491
-
492
- # Build gem
493
- rake build
494
- ```
495
- <% when "dotnet" %>
496
- ```bash
497
- # Build project
498
- dotnet build
499
-
500
- # Run project
501
- dotnet run --project src/<%= project_name %>.csproj
502
-
503
- # Test project
504
- dotnet test
505
- ```
506
- <% when "node" %>
507
- ```bash
508
- # Install dependencies
509
- npm install
510
-
511
- # Start development server
512
- npm run dev
513
-
514
- # Run tests
515
- npm test
516
- ```
517
- <% when "rust" %>
518
- ```bash
519
- # Build project
520
- cargo build
521
-
522
- # Run project
523
- cargo run
524
-
525
- # Test project
526
- cargo test
527
- ```
528
- <% end %>
529
-
530
- ## Contributing
531
-
532
- 1. Fork the repository
533
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
534
- 3. Commit your changes (`git commit -am 'Add amazing feature'`)
535
- 4. Push to the branch (`git push origin feature/amazing-feature`)
536
- 5. Create a Pull Request
537
-
538
- ## License
539
-
540
- This project is licensed under the MIT License - see the LICENSE file for details.
541
- MARKDOWN
542
- end
543
- end
544
-
545
- # Initialize existing directory as project
546
- class InitProjectCommand < Clamp::Command
547
- self.description = <<~DESC
548
- Initialize an existing directory as a Makit-managed project.
549
-
550
- Examples:
551
- makit project init
552
- makit project init --type ruby
553
- makit project init /path/to/existing/project
554
- DESC
555
-
556
- parameter "PATH", "Path to initialize (default: current directory)", attribute_name: :path, default: Dir.pwd
557
- option ["--type", "-t"], "TYPE", "Project type (ruby, dotnet, node, rust)", default: "auto"
558
- option ["--force"], :flag, "Force initialization even if project files exist"
559
-
560
- def execute
561
- init_path = File.expand_path(path)
562
-
563
- unless Dir.exist?(init_path)
564
- puts "Error: Directory does not exist: #{init_path}"
565
- exit 1
566
- end
567
-
568
- detected_type = detect_project_type(init_path)
569
- actual_type = type == "auto" ? detected_type : type
570
-
571
- if actual_type == "unknown"
572
- puts "Error: Could not detect project type. Please specify --type explicitly."
573
- exit 1
574
- end
575
-
576
- puts "Initializing #{actual_type} project in: #{init_path}"
577
-
578
- if has_project_files?(init_path, actual_type) && !force?
579
- puts "Error: Project files already exist. Use --force to overwrite."
580
- exit 1
581
- end
582
-
583
- initialize_project(init_path, actual_type)
584
- puts "✅ Successfully initialized #{actual_type} project"
585
- end
586
-
587
- private
588
-
589
- def detect_project_type(dir)
590
- return "ruby" if File.exist?(File.join(dir, "Gemfile")) || File.exist?(File.join(dir, ".gemspec"))
591
- return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
592
- return "node" if File.exist?(File.join(dir, "package.json"))
593
- return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
594
-
595
- "unknown"
596
- end
597
-
598
- def has_project_files?(dir, project_type)
599
- case project_type
600
- when "ruby"
601
- File.exist?(File.join(dir, "Rakefile"))
602
- when "dotnet"
603
- Dir.glob(File.join(dir, "*.csproj")).any?
604
- when "node"
605
- File.exist?(File.join(dir, "package.json"))
606
- when "rust"
607
- File.exist?(File.join(dir, "Cargo.toml"))
608
- else
609
- false
610
- end
611
- end
612
-
613
- def initialize_project(dir, project_type)
614
- case project_type
615
- when "ruby"
616
- initialize_ruby_project(dir)
617
- when "dotnet"
618
- initialize_dotnet_project(dir)
619
- when "node"
620
- initialize_node_project(dir)
621
- when "rust"
622
- initialize_rust_project(dir)
623
- end
624
- end
625
-
626
- def initialize_ruby_project(dir)
627
- File.write(File.join(dir, "Rakefile"), basic_ruby_rakefile) unless File.exist?(File.join(dir, "Rakefile"))
628
-
629
- return if Dir.exist?(File.join(dir, "test"))
630
-
631
- FileUtils.mkdir_p(File.join(dir, "test"))
632
- end
633
-
634
- def initialize_dotnet_project(_dir)
635
- # Add any .NET-specific initialization if needed
636
- puts " ✅ .NET project initialized"
637
- end
638
-
639
- def initialize_node_project(_dir)
640
- # Add any Node.js-specific initialization if needed
641
- puts " ✅ Node.js project initialized"
642
- end
643
-
644
- def initialize_rust_project(_dir)
645
- # Add any Rust-specific initialization if needed
646
- puts " ✅ Rust project initialized"
647
- end
648
-
649
- def basic_ruby_rakefile
650
- <<~RUBY
651
- # frozen_string_literal: true
652
-
653
- require "rake/testtask"
654
-
655
- Rake::TestTask.new(:test) do |t|
656
- t.libs << "test"
657
- t.libs << "lib"
658
- t.test_files = FileList["test/**/test_*.rb"]
659
- end
660
-
661
- task default: :test
662
- RUBY
663
- end
664
- end
665
-
666
- # Setup project dependencies and configuration
667
- class SetupProjectCommand < Clamp::Command
668
- self.description = <<~DESC
669
- Setup project dependencies and development environment.
670
-
671
- Examples:
672
- makit project setup
673
- makit project setup --skip-deps
674
- makit project setup /path/to/project
675
- DESC
676
-
677
- parameter "PATH", "Path to project (default: current directory)", attribute_name: :path, default: Dir.pwd
678
- option ["--skip-deps"], :flag, "Skip dependency installation"
679
- option ["--verbose"], :flag, "Show verbose output"
680
-
681
- def execute
682
- project_path = File.expand_path(path)
683
-
684
- unless Dir.exist?(project_path)
685
- puts "Error: Directory does not exist: #{project_path}"
686
- exit 1
687
- end
688
-
689
- project_type = detect_project_type(project_path)
690
-
691
- if project_type == "unknown"
692
- puts "Error: Could not detect project type in: #{project_path}"
693
- exit 1
694
- end
695
-
696
- puts "Setting up #{project_type} project dependencies..."
697
-
698
- Dir.chdir(project_path) do
699
- setup_project(project_type)
700
- end
701
-
702
- puts "✅ Project setup completed successfully"
703
- end
704
-
705
- private
706
-
707
- def detect_project_type(dir)
708
- return "ruby" if File.exist?(File.join(dir, "Gemfile"))
709
- return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
710
- return "node" if File.exist?(File.join(dir, "package.json"))
711
- return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
712
-
713
- "unknown"
714
- end
715
-
716
- def setup_project(project_type)
717
- case project_type
718
- when "ruby"
719
- setup_ruby_project
720
- when "dotnet"
721
- setup_dotnet_project
722
- when "node"
723
- setup_node_project
724
- when "rust"
725
- setup_rust_project
726
- end
727
- end
728
-
729
- def setup_ruby_project
730
- unless skip_deps?
731
- puts " Installing Ruby dependencies..."
732
- system("bundle install") || puts("Warning: bundle install failed")
733
- end
734
-
735
- puts " ✅ Ruby project setup complete"
736
- end
737
-
738
- def setup_dotnet_project
739
- unless skip_deps?
740
- puts " Restoring .NET dependencies..."
741
- system("dotnet restore") || puts("Warning: dotnet restore failed")
742
- end
743
-
744
- puts " ✅ .NET project setup complete"
745
- end
746
-
747
- def setup_node_project
748
- unless skip_deps?
749
- puts " Installing Node.js dependencies..."
750
- if File.exist?("package-lock.json")
751
- system("npm ci") || puts("Warning: npm ci failed")
752
- else
753
- system("npm install") || puts("Warning: npm install failed")
754
- end
755
- end
756
-
757
- puts " ✅ Node.js project setup complete"
758
- end
759
-
760
- def setup_rust_project
761
- unless skip_deps?
762
- puts " Building Rust project..."
763
- system("cargo build") || puts("Warning: cargo build failed")
764
- end
765
-
766
- puts " ✅ Rust project setup complete"
767
- end
768
- end
769
-
770
- # Open project in IDE or navigate to directory
771
- class WorkProjectCommand < Clamp::Command
772
- self.description = <<~DESC
773
- Open project in your preferred IDE or navigate to project directory.
774
-
775
- Examples:
776
- makit project work
777
- makit project work /path/to/project
778
- makit project work --editor code
779
- DESC
780
-
781
- parameter "PATH", "Path to project (default: current directory)", attribute_name: :path, default: Dir.pwd
782
- option ["--editor"], "EDITOR", "Preferred editor (code, subl, atom, vim)", default: "auto"
783
- option ["--list-editors"], :flag, "List available editors"
784
-
785
- def execute
786
- if list_editors?
787
- show_available_editors
788
- return
789
- end
790
-
791
- project_path = File.expand_path(path)
792
-
793
- unless Dir.exist?(project_path)
794
- puts "Error: Directory does not exist: #{project_path}"
795
- exit 1
796
- end
797
-
798
- editor_cmd = determine_editor
799
-
800
- if editor_cmd
801
- puts "Opening project in #{editor_cmd}..."
802
- launch_editor(editor_cmd, project_path)
803
- else
804
- puts "No suitable editor found. Available editors:"
805
- show_available_editors
806
- puts "\nNavigating to project directory: #{project_path}"
807
- puts "Run: cd #{project_path}"
808
- end
809
- end
810
-
811
- private
812
-
813
- def show_available_editors
814
- editors = {
815
- "code" => "Visual Studio Code",
816
- "subl" => "Sublime Text",
817
- "atom" => "Atom",
818
- "vim" => "Vim",
819
- "nvim" => "Neovim",
820
- "emacs" => "Emacs",
821
- }
822
-
823
- puts "Available editors:"
824
- editors.each do |cmd, name|
825
- available = system("which #{cmd} > /dev/null 2>&1")
826
- status = available ? "✅" : "❌"
827
- puts " #{status} #{name} (#{cmd})"
828
- end
829
- end
830
-
831
- def determine_editor
832
- return editor if editor != "auto" && editor_available?(editor)
833
-
834
- # Try to detect available editors in preference order
835
- preferred_editors = %w[code subl atom vim nvim]
836
- preferred_editors.find { |ed| editor_available?(ed) }
837
- end
838
-
839
- def editor_available?(editor_name)
840
- system("which #{editor_name} > /dev/null 2>&1")
841
- end
842
-
843
- def launch_editor(editor_cmd, project_path)
844
- case editor_cmd
845
- when "code"
846
- system("code '#{project_path}'")
847
- when "subl"
848
- system("subl '#{project_path}'")
849
- when "atom"
850
- system("atom '#{project_path}'")
851
- when "vim", "nvim"
852
- system("#{editor_cmd} '#{project_path}'")
853
- when "emacs"
854
- system("emacs '#{project_path}' &")
855
- else
856
- puts "Unknown editor: #{editor_cmd}"
857
- exit 1
858
- end
859
- end
860
- end
861
-
862
- # Add subcommand declarations after all classes are defined
863
- ProjectCommand.subcommand "new", "Create new project from template", NewProjectCommand
864
- ProjectCommand.subcommand "init", "Initialize existing directory as project", InitProjectCommand
865
- ProjectCommand.subcommand "setup", "Setup project dependencies", SetupProjectCommand
866
- ProjectCommand.subcommand "work", "Open project in IDE", WorkProjectCommand
867
- end
868
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "clamp"
4
+ require "fileutils"
5
+ require "erb"
6
+
7
+ module Makit
8
+ module Cli
9
+ # Consolidated project management commands
10
+ # Combines: new, init, setup, work functionality
11
+ class ProjectCommand < Clamp::Command
12
+ self.description = <<~DESC
13
+ Manage project creation, initialization, and workspace setup.
14
+
15
+ Available operations:
16
+ new - Create new project from template
17
+ init - Initialize existing directory as project
18
+ setup - Setup project dependencies and configuration#{" "}
19
+ work - Open project in IDE or navigate to directory
20
+ DESC
21
+ end
22
+
23
+ # Create new project from template
24
+ class NewProjectCommand < Clamp::Command
25
+ self.description = <<~DESC
26
+ Create a new project from available templates.
27
+
28
+ Examples:
29
+ makit project new my-app --type ruby
30
+ makit project new WebApp --type dotnet --framework net8.0
31
+ makit project new api-service --type node
32
+ DESC
33
+
34
+ parameter "PROJECT_NAME", "Name of the new project", attribute_name: :project_name, required: true
35
+ option ["--type", "-t"], "TYPE", "Project type (ruby, dotnet, node, rust)", default: "ruby"
36
+ option ["--framework"], "FRAMEWORK", "Target framework (for .NET projects)", default: "net8.0"
37
+ option ["--template"], "TEMPLATE", "Specific template to use"
38
+ option ["--directory", "-d"], "DIR", "Parent directory for new project", default: Dir.pwd
39
+ option ["--dry-run"], :flag, "Show what would be created without actually creating"
40
+ option ["--verbose"], :flag, "Show verbose output"
41
+
42
+ def execute
43
+ validate_project_name
44
+ validate_project_type
45
+
46
+ project_dir = File.join(directory, project_name)
47
+
48
+ if Dir.exist?(project_dir)
49
+ puts "Error: Project directory already exists: #{project_dir}"
50
+ exit 1
51
+ end
52
+
53
+ puts "Creating #{type} project: #{project_name}"
54
+ puts "Location: #{project_dir}"
55
+ puts "Template: #{selected_template}" if verbose?
56
+
57
+ return if dry_run?
58
+
59
+ create_project_structure(project_dir)
60
+ puts "✅ Successfully created #{type} project: #{project_name}"
61
+ puts "📁 Project location: #{project_dir}"
62
+ puts "\nNext steps:"
63
+ puts " cd #{project_name}"
64
+ puts " makit project setup # Setup dependencies"
65
+ puts " makit project work # Open in IDE"
66
+ end
67
+
68
+ private
69
+
70
+ def validate_project_name
71
+ if project_name.nil? || project_name.strip.empty?
72
+ puts "Error: Project name cannot be empty"
73
+ exit 1
74
+ end
75
+
76
+ # Check for valid project name characters
77
+ return if project_name.match?(/\A[a-zA-Z0-9][a-zA-Z0-9._-]*\z/)
78
+
79
+ puts "Error: Project name must start with letter/number and contain only letters, numbers, dots, hyphens, and underscores"
80
+ exit 1
81
+ end
82
+
83
+ def validate_project_type
84
+ valid_types = %w[ruby dotnet node rust]
85
+ return if valid_types.include?(type)
86
+
87
+ puts "Error: Invalid project type '#{type}'. Valid types: #{valid_types.join(", ")}"
88
+ exit 1
89
+ end
90
+
91
+ def selected_template
92
+ return template if template
93
+
94
+ case type
95
+ when "ruby" then "ruby-gem"
96
+ when "dotnet" then "dotnet-console"
97
+ when "node" then "node-express"
98
+ when "rust" then "rust-binary"
99
+ else "basic"
100
+ end
101
+ end
102
+
103
+ def create_project_structure(project_dir)
104
+ FileUtils.mkdir_p(project_dir)
105
+
106
+ case type
107
+ when "ruby"
108
+ create_ruby_project(project_dir)
109
+ when "dotnet"
110
+ create_dotnet_project(project_dir)
111
+ when "node"
112
+ create_node_project(project_dir)
113
+ when "rust"
114
+ create_rust_project(project_dir)
115
+ end
116
+ end
117
+
118
+ def create_ruby_project(project_dir)
119
+ # Create basic Ruby gem structure
120
+ FileUtils.mkdir_p(File.join(project_dir, "lib", project_name.tr("-", "_")))
121
+ FileUtils.mkdir_p(File.join(project_dir, "test"))
122
+ FileUtils.mkdir_p(File.join(project_dir, "bin"))
123
+
124
+ # Create main library file
125
+ lib_file = File.join(project_dir, "lib", "#{project_name.tr("-", "_")}.rb")
126
+ write_template_file(lib_file, ruby_main_template)
127
+
128
+ # Create version file
129
+ version_file = File.join(project_dir, "lib", project_name.tr("-", "_"), "version.rb")
130
+ write_template_file(version_file, ruby_version_template)
131
+
132
+ # Create gemspec
133
+ gemspec_file = File.join(project_dir, "#{project_name}.gemspec")
134
+ write_template_file(gemspec_file, ruby_gemspec_template)
135
+
136
+ # Create Gemfile
137
+ gemfile = File.join(project_dir, "Gemfile")
138
+ write_template_file(gemfile, ruby_gemfile_template)
139
+
140
+ # Create Rakefile
141
+ rakefile = File.join(project_dir, "Rakefile")
142
+ write_template_file(rakefile, ruby_rakefile_template)
143
+
144
+ # Create test file
145
+ test_file = File.join(project_dir, "test", "test_#{project_name.tr("-", "_")}.rb")
146
+ write_template_file(test_file, ruby_test_template)
147
+
148
+ # Create README
149
+ readme_file = File.join(project_dir, "README.md")
150
+ write_template_file(readme_file, readme_template)
151
+
152
+ puts " ✅ Created Ruby gem structure" if verbose?
153
+ end
154
+
155
+ def create_dotnet_project(project_dir)
156
+ # Create .NET console application structure
157
+ FileUtils.mkdir_p(File.join(project_dir, "src"))
158
+
159
+ # Create project file
160
+ csproj_file = File.join(project_dir, "src", "#{project_name}.csproj")
161
+ write_template_file(csproj_file, dotnet_csproj_template)
162
+
163
+ # Create Program.cs
164
+ program_file = File.join(project_dir, "src", "Program.cs")
165
+ write_template_file(program_file, dotnet_program_template)
166
+
167
+ # Create solution file
168
+ sln_file = File.join(project_dir, "#{project_name}.sln")
169
+ write_template_file(sln_file, dotnet_solution_template)
170
+
171
+ # Create README
172
+ readme_file = File.join(project_dir, "README.md")
173
+ write_template_file(readme_file, readme_template)
174
+
175
+ puts " ✅ Created .NET #{framework} project structure" if verbose?
176
+ end
177
+
178
+ def create_node_project(project_dir)
179
+ # Create Node.js project structure
180
+ FileUtils.mkdir_p(File.join(project_dir, "src"))
181
+ FileUtils.mkdir_p(File.join(project_dir, "test"))
182
+
183
+ # Create package.json
184
+ package_file = File.join(project_dir, "package.json")
185
+ write_template_file(package_file, node_package_template)
186
+
187
+ # Create main application file
188
+ main_file = File.join(project_dir, "src", "index.js")
189
+ write_template_file(main_file, node_main_template)
190
+
191
+ # Create test file
192
+ test_file = File.join(project_dir, "test", "index.test.js")
193
+ write_template_file(test_file, node_test_template)
194
+
195
+ # Create README
196
+ readme_file = File.join(project_dir, "README.md")
197
+ write_template_file(readme_file, readme_template)
198
+
199
+ puts " ✅ Created Node.js project structure" if verbose?
200
+ end
201
+
202
+ def create_rust_project(project_dir)
203
+ # Create Rust project structure
204
+ FileUtils.mkdir_p(File.join(project_dir, "src"))
205
+
206
+ # Create Cargo.toml
207
+ cargo_file = File.join(project_dir, "Cargo.toml")
208
+ write_template_file(cargo_file, rust_cargo_template)
209
+
210
+ # Create main.rs
211
+ main_file = File.join(project_dir, "src", "main.rs")
212
+ write_template_file(main_file, rust_main_template)
213
+
214
+ # Create README
215
+ readme_file = File.join(project_dir, "README.md")
216
+ write_template_file(readme_file, readme_template)
217
+
218
+ puts " ✅ Created Rust project structure" if verbose?
219
+ end
220
+
221
+ def write_template_file(file_path, template_content)
222
+ FileUtils.mkdir_p(File.dirname(file_path))
223
+ processed_content = ERB.new(template_content).result(binding)
224
+ File.write(file_path, processed_content)
225
+ puts " Created: #{file_path}" if verbose?
226
+ end
227
+
228
+ # Template methods
229
+ def ruby_main_template
230
+ <<~RUBY
231
+ # frozen_string_literal: true
232
+
233
+ require_relative "<%= project_name.tr("-", "_") %>/version"
234
+
235
+ module <%= project_name.split(/[-_]/).map(&:capitalize).join %>
236
+ class Error < StandardError; end
237
+ #{" "}
238
+ def self.hello
239
+ "Hello from <%= project_name %>!"
240
+ end
241
+ end
242
+ RUBY
243
+ end
244
+
245
+ def ruby_version_template
246
+ <<~RUBY
247
+ # frozen_string_literal: true
248
+
249
+ module <%= project_name.split(/[-_]/).map(&:capitalize).join %>
250
+ VERSION = "0.1.0"
251
+ end
252
+ RUBY
253
+ end
254
+
255
+ def ruby_gemspec_template
256
+ <<~RUBY
257
+ # frozen_string_literal: true
258
+
259
+ require_relative "lib/<%= project_name.tr("-", "_") %>/version"
260
+
261
+ Gem::Specification.new do |spec|
262
+ spec.name = "<%= project_name %>"
263
+ spec.version = <%= project_name.split(/[-_]/).map(&:capitalize).join %>::VERSION
264
+ spec.authors = ["Your Name"]
265
+ spec.email = ["your.email@example.com"]
266
+
267
+ spec.summary = "A new Ruby gem"
268
+ spec.description = "A longer description of your new gem"
269
+ spec.homepage = "https://github.com/yourusername/<%= project_name %>"
270
+ spec.license = "MIT"
271
+
272
+ spec.required_ruby_version = ">= 3.0.0"
273
+
274
+ spec.files = Dir.glob("lib/**/*") + ["README.md"]
275
+ spec.require_paths = ["lib"]
276
+
277
+ spec.add_development_dependency "minitest", "~> 5.0"
278
+ spec.add_development_dependency "rake", "~> 13.0"
279
+ end
280
+ RUBY
281
+ end
282
+
283
+ def ruby_gemfile_template
284
+ <<~RUBY
285
+ # frozen_string_literal: true
286
+
287
+ source "https://rubygems.org"
288
+
289
+ gemspec
290
+ RUBY
291
+ end
292
+
293
+ def ruby_rakefile_template
294
+ <<~RUBY
295
+ # frozen_string_literal: true
296
+
297
+ require "bundler/gem_tasks"
298
+ require "rake/testtask"
299
+
300
+ Rake::TestTask.new(:test) do |t|
301
+ t.libs << "test"
302
+ t.libs << "lib"
303
+ t.test_files = FileList["test/**/test_*.rb"]
304
+ end
305
+
306
+ task default: :test
307
+ RUBY
308
+ end
309
+
310
+ def ruby_test_template
311
+ <<~RUBY
312
+ # frozen_string_literal: true
313
+
314
+ require "test_helper"
315
+ require "<%= project_name.tr("-", "_") %>"
316
+
317
+ class Test<%= project_name.split(/[-_]/).map(&:capitalize).join %> < Minitest::Test
318
+ def test_version
319
+ refute_nil <%= project_name.split(/[-_]/).map(&:capitalize).join %>::VERSION
320
+ end
321
+
322
+ def test_hello
323
+ assert_equal "Hello from <%= project_name %>!", <%= project_name.split(/[-_]/).map(&:capitalize).join %>.hello
324
+ end
325
+ end
326
+ RUBY
327
+ end
328
+
329
+ def dotnet_csproj_template
330
+ <<~XML
331
+ <Project Sdk="Microsoft.NET.Sdk">
332
+
333
+ <PropertyGroup>
334
+ <OutputType>Exe</OutputType>
335
+ <TargetFramework><%= framework %></TargetFramework>
336
+ <ImplicitUsings>enable</ImplicitUsings>
337
+ <Nullable>enable</Nullable>
338
+ </PropertyGroup>
339
+
340
+ </Project>
341
+ XML
342
+ end
343
+
344
+ def dotnet_program_template
345
+ <<~CSHARP
346
+ namespace <%= project_name.split(/[-_]/).map(&:capitalize).join %>;
347
+
348
+ class Program
349
+ {
350
+ static void Main(string[] args)
351
+ {
352
+ Console.WriteLine("Hello from <%= project_name %>!");
353
+ }
354
+ }
355
+ CSHARP
356
+ end
357
+
358
+ def dotnet_solution_template
359
+ <<~TEXT
360
+ Microsoft Visual Studio Solution File, Format Version 12.00
361
+ # Visual Studio Version 17
362
+ VisualStudioVersion = 17.0.31903.59
363
+ MinimumVisualStudioVersion = 10.0.40219.1
364
+ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "<%= project_name %>", "src\\<%= project_name %>.csproj", "{<%= SecureRandom.uuid.upcase %>}"
365
+ EndProject
366
+ Global
367
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
368
+ Debug|Any CPU = Debug|Any CPU
369
+ Release|Any CPU = Release|Any CPU
370
+ EndGlobalSection
371
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
372
+ {<%= SecureRandom.uuid.upcase %>}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
373
+ {<%= SecureRandom.uuid.upcase %>}.Debug|Any CPU.Build.0 = Debug|Any CPU
374
+ {<%= SecureRandom.uuid.upcase %>}.Release|Any CPU.ActiveCfg = Release|Any CPU
375
+ {<%= SecureRandom.uuid.upcase %>}.Release|Any CPU.Build.0 = Release|Any CPU
376
+ EndGlobalSection
377
+ EndGlobal
378
+ TEXT
379
+ end
380
+
381
+ def node_package_template
382
+ <<~JSON
383
+ {
384
+ "name": "<%= project_name %>",
385
+ "version": "1.0.0",
386
+ "description": "A new Node.js project",
387
+ "main": "src/index.js",
388
+ "scripts": {
389
+ "start": "node src/index.js",
390
+ "test": "jest",
391
+ "dev": "nodemon src/index.js"
392
+ },
393
+ "keywords": [],
394
+ "author": "Your Name",
395
+ "license": "MIT",
396
+ "dependencies": {
397
+ "express": "^4.18.0"
398
+ },
399
+ "devDependencies": {
400
+ "jest": "^29.0.0",
401
+ "nodemon": "^2.0.0"
402
+ }
403
+ }
404
+ JSON
405
+ end
406
+
407
+ def node_main_template
408
+ <<~JAVASCRIPT
409
+ const express = require('express');
410
+ const app = express();
411
+ const port = process.env.PORT || 3000;
412
+
413
+ app.get('/', (req, res) => {
414
+ res.json({ message: 'Hello from <%= project_name %>!' });
415
+ });
416
+
417
+ app.listen(port, () => {
418
+ console.log(`<%= project_name %> listening on port ${port}`);
419
+ });
420
+
421
+ module.exports = app;
422
+ JAVASCRIPT
423
+ end
424
+
425
+ def node_test_template
426
+ <<~JAVASCRIPT
427
+ const request = require('supertest');
428
+ const app = require('../src/index');
429
+
430
+ describe('GET /', () => {
431
+ it('should return hello message', async () => {
432
+ const res = await request(app)
433
+ .get('/')
434
+ .expect('Content-Type', /json/)
435
+ .expect(200);
436
+ #{" "}
437
+ expect(res.body.message).toBe('Hello from <%= project_name %>!');
438
+ });
439
+ });
440
+ JAVASCRIPT
441
+ end
442
+
443
+ def rust_cargo_template
444
+ <<~TOML
445
+ [package]
446
+ name = "<%= project_name %>"
447
+ version = "0.1.0"
448
+ edition = "2021"
449
+ authors = ["Your Name <your.email@example.com>"]
450
+ description = "A new Rust project"
451
+ license = "MIT"
452
+
453
+ [dependencies]
454
+ TOML
455
+ end
456
+
457
+ def rust_main_template
458
+ <<~RUST
459
+ fn main() {
460
+ println!("Hello from <%= project_name %>!");
461
+ }
462
+
463
+ #[cfg(test)]
464
+ mod tests {
465
+ use super::*;
466
+
467
+ #[test]
468
+ fn it_works() {
469
+ assert_eq!(2 + 2, 4);
470
+ }
471
+ }
472
+ RUST
473
+ end
474
+
475
+ def readme_template
476
+ <<~MARKDOWN
477
+ # <%= project_name.split(/[-_]/).map(&:capitalize).join(" ") %>
478
+
479
+ A new <%= type %> project created with Makit.
480
+
481
+ ## Getting Started
482
+
483
+ <% case type %>
484
+ <% when "ruby" %>
485
+ ```bash
486
+ # Install dependencies
487
+ bundle install
488
+
489
+ # Run tests
490
+ rake test
491
+
492
+ # Build gem
493
+ rake build
494
+ ```
495
+ <% when "dotnet" %>
496
+ ```bash
497
+ # Build project
498
+ dotnet build
499
+
500
+ # Run project
501
+ dotnet run --project src/<%= project_name %>.csproj
502
+
503
+ # Test project
504
+ dotnet test
505
+ ```
506
+ <% when "node" %>
507
+ ```bash
508
+ # Install dependencies
509
+ npm install
510
+
511
+ # Start development server
512
+ npm run dev
513
+
514
+ # Run tests
515
+ npm test
516
+ ```
517
+ <% when "rust" %>
518
+ ```bash
519
+ # Build project
520
+ cargo build
521
+
522
+ # Run project
523
+ cargo run
524
+
525
+ # Test project
526
+ cargo test
527
+ ```
528
+ <% end %>
529
+
530
+ ## Contributing
531
+
532
+ 1. Fork the repository
533
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
534
+ 3. Commit your changes (`git commit -am 'Add amazing feature'`)
535
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
536
+ 5. Create a Pull Request
537
+
538
+ ## License
539
+
540
+ This project is licensed under the MIT License - see the LICENSE file for details.
541
+ MARKDOWN
542
+ end
543
+ end
544
+
545
+ # Initialize existing directory as project
546
+ class InitProjectCommand < Clamp::Command
547
+ self.description = <<~DESC
548
+ Initialize an existing directory as a Makit-managed project.
549
+
550
+ Examples:
551
+ makit project init
552
+ makit project init --type ruby
553
+ makit project init /path/to/existing/project
554
+ DESC
555
+
556
+ parameter "PATH", "Path to initialize (default: current directory)", attribute_name: :path, default: Dir.pwd
557
+ option ["--type", "-t"], "TYPE", "Project type (ruby, dotnet, node, rust)", default: "auto"
558
+ option ["--force"], :flag, "Force initialization even if project files exist"
559
+
560
+ def execute
561
+ init_path = File.expand_path(path)
562
+
563
+ unless Dir.exist?(init_path)
564
+ puts "Error: Directory does not exist: #{init_path}"
565
+ exit 1
566
+ end
567
+
568
+ detected_type = detect_project_type(init_path)
569
+ actual_type = type == "auto" ? detected_type : type
570
+
571
+ if actual_type == "unknown"
572
+ puts "Error: Could not detect project type. Please specify --type explicitly."
573
+ exit 1
574
+ end
575
+
576
+ puts "Initializing #{actual_type} project in: #{init_path}"
577
+
578
+ if has_project_files?(init_path, actual_type) && !force?
579
+ puts "Error: Project files already exist. Use --force to overwrite."
580
+ exit 1
581
+ end
582
+
583
+ initialize_project(init_path, actual_type)
584
+ puts "✅ Successfully initialized #{actual_type} project"
585
+ end
586
+
587
+ private
588
+
589
+ def detect_project_type(dir)
590
+ return "ruby" if File.exist?(File.join(dir, "Gemfile")) || File.exist?(File.join(dir, ".gemspec"))
591
+ return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
592
+ return "node" if File.exist?(File.join(dir, "package.json"))
593
+ return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
594
+
595
+ "unknown"
596
+ end
597
+
598
+ def has_project_files?(dir, project_type)
599
+ case project_type
600
+ when "ruby"
601
+ File.exist?(File.join(dir, "Rakefile"))
602
+ when "dotnet"
603
+ Dir.glob(File.join(dir, "*.csproj")).any?
604
+ when "node"
605
+ File.exist?(File.join(dir, "package.json"))
606
+ when "rust"
607
+ File.exist?(File.join(dir, "Cargo.toml"))
608
+ else
609
+ false
610
+ end
611
+ end
612
+
613
+ def initialize_project(dir, project_type)
614
+ case project_type
615
+ when "ruby"
616
+ initialize_ruby_project(dir)
617
+ when "dotnet"
618
+ initialize_dotnet_project(dir)
619
+ when "node"
620
+ initialize_node_project(dir)
621
+ when "rust"
622
+ initialize_rust_project(dir)
623
+ end
624
+ end
625
+
626
+ def initialize_ruby_project(dir)
627
+ File.write(File.join(dir, "Rakefile"), basic_ruby_rakefile) unless File.exist?(File.join(dir, "Rakefile"))
628
+
629
+ return if Dir.exist?(File.join(dir, "test"))
630
+
631
+ FileUtils.mkdir_p(File.join(dir, "test"))
632
+ end
633
+
634
+ def initialize_dotnet_project(_dir)
635
+ # Add any .NET-specific initialization if needed
636
+ puts " ✅ .NET project initialized"
637
+ end
638
+
639
+ def initialize_node_project(_dir)
640
+ # Add any Node.js-specific initialization if needed
641
+ puts " ✅ Node.js project initialized"
642
+ end
643
+
644
+ def initialize_rust_project(_dir)
645
+ # Add any Rust-specific initialization if needed
646
+ puts " ✅ Rust project initialized"
647
+ end
648
+
649
+ def basic_ruby_rakefile
650
+ <<~RUBY
651
+ # frozen_string_literal: true
652
+
653
+ require "rake/testtask"
654
+
655
+ Rake::TestTask.new(:test) do |t|
656
+ t.libs << "test"
657
+ t.libs << "lib"
658
+ t.test_files = FileList["test/**/test_*.rb"]
659
+ end
660
+
661
+ task default: :test
662
+ RUBY
663
+ end
664
+ end
665
+
666
+ # Setup project dependencies and configuration
667
+ class SetupProjectCommand < Clamp::Command
668
+ self.description = <<~DESC
669
+ Setup project dependencies and development environment.
670
+
671
+ Examples:
672
+ makit project setup
673
+ makit project setup --skip-deps
674
+ makit project setup /path/to/project
675
+ DESC
676
+
677
+ parameter "PATH", "Path to project (default: current directory)", attribute_name: :path, default: Dir.pwd
678
+ option ["--skip-deps"], :flag, "Skip dependency installation"
679
+ option ["--verbose"], :flag, "Show verbose output"
680
+
681
+ def execute
682
+ project_path = File.expand_path(path)
683
+
684
+ unless Dir.exist?(project_path)
685
+ puts "Error: Directory does not exist: #{project_path}"
686
+ exit 1
687
+ end
688
+
689
+ project_type = detect_project_type(project_path)
690
+
691
+ if project_type == "unknown"
692
+ puts "Error: Could not detect project type in: #{project_path}"
693
+ exit 1
694
+ end
695
+
696
+ puts "Setting up #{project_type} project dependencies..."
697
+
698
+ Dir.chdir(project_path) do
699
+ setup_project(project_type)
700
+ end
701
+
702
+ puts "✅ Project setup completed successfully"
703
+ end
704
+
705
+ private
706
+
707
+ def detect_project_type(dir)
708
+ return "ruby" if File.exist?(File.join(dir, "Gemfile"))
709
+ return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
710
+ return "node" if File.exist?(File.join(dir, "package.json"))
711
+ return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
712
+
713
+ "unknown"
714
+ end
715
+
716
+ def setup_project(project_type)
717
+ case project_type
718
+ when "ruby"
719
+ setup_ruby_project
720
+ when "dotnet"
721
+ setup_dotnet_project
722
+ when "node"
723
+ setup_node_project
724
+ when "rust"
725
+ setup_rust_project
726
+ end
727
+ end
728
+
729
+ def setup_ruby_project
730
+ unless skip_deps?
731
+ puts " Installing Ruby dependencies..."
732
+ system("bundle install") || puts("Warning: bundle install failed")
733
+ end
734
+
735
+ puts " ✅ Ruby project setup complete"
736
+ end
737
+
738
+ def setup_dotnet_project
739
+ unless skip_deps?
740
+ puts " Restoring .NET dependencies..."
741
+ system("dotnet restore") || puts("Warning: dotnet restore failed")
742
+ end
743
+
744
+ puts " ✅ .NET project setup complete"
745
+ end
746
+
747
+ def setup_node_project
748
+ unless skip_deps?
749
+ puts " Installing Node.js dependencies..."
750
+ if File.exist?("package-lock.json")
751
+ system("npm ci") || puts("Warning: npm ci failed")
752
+ else
753
+ system("npm install") || puts("Warning: npm install failed")
754
+ end
755
+ end
756
+
757
+ puts " ✅ Node.js project setup complete"
758
+ end
759
+
760
+ def setup_rust_project
761
+ unless skip_deps?
762
+ puts " Building Rust project..."
763
+ system("cargo build") || puts("Warning: cargo build failed")
764
+ end
765
+
766
+ puts " ✅ Rust project setup complete"
767
+ end
768
+ end
769
+
770
+ # Open project in IDE or navigate to directory
771
+ class WorkProjectCommand < Clamp::Command
772
+ self.description = <<~DESC
773
+ Open project in your preferred IDE or navigate to project directory.
774
+
775
+ Examples:
776
+ makit project work
777
+ makit project work /path/to/project
778
+ makit project work --editor code
779
+ DESC
780
+
781
+ parameter "PATH", "Path to project (default: current directory)", attribute_name: :path, default: Dir.pwd
782
+ option ["--editor"], "EDITOR", "Preferred editor (code, subl, atom, vim)", default: "auto"
783
+ option ["--list-editors"], :flag, "List available editors"
784
+
785
+ def execute
786
+ if list_editors?
787
+ show_available_editors
788
+ return
789
+ end
790
+
791
+ project_path = File.expand_path(path)
792
+
793
+ unless Dir.exist?(project_path)
794
+ puts "Error: Directory does not exist: #{project_path}"
795
+ exit 1
796
+ end
797
+
798
+ editor_cmd = determine_editor
799
+
800
+ if editor_cmd
801
+ puts "Opening project in #{editor_cmd}..."
802
+ launch_editor(editor_cmd, project_path)
803
+ else
804
+ puts "No suitable editor found. Available editors:"
805
+ show_available_editors
806
+ puts "\nNavigating to project directory: #{project_path}"
807
+ puts "Run: cd #{project_path}"
808
+ end
809
+ end
810
+
811
+ private
812
+
813
+ def show_available_editors
814
+ editors = {
815
+ "code" => "Visual Studio Code",
816
+ "subl" => "Sublime Text",
817
+ "atom" => "Atom",
818
+ "vim" => "Vim",
819
+ "nvim" => "Neovim",
820
+ "emacs" => "Emacs",
821
+ }
822
+
823
+ puts "Available editors:"
824
+ editors.each do |cmd, name|
825
+ available = system("which #{cmd} > /dev/null 2>&1")
826
+ status = available ? "✅" : "❌"
827
+ puts " #{status} #{name} (#{cmd})"
828
+ end
829
+ end
830
+
831
+ def determine_editor
832
+ return editor if editor != "auto" && editor_available?(editor)
833
+
834
+ # Try to detect available editors in preference order
835
+ preferred_editors = %w[code subl atom vim nvim]
836
+ preferred_editors.find { |ed| editor_available?(ed) }
837
+ end
838
+
839
+ def editor_available?(editor_name)
840
+ system("which #{editor_name} > /dev/null 2>&1")
841
+ end
842
+
843
+ def launch_editor(editor_cmd, project_path)
844
+ case editor_cmd
845
+ when "code"
846
+ system("code '#{project_path}'")
847
+ when "subl"
848
+ system("subl '#{project_path}'")
849
+ when "atom"
850
+ system("atom '#{project_path}'")
851
+ when "vim", "nvim"
852
+ system("#{editor_cmd} '#{project_path}'")
853
+ when "emacs"
854
+ system("emacs '#{project_path}' &")
855
+ else
856
+ puts "Unknown editor: #{editor_cmd}"
857
+ exit 1
858
+ end
859
+ end
860
+ end
861
+
862
+ # Add subcommand declarations after all classes are defined
863
+ ProjectCommand.subcommand "new", "Create new project from template", NewProjectCommand
864
+ ProjectCommand.subcommand "init", "Initialize existing directory as project", InitProjectCommand
865
+ ProjectCommand.subcommand "setup", "Setup project dependencies", SetupProjectCommand
866
+ ProjectCommand.subcommand "work", "Open project in IDE", WorkProjectCommand
867
+ end
868
+ end