tsikol 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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/CONTRIBUTING.md +84 -0
  4. data/LICENSE +21 -0
  5. data/README.md +579 -0
  6. data/Rakefile +12 -0
  7. data/docs/README.md +69 -0
  8. data/docs/api/middleware.md +721 -0
  9. data/docs/api/prompt.md +858 -0
  10. data/docs/api/resource.md +651 -0
  11. data/docs/api/server.md +509 -0
  12. data/docs/api/test-helpers.md +591 -0
  13. data/docs/api/tool.md +527 -0
  14. data/docs/cookbook/authentication.md +651 -0
  15. data/docs/cookbook/caching.md +877 -0
  16. data/docs/cookbook/dynamic-tools.md +970 -0
  17. data/docs/cookbook/error-handling.md +887 -0
  18. data/docs/cookbook/logging.md +1044 -0
  19. data/docs/cookbook/rate-limiting.md +717 -0
  20. data/docs/examples/code-assistant.md +922 -0
  21. data/docs/examples/complete-server.md +726 -0
  22. data/docs/examples/database-manager.md +1198 -0
  23. data/docs/examples/devops-tools.md +1382 -0
  24. data/docs/examples/echo-server.md +501 -0
  25. data/docs/examples/weather-service.md +822 -0
  26. data/docs/guides/completion.md +472 -0
  27. data/docs/guides/getting-started.md +462 -0
  28. data/docs/guides/middleware.md +823 -0
  29. data/docs/guides/project-structure.md +434 -0
  30. data/docs/guides/prompts.md +920 -0
  31. data/docs/guides/resources.md +720 -0
  32. data/docs/guides/sampling.md +804 -0
  33. data/docs/guides/testing.md +863 -0
  34. data/docs/guides/tools.md +627 -0
  35. data/examples/README.md +92 -0
  36. data/examples/advanced_features.rb +129 -0
  37. data/examples/basic-migrated/app/prompts/weather_chat.rb +44 -0
  38. data/examples/basic-migrated/app/resources/weather_alerts.rb +18 -0
  39. data/examples/basic-migrated/app/tools/get_current_weather.rb +34 -0
  40. data/examples/basic-migrated/app/tools/get_forecast.rb +30 -0
  41. data/examples/basic-migrated/app/tools/get_weather_by_coords.rb +48 -0
  42. data/examples/basic-migrated/server.rb +25 -0
  43. data/examples/basic.rb +73 -0
  44. data/examples/full_featured.rb +175 -0
  45. data/examples/middleware_example.rb +112 -0
  46. data/examples/sampling_example.rb +104 -0
  47. data/examples/weather-service/app/prompts/weather/chat.rb +90 -0
  48. data/examples/weather-service/app/resources/weather/alerts.rb +59 -0
  49. data/examples/weather-service/app/tools/weather/get_current.rb +82 -0
  50. data/examples/weather-service/app/tools/weather/get_forecast.rb +90 -0
  51. data/examples/weather-service/server.rb +28 -0
  52. data/exe/tsikol +6 -0
  53. data/lib/tsikol/cli/templates/Gemfile.erb +10 -0
  54. data/lib/tsikol/cli/templates/README.md.erb +38 -0
  55. data/lib/tsikol/cli/templates/gitignore.erb +49 -0
  56. data/lib/tsikol/cli/templates/prompt.rb.erb +53 -0
  57. data/lib/tsikol/cli/templates/resource.rb.erb +29 -0
  58. data/lib/tsikol/cli/templates/server.rb.erb +24 -0
  59. data/lib/tsikol/cli/templates/tool.rb.erb +60 -0
  60. data/lib/tsikol/cli.rb +203 -0
  61. data/lib/tsikol/error_handler.rb +141 -0
  62. data/lib/tsikol/health.rb +198 -0
  63. data/lib/tsikol/http_transport.rb +72 -0
  64. data/lib/tsikol/lifecycle.rb +149 -0
  65. data/lib/tsikol/middleware.rb +168 -0
  66. data/lib/tsikol/prompt.rb +101 -0
  67. data/lib/tsikol/resource.rb +53 -0
  68. data/lib/tsikol/router.rb +190 -0
  69. data/lib/tsikol/server.rb +660 -0
  70. data/lib/tsikol/stdio_transport.rb +108 -0
  71. data/lib/tsikol/test_helpers.rb +261 -0
  72. data/lib/tsikol/tool.rb +111 -0
  73. data/lib/tsikol/version.rb +5 -0
  74. data/lib/tsikol.rb +72 -0
  75. metadata +219 -0
@@ -0,0 +1,922 @@
1
+ # Code Assistant Example
2
+
3
+ An intelligent MCP server that provides code analysis, generation, and refactoring capabilities with AI-powered features.
4
+
5
+ ## Overview
6
+
7
+ This example demonstrates:
8
+ - File system operations
9
+ - Code analysis and parsing
10
+ - AI-powered code generation (sampling)
11
+ - Syntax highlighting
12
+ - Git integration
13
+ - Code completion
14
+ - Advanced prompt engineering
15
+
16
+ ## Implementation
17
+
18
+ ### server.rb
19
+
20
+ ```ruby
21
+ #!/usr/bin/env ruby
22
+
23
+ require 'tsikol'
24
+ require 'parser/current'
25
+ require 'syntax_tree'
26
+ require 'rugged'
27
+ require 'rouge'
28
+
29
+ # Code analysis tool
30
+ class CodeAnalyzerTool < Tsikol::Tool
31
+ name "analyze_code"
32
+ description "Analyze code for quality, complexity, and potential issues"
33
+
34
+ parameter :file_path do
35
+ type :string
36
+ required
37
+ description "Path to the file to analyze"
38
+
39
+ complete do |partial|
40
+ Dir.glob("#{partial}*").select { |f| File.file?(f) }
41
+ end
42
+ end
43
+
44
+ parameter :checks do
45
+ type :array
46
+ optional
47
+ default ["complexity", "style", "security"]
48
+ description "Types of checks to perform"
49
+ end
50
+
51
+ def execute(file_path:, checks: ["complexity", "style", "security"])
52
+ unless File.exist?(file_path)
53
+ raise ArgumentError, "File not found: #{file_path}"
54
+ end
55
+
56
+ content = File.read(file_path)
57
+ language = detect_language(file_path)
58
+
59
+ results = {
60
+ file: file_path,
61
+ language: language,
62
+ metrics: analyze_metrics(content, language),
63
+ issues: []
64
+ }
65
+
66
+ # Perform requested checks
67
+ checks.each do |check|
68
+ case check
69
+ when "complexity"
70
+ results[:issues].concat(check_complexity(content, language))
71
+ when "style"
72
+ results[:issues].concat(check_style(content, language))
73
+ when "security"
74
+ results[:issues].concat(check_security(content, language))
75
+ end
76
+ end
77
+
78
+ format_analysis_results(results)
79
+ end
80
+
81
+ private
82
+
83
+ def detect_language(file_path)
84
+ case File.extname(file_path)
85
+ when ".rb" then "ruby"
86
+ when ".py" then "python"
87
+ when ".js" then "javascript"
88
+ when ".java" then "java"
89
+ when ".go" then "go"
90
+ else "unknown"
91
+ end
92
+ end
93
+
94
+ def analyze_metrics(content, language)
95
+ lines = content.lines
96
+
97
+ {
98
+ lines_of_code: lines.count,
99
+ blank_lines: lines.count(&:blank?),
100
+ comment_lines: count_comment_lines(lines, language),
101
+ average_line_length: lines.map(&:length).sum.to_f / lines.count
102
+ }
103
+ end
104
+
105
+ def check_complexity(content, language)
106
+ issues = []
107
+
108
+ if language == "ruby"
109
+ ast = Parser::CurrentRuby.parse(content)
110
+
111
+ # Check method complexity
112
+ processor = ComplexityProcessor.new
113
+ processor.process(ast)
114
+
115
+ processor.complex_methods.each do |method|
116
+ issues << {
117
+ type: "complexity",
118
+ severity: "warning",
119
+ line: method[:line],
120
+ message: "Method '#{method[:name]}' has cyclomatic complexity of #{method[:complexity]} (threshold: 10)"
121
+ }
122
+ end
123
+ end
124
+
125
+ issues
126
+ end
127
+
128
+ def format_analysis_results(results)
129
+ output = ["Code Analysis Results", "=" * 40, ""]
130
+
131
+ output << "File: #{results[:file]}"
132
+ output << "Language: #{results[:language]}"
133
+ output << ""
134
+ output << "Metrics:"
135
+ results[:metrics].each do |key, value|
136
+ output << " #{key.to_s.gsub('_', ' ').capitalize}: #{value.is_a?(Float) ? value.round(2) : value}"
137
+ end
138
+
139
+ if results[:issues].any?
140
+ output << ""
141
+ output << "Issues Found:"
142
+ results[:issues].group_by { |i| i[:type] }.each do |type, issues|
143
+ output << ""
144
+ output << " #{type.capitalize}:"
145
+ issues.each do |issue|
146
+ output << " Line #{issue[:line]}: [#{issue[:severity]}] #{issue[:message]}"
147
+ end
148
+ end
149
+ else
150
+ output << ""
151
+ output << "No issues found!"
152
+ end
153
+
154
+ output.join("\n")
155
+ end
156
+ end
157
+
158
+ # Code generation tool with sampling
159
+ class CodeGeneratorTool < Tsikol::Tool
160
+ name "generate_code"
161
+ description "Generate code based on specifications using AI"
162
+
163
+ # Enable sampling for this tool
164
+ sampling true
165
+
166
+ parameter :specification do
167
+ type :string
168
+ required
169
+ description "Description of what code to generate"
170
+ end
171
+
172
+ parameter :language do
173
+ type :string
174
+ required
175
+ enum ["ruby", "python", "javascript", "java", "go"]
176
+ description "Target programming language"
177
+ end
178
+
179
+ parameter :style do
180
+ type :string
181
+ optional
182
+ default "clean"
183
+ enum ["clean", "documented", "compact", "enterprise"]
184
+ description "Code style preference"
185
+ end
186
+
187
+ parameter :include_tests do
188
+ type :boolean
189
+ optional
190
+ default false
191
+ description "Include unit tests"
192
+ end
193
+
194
+ def execute(specification:, language:, style: "clean", include_tests: false)
195
+ # Use AI sampling to generate code
196
+ messages = build_generation_messages(specification, language, style, include_tests)
197
+
198
+ response = @server.sample_text(
199
+ messages: messages,
200
+ max_tokens: 2000,
201
+ temperature: 0.3 # Lower temperature for more consistent code
202
+ )
203
+
204
+ if response[:error]
205
+ raise "Code generation failed: #{response[:error]}"
206
+ end
207
+
208
+ generated_code = response[:text]
209
+
210
+ # Format and validate generated code
211
+ formatted_code = format_code(generated_code, language)
212
+
213
+ if include_tests
214
+ test_code = generate_tests(specification, formatted_code, language)
215
+ formatted_code += "\n\n" + test_code
216
+ end
217
+
218
+ formatted_code
219
+ end
220
+
221
+ private
222
+
223
+ def build_generation_messages(spec, language, style, include_tests)
224
+ system_prompt = build_system_prompt(language, style)
225
+
226
+ user_prompt = <<~PROMPT
227
+ Generate #{language} code for the following specification:
228
+
229
+ #{spec}
230
+
231
+ Requirements:
232
+ - Follow #{style} coding style
233
+ - Include error handling
234
+ - Add appropriate comments
235
+ #{include_tests ? "- Include comprehensive unit tests" : ""}
236
+ PROMPT
237
+
238
+ [
239
+ {
240
+ role: "system",
241
+ content: { type: "text", text: system_prompt }
242
+ },
243
+ {
244
+ role: "user",
245
+ content: { type: "text", text: user_prompt }
246
+ }
247
+ ]
248
+ end
249
+
250
+ def build_system_prompt(language, style)
251
+ base = "You are an expert #{language} developer. Generate high-quality, production-ready code."
252
+
253
+ style_guides = {
254
+ "clean" => "Follow clean code principles: clear naming, small functions, minimal complexity.",
255
+ "documented" => "Include comprehensive documentation: docstrings, inline comments, usage examples.",
256
+ "compact" => "Write concise code while maintaining readability. Avoid unnecessary verbosity.",
257
+ "enterprise" => "Follow enterprise patterns: interfaces, dependency injection, comprehensive error handling."
258
+ }
259
+
260
+ "#{base} #{style_guides[style]}"
261
+ end
262
+ end
263
+
264
+ # Code refactoring tool
265
+ class CodeRefactorTool < Tsikol::Tool
266
+ name "refactor_code"
267
+ description "Refactor code to improve quality and maintainability"
268
+
269
+ parameter :file_path do
270
+ type :string
271
+ required
272
+ description "Path to file to refactor"
273
+ end
274
+
275
+ parameter :refactoring_type do
276
+ type :string
277
+ required
278
+ enum ["extract_method", "rename", "simplify", "dry", "modernize"]
279
+ description "Type of refactoring to perform"
280
+ end
281
+
282
+ parameter :options do
283
+ type :object
284
+ optional
285
+ default {}
286
+ description "Refactoring-specific options"
287
+ end
288
+
289
+ def execute(file_path:, refactoring_type:, options: {})
290
+ unless File.exist?(file_path)
291
+ raise ArgumentError, "File not found: #{file_path}"
292
+ end
293
+
294
+ content = File.read(file_path)
295
+ language = detect_language(file_path)
296
+
297
+ refactored = case refactoring_type
298
+ when "extract_method"
299
+ extract_method(content, language, options)
300
+ when "rename"
301
+ rename_symbols(content, language, options)
302
+ when "simplify"
303
+ simplify_code(content, language)
304
+ when "dry"
305
+ remove_duplication(content, language)
306
+ when "modernize"
307
+ modernize_code(content, language)
308
+ end
309
+
310
+ # Show diff
311
+ format_refactoring_result(content, refactored, refactoring_type)
312
+ end
313
+
314
+ private
315
+
316
+ def extract_method(content, language, options)
317
+ start_line = options["start_line"] || raise("start_line required")
318
+ end_line = options["end_line"] || raise("end_line required")
319
+ method_name = options["method_name"] || "extracted_method"
320
+
321
+ lines = content.lines
322
+ extracted = lines[start_line-1..end_line-1]
323
+
324
+ # Simple extraction (real implementation would analyze variables)
325
+ new_method = "\ndef #{method_name}\n#{extracted.map { |l| " #{l}" }.join}\nend\n"
326
+
327
+ # Replace with method call
328
+ lines[start_line-1..end_line-1] = ["#{method_name}\n"]
329
+
330
+ lines.join + new_method
331
+ end
332
+
333
+ def format_refactoring_result(original, refactored, type)
334
+ <<~RESULT
335
+ Refactoring: #{type}
336
+ #{"=" * 40}
337
+
338
+ Original:
339
+ #{original}
340
+
341
+ #{"=" * 40}
342
+
343
+ Refactored:
344
+ #{refactored}
345
+
346
+ #{"=" * 40}
347
+
348
+ Summary:
349
+ - Lines changed: #{count_changed_lines(original, refactored)}
350
+ - Size difference: #{refactored.length - original.length} chars
351
+ RESULT
352
+ end
353
+ end
354
+
355
+ # Git integration resource
356
+ class GitResource < Tsikol::Resource
357
+ uri "git/:command/:path?"
358
+ description "Git repository information and operations"
359
+
360
+ def read
361
+ command = params[:command]
362
+ path = params[:path] || "."
363
+
364
+ repo = Rugged::Repository.new(path)
365
+
366
+ case command
367
+ when "status"
368
+ git_status(repo)
369
+ when "log"
370
+ git_log(repo)
371
+ when "branches"
372
+ git_branches(repo)
373
+ when "diff"
374
+ git_diff(repo)
375
+ else
376
+ { error: "Unknown git command: #{command}" }.to_json
377
+ end
378
+ rescue Rugged::RepositoryError => e
379
+ { error: "Git error: #{e.message}" }.to_json
380
+ end
381
+
382
+ private
383
+
384
+ def git_status(repo)
385
+ status = {
386
+ branch: repo.head.name.sub('refs/heads/', ''),
387
+ ahead: 0, # Would need to compare with upstream
388
+ behind: 0,
389
+ modified: [],
390
+ untracked: [],
391
+ staged: []
392
+ }
393
+
394
+ repo.status_file_each do |file, status_flags|
395
+ if status_flags.include?(:worktree_modified)
396
+ status[:modified] << file
397
+ elsif status_flags.include?(:worktree_new)
398
+ status[:untracked] << file
399
+ elsif status_flags.include?(:index_modified) || status_flags.include?(:index_new)
400
+ status[:staged] << file
401
+ end
402
+ end
403
+
404
+ status.to_json
405
+ end
406
+
407
+ def git_log(repo, limit = 20)
408
+ walker = Rugged::Walker.new(repo)
409
+ walker.push(repo.head.target_id)
410
+
411
+ commits = walker.take(limit).map do |commit|
412
+ {
413
+ sha: commit.oid[0..7],
414
+ message: commit.message.lines.first.strip,
415
+ author: commit.author[:name],
416
+ date: commit.author[:time].iso8601
417
+ }
418
+ end
419
+
420
+ { commits: commits }.to_json
421
+ end
422
+ end
423
+
424
+ # Code completion provider
425
+ class CodeCompletionResource < Tsikol::Resource
426
+ uri "completion/:language/:context"
427
+ description "Provide code completion suggestions"
428
+
429
+ # Enable completion capability
430
+ completion true
431
+
432
+ def complete
433
+ language = params[:language]
434
+ context = params[:context]
435
+ partial = params[:partial] || ""
436
+
437
+ suggestions = case language
438
+ when "ruby"
439
+ ruby_completions(context, partial)
440
+ when "python"
441
+ python_completions(context, partial)
442
+ else
443
+ []
444
+ end
445
+
446
+ {
447
+ completion: "completion",
448
+ values: suggestions.map { |s| { value: s[:text], label: s[:label] } }
449
+ }
450
+ end
451
+
452
+ private
453
+
454
+ def ruby_completions(context, partial)
455
+ case context
456
+ when "method"
457
+ %w[def private protected public attr_reader attr_writer attr_accessor].select { |m| m.start_with?(partial) }
458
+ .map { |m| { text: m, label: "#{m} (keyword)" } }
459
+ when "class"
460
+ %w[class module include extend prepend].select { |k| k.start_with?(partial) }
461
+ .map { |k| { text: k, label: "#{k} (keyword)" } }
462
+ else
463
+ []
464
+ end
465
+ end
466
+ end
467
+
468
+ # Code assistant prompt
469
+ class CodeAssistantPrompt < Tsikol::Prompt
470
+ name "code_assistant"
471
+ description "Expert programming assistant with deep knowledge of multiple languages"
472
+
473
+ argument :expertise do
474
+ type :string
475
+ required
476
+ enum ["general", "web", "systems", "data_science", "mobile", "devops"]
477
+ description "Area of expertise to focus on"
478
+ end
479
+
480
+ argument :languages do
481
+ type :array
482
+ optional
483
+ default ["ruby", "python", "javascript"]
484
+ description "Preferred programming languages"
485
+ end
486
+
487
+ argument :level do
488
+ type :string
489
+ optional
490
+ default "intermediate"
491
+ enum ["beginner", "intermediate", "advanced", "expert"]
492
+ description "User's skill level"
493
+ end
494
+
495
+ def get_messages(expertise:, languages: ["ruby", "python", "javascript"], level: "intermediate")
496
+ system = build_system_message(expertise, languages, level)
497
+
498
+ [
499
+ {
500
+ role: "system",
501
+ content: { type: "text", text: system }
502
+ },
503
+ {
504
+ role: "user",
505
+ content: { type: "text", text: "Initialize code assistant" }
506
+ },
507
+ {
508
+ role: "assistant",
509
+ content: {
510
+ type: "text",
511
+ text: "I'm ready to help with your #{expertise} programming needs. I specialize in #{languages.join(', ')} and will adjust my explanations for #{level} level understanding. I can analyze code, generate solutions, suggest refactorings, and help with debugging. What would you like to work on?"
512
+ }
513
+ }
514
+ ]
515
+ end
516
+
517
+ private
518
+
519
+ def build_system_message(expertise, languages, level)
520
+ base = <<~SYSTEM
521
+ You are an expert programming assistant specializing in #{expertise} development.
522
+
523
+ Primary languages: #{languages.join(', ')}
524
+ User level: #{level}
525
+
526
+ Available tools:
527
+ - analyze_code: Analyze code quality and find issues
528
+ - generate_code: Generate new code from specifications
529
+ - refactor_code: Improve existing code
530
+
531
+ Key capabilities:
532
+ 1. Write clean, efficient, and maintainable code
533
+ 2. Explain complex concepts clearly
534
+ 3. Debug issues systematically
535
+ 4. Suggest best practices and patterns
536
+ 5. Provide performance optimization advice
537
+
538
+ Guidelines:
539
+ - Adjust explanations to #{level} level
540
+ - Always consider security implications
541
+ - Prefer readability over cleverness
542
+ - Include error handling in all code
543
+ - Follow language-specific conventions
544
+ SYSTEM
545
+
546
+ base + expertise_specific_guidance(expertise)
547
+ end
548
+
549
+ def expertise_specific_guidance(expertise)
550
+ case expertise
551
+ when "web"
552
+ "\nWeb Development Focus:\n- Modern frameworks and libraries\n- RESTful API design\n- Frontend/backend integration\n- Security (XSS, CSRF, etc.)\n- Performance optimization"
553
+ when "systems"
554
+ "\nSystems Programming Focus:\n- Memory management\n- Concurrency and parallelism\n- Low-level optimization\n- Operating system interfaces\n- Network programming"
555
+ when "data_science"
556
+ "\nData Science Focus:\n- Data manipulation and analysis\n- Machine learning algorithms\n- Statistical methods\n- Visualization techniques\n- Big data processing"
557
+ else
558
+ ""
559
+ end
560
+ end
561
+ end
562
+
563
+ # Syntax highlighting resource
564
+ class SyntaxHighlightResource < Tsikol::Resource
565
+ uri "highlight/:file_path"
566
+ description "Syntax highlighted code"
567
+
568
+ def read
569
+ file_path = params[:file_path]
570
+ theme = params[:theme] || "monokai"
571
+
572
+ unless File.exist?(file_path)
573
+ return { error: "File not found" }.to_json
574
+ end
575
+
576
+ content = File.read(file_path)
577
+ language = detect_language(file_path)
578
+
579
+ lexer = Rouge::Lexer.find(language) || Rouge::Lexers::PlainText
580
+ formatter = Rouge::Formatters::HTML.new
581
+
582
+ highlighted = formatter.format(lexer.lex(content))
583
+
584
+ {
585
+ file: file_path,
586
+ language: language,
587
+ theme: theme,
588
+ html: highlighted,
589
+ css: Rouge::Themes::Monokai.render(scope: '.highlight')
590
+ }.to_json
591
+ end
592
+
593
+ private
594
+
595
+ def detect_language(file_path)
596
+ Rouge::Lexer.guess_by_filename(file_path)&.tag || "plaintext"
597
+ end
598
+ end
599
+
600
+ # Complexity analysis processor
601
+ class ComplexityProcessor < Parser::AST::Processor
602
+ attr_reader :complex_methods
603
+
604
+ def initialize
605
+ @complex_methods = []
606
+ @complexity_stack = []
607
+ end
608
+
609
+ def on_def(node)
610
+ method_name = node.children[0]
611
+ @complexity_stack.push({ name: method_name, complexity: 1, line: node.location.line })
612
+ super
613
+
614
+ method_info = @complexity_stack.pop
615
+ if method_info[:complexity] > 10
616
+ @complex_methods << method_info
617
+ end
618
+ end
619
+
620
+ def on_if(node)
621
+ @complexity_stack.last[:complexity] += 1 if @complexity_stack.any?
622
+ super
623
+ end
624
+
625
+ alias on_case on_if
626
+ alias on_when on_if
627
+ alias on_while on_if
628
+ alias on_until on_if
629
+ alias on_for on_if
630
+ end
631
+
632
+ # Start the server
633
+ Tsikol.start(
634
+ name: "code-assistant",
635
+ version: "1.0.0",
636
+ description: "Intelligent code analysis and generation assistant"
637
+ ) do
638
+ # Enable all capabilities
639
+ logging true
640
+ prompts true
641
+ sampling true
642
+ completion true
643
+
644
+ # Middleware
645
+ use Tsikol::LoggingMiddleware, level: :info
646
+ use Tsikol::AuthenticationMiddleware,
647
+ type: :bearer,
648
+ validator: ->(token) { validate_api_token(token) }
649
+
650
+ # Tools
651
+ tool CodeAnalyzerTool
652
+ tool CodeGeneratorTool
653
+ tool CodeRefactorTool
654
+
655
+ # Resources
656
+ resource GitResource
657
+ resource CodeCompletionResource
658
+ resource SyntaxHighlightResource
659
+
660
+ # Prompts
661
+ prompt CodeAssistantPrompt
662
+
663
+ # Helper methods
664
+ def validate_api_token(token)
665
+ # In production, validate against database
666
+ return { user_id: "demo", tier: "premium" } if token == "demo-token"
667
+ nil
668
+ end
669
+ end
670
+ ```
671
+
672
+ ### Testing
673
+
674
+ ```ruby
675
+ require 'minitest/autorun'
676
+ require 'tsikol/test_helpers'
677
+
678
+ class CodeAssistantTest < Minitest::Test
679
+ include Tsikol::TestHelpers
680
+
681
+ def setup
682
+ @server = create_test_server
683
+ @client = TestClient.new(@server)
684
+
685
+ # Create test files
686
+ setup_test_files
687
+ end
688
+
689
+ def teardown
690
+ cleanup_test_files
691
+ end
692
+
693
+ def test_code_analysis
694
+ response = @client.call_tool("analyze_code", {
695
+ "file_path" => "test_code.rb",
696
+ "checks" => ["complexity", "style"]
697
+ })
698
+
699
+ assert_successful_response(response)
700
+ result = response.dig(:result, :content, 0, :text)
701
+ assert_match /Code Analysis Results/, result
702
+ assert_match /Lines of code:/, result
703
+ end
704
+
705
+ def test_code_generation
706
+ response = @client.call_tool("generate_code", {
707
+ "specification" => "Create a function to calculate fibonacci numbers",
708
+ "language" => "ruby",
709
+ "style" => "documented"
710
+ })
711
+
712
+ assert_successful_response(response)
713
+ code = response.dig(:result, :content, 0, :text)
714
+ assert_match /def fibonacci/, code
715
+ assert_match /#/, code # Should have comments
716
+ end
717
+
718
+ def test_code_refactoring
719
+ response = @client.call_tool("refactor_code", {
720
+ "file_path" => "test_code.rb",
721
+ "refactoring_type" => "extract_method",
722
+ "options" => {
723
+ "start_line" => 3,
724
+ "end_line" => 5,
725
+ "method_name" => "calculate_sum"
726
+ }
727
+ })
728
+
729
+ assert_successful_response(response)
730
+ result = response.dig(:result, :content, 0, :text)
731
+ assert_match /def calculate_sum/, result
732
+ end
733
+
734
+ def test_git_integration
735
+ response = @client.read_resource("git/status")
736
+
737
+ assert_successful_response(response)
738
+ status = JSON.parse(response.dig(:result, :contents, 0, :text))
739
+ assert status.key?("branch")
740
+ end
741
+
742
+ def test_code_completion
743
+ response = @client.complete_resource("completion/ruby/method", {
744
+ "partial" => "att"
745
+ })
746
+
747
+ assert_successful_response(response)
748
+ completions = response.dig(:result, :completion, :values)
749
+ assert completions.any? { |c| c[:value] == "attr_reader" }
750
+ end
751
+
752
+ private
753
+
754
+ def setup_test_files
755
+ File.write("test_code.rb", <<~RUBY)
756
+ class Calculator
757
+ def complex_method(a, b, c)
758
+ if a > 0
759
+ if b > 0
760
+ if c > 0
761
+ a + b + c
762
+ else
763
+ a + b
764
+ end
765
+ else
766
+ a
767
+ end
768
+ else
769
+ 0
770
+ end
771
+ end
772
+ end
773
+ RUBY
774
+
775
+ # Initialize git repo
776
+ `git init . 2>/dev/null`
777
+ end
778
+
779
+ def cleanup_test_files
780
+ File.delete("test_code.rb") if File.exist?("test_code.rb")
781
+ FileUtils.rm_rf(".git")
782
+ end
783
+
784
+ def create_test_server
785
+ # Mock server for testing
786
+ Tsikol::Server.new(name: "test-code-assistant") do
787
+ sampling false # Disable sampling for tests
788
+
789
+ tool CodeAnalyzerTool
790
+ tool CodeGeneratorTool
791
+ tool CodeRefactorTool
792
+ resource GitResource
793
+ resource CodeCompletionResource
794
+ end
795
+ end
796
+ end
797
+ ```
798
+
799
+ ## Advanced Features
800
+
801
+ ### IDE Integration
802
+
803
+ ```ruby
804
+ # IDE server extension
805
+ class IDEServer < Tsikol::Server
806
+ def initialize
807
+ super(name: "code-assistant-ide")
808
+
809
+ # Language Server Protocol support
810
+ enable_lsp
811
+
812
+ # Real-time diagnostics
813
+ enable_diagnostics
814
+
815
+ # Workspace indexing
816
+ @workspace_index = WorkspaceIndex.new
817
+ end
818
+
819
+ def on_file_change(file_path)
820
+ # Re-analyze on file change
821
+ analyze_file_async(file_path)
822
+
823
+ # Update diagnostics
824
+ publish_diagnostics(file_path)
825
+ end
826
+
827
+ def on_completion_request(file_path, position)
828
+ # Context-aware completion
829
+ context = analyze_context(file_path, position)
830
+ provide_completions(context)
831
+ end
832
+ end
833
+ ```
834
+
835
+ ### AI-Powered Features
836
+
837
+ ```ruby
838
+ # Advanced AI code review
839
+ class AICodeReviewTool < Tsikol::Tool
840
+ name "ai_code_review"
841
+ description "Comprehensive AI-powered code review"
842
+
843
+ sampling true
844
+
845
+ def execute(file_path:, focus_areas: [])
846
+ content = File.read(file_path)
847
+
848
+ # Get AI review
849
+ review = get_ai_review(content, focus_areas)
850
+
851
+ # Parse and structure results
852
+ structure_review_results(review)
853
+ end
854
+
855
+ private
856
+
857
+ def get_ai_review(content, focus_areas)
858
+ messages = [
859
+ {
860
+ role: "system",
861
+ content: {
862
+ type: "text",
863
+ text: "You are an expert code reviewer. Review the following code for: #{focus_areas.join(', ')}"
864
+ }
865
+ },
866
+ {
867
+ role: "user",
868
+ content: {
869
+ type: "text",
870
+ text: "Review this code:\n\n#{content}"
871
+ }
872
+ }
873
+ ]
874
+
875
+ response = @server.sample_text(
876
+ messages: messages,
877
+ max_tokens: 2000,
878
+ temperature: 0.2
879
+ )
880
+
881
+ response[:text]
882
+ end
883
+ end
884
+ ```
885
+
886
+ ## Best Practices Demonstrated
887
+
888
+ 1. **Modular Architecture** - Separate tools for different functions
889
+ 2. **AI Integration** - Smart use of sampling for code generation
890
+ 3. **Language Agnostic** - Support for multiple programming languages
891
+ 4. **Git Integration** - Version control awareness
892
+ 5. **Real-time Features** - Code completion and diagnostics
893
+ 6. **Security** - Authentication and input validation
894
+ 7. **Performance** - Efficient code analysis with AST parsing
895
+ 8. **Testing** - Comprehensive test coverage
896
+ 9. **Error Handling** - Graceful degradation
897
+ 10. **Documentation** - Self-documenting code with examples
898
+
899
+ ## Performance Considerations
900
+
901
+ 1. **AST Caching** - Cache parsed ASTs for repeated analysis
902
+ 2. **Incremental Analysis** - Only re-analyze changed parts
903
+ 3. **Background Processing** - Async analysis for large files
904
+ 4. **Index Optimization** - Pre-index workspace for fast search
905
+ 5. **Streaming Results** - Stream large analysis results
906
+
907
+ ## Security Notes
908
+
909
+ 1. **Sandboxed Execution** - Never execute untrusted code
910
+ 2. **Path Validation** - Prevent directory traversal
911
+ 3. **Rate Limiting** - Prevent DoS on expensive operations
912
+ 4. **Authentication** - Require auth for sensitive operations
913
+ 5. **Input Sanitization** - Clean all file paths and content
914
+
915
+ ## Next Steps
916
+
917
+ - Add support for more languages
918
+ - Implement debugging capabilities
919
+ - Add code metrics dashboard
920
+ - Create VS Code extension
921
+ - Add collaborative features
922
+ - Implement code search across repositories