archsight 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 (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +24 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +186 -0
  5. data/Dockerfile +39 -0
  6. data/LICENSE.txt +201 -0
  7. data/README.md +170 -0
  8. data/SECURITY.md +27 -0
  9. data/exe/archsight +9 -0
  10. data/lib/archsight/annotations/aggregators.rb +109 -0
  11. data/lib/archsight/annotations/annotation.rb +168 -0
  12. data/lib/archsight/annotations/architecture_annotations.rb +59 -0
  13. data/lib/archsight/annotations/backup_annotations.rb +21 -0
  14. data/lib/archsight/annotations/computed.rb +264 -0
  15. data/lib/archsight/annotations/email_recipient.rb +35 -0
  16. data/lib/archsight/annotations/generated_annotations.rb +17 -0
  17. data/lib/archsight/annotations/git_annotations.rb +21 -0
  18. data/lib/archsight/annotations/relation_resolver.rb +160 -0
  19. data/lib/archsight/cli.rb +120 -0
  20. data/lib/archsight/configuration.rb +36 -0
  21. data/lib/archsight/database.rb +183 -0
  22. data/lib/archsight/documentation.rb +171 -0
  23. data/lib/archsight/graph.rb +113 -0
  24. data/lib/archsight/helpers.rb +210 -0
  25. data/lib/archsight/linter.rb +77 -0
  26. data/lib/archsight/mcp/analyze_resource_tool.rb +222 -0
  27. data/lib/archsight/mcp/base.rb +48 -0
  28. data/lib/archsight/mcp/query_tool.rb +113 -0
  29. data/lib/archsight/mcp/resource_doc_tool.rb +87 -0
  30. data/lib/archsight/mcp.rb +6 -0
  31. data/lib/archsight/query/ast.rb +279 -0
  32. data/lib/archsight/query/errors.rb +39 -0
  33. data/lib/archsight/query/evaluator.rb +707 -0
  34. data/lib/archsight/query/lexer.rb +289 -0
  35. data/lib/archsight/query/parser.rb +506 -0
  36. data/lib/archsight/query.rb +68 -0
  37. data/lib/archsight/renderer.rb +134 -0
  38. data/lib/archsight/resources/application_component.rb +346 -0
  39. data/lib/archsight/resources/application_interface.rb +54 -0
  40. data/lib/archsight/resources/application_service.rb +222 -0
  41. data/lib/archsight/resources/base.rb +300 -0
  42. data/lib/archsight/resources/business_actor.rb +195 -0
  43. data/lib/archsight/resources/business_constraint.rb +32 -0
  44. data/lib/archsight/resources/business_process.rb +37 -0
  45. data/lib/archsight/resources/business_product.rb +206 -0
  46. data/lib/archsight/resources/business_requirement.rb +56 -0
  47. data/lib/archsight/resources/compliance_evidence.rb +42 -0
  48. data/lib/archsight/resources/data_object.rb +49 -0
  49. data/lib/archsight/resources/motivation_goal.rb +37 -0
  50. data/lib/archsight/resources/motivation_outcome.rb +33 -0
  51. data/lib/archsight/resources/motivation_stakeholder.rb +38 -0
  52. data/lib/archsight/resources/strategy_capability.rb +38 -0
  53. data/lib/archsight/resources/technology_artifact.rb +154 -0
  54. data/lib/archsight/resources/technology_interface.rb +34 -0
  55. data/lib/archsight/resources/technology_node.rb +42 -0
  56. data/lib/archsight/resources/technology_service.rb +35 -0
  57. data/lib/archsight/resources/technology_system_software.rb +37 -0
  58. data/lib/archsight/resources/view.rb +51 -0
  59. data/lib/archsight/resources.rb +49 -0
  60. data/lib/archsight/template.rb +49 -0
  61. data/lib/archsight/version.rb +5 -0
  62. data/lib/archsight/web/application.rb +290 -0
  63. data/lib/archsight/web/doc/archimate.md +215 -0
  64. data/lib/archsight/web/doc/computed_annotations.md +316 -0
  65. data/lib/archsight/web/doc/icons.md +303 -0
  66. data/lib/archsight/web/doc/index.md.erb +74 -0
  67. data/lib/archsight/web/doc/modeling.md +200 -0
  68. data/lib/archsight/web/doc/search.md +227 -0
  69. data/lib/archsight/web/doc/togaf.md +255 -0
  70. data/lib/archsight/web/doc/tool.md +90 -0
  71. data/lib/archsight/web/public/css/artifact.css +985 -0
  72. data/lib/archsight/web/public/css/base.css +201 -0
  73. data/lib/archsight/web/public/css/graph.css +106 -0
  74. data/lib/archsight/web/public/css/highlight.min.css +10 -0
  75. data/lib/archsight/web/public/css/iconoir.css +22 -0
  76. data/lib/archsight/web/public/css/instance.css +329 -0
  77. data/lib/archsight/web/public/css/layout.css +421 -0
  78. data/lib/archsight/web/public/css/mermaid-layers.css +188 -0
  79. data/lib/archsight/web/public/css/pico.min.css +4 -0
  80. data/lib/archsight/web/public/favicon.ico +0 -0
  81. data/lib/archsight/web/public/img/archimate.png +0 -0
  82. data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
  83. data/lib/archsight/web/public/js/graph-zoom.js +18 -0
  84. data/lib/archsight/web/public/js/highlight.min.js +3899 -0
  85. data/lib/archsight/web/public/js/htmx.min.js +1 -0
  86. data/lib/archsight/web/public/js/mermaid-init.js +88 -0
  87. data/lib/archsight/web/public/js/mermaid.min.js +2811 -0
  88. data/lib/archsight/web/public/js/sparkline.js +42 -0
  89. data/lib/archsight/web/public/js/svg-pan-zoom.min.js +3 -0
  90. data/lib/archsight/web/public/js/svg-zoom-controls.js +93 -0
  91. data/lib/archsight/web/views/index.haml +12 -0
  92. data/lib/archsight/web/views/partials/artifact/_activity.haml +55 -0
  93. data/lib/archsight/web/views/partials/artifact/_agentic.haml +25 -0
  94. data/lib/archsight/web/views/partials/artifact/_deployment.haml +29 -0
  95. data/lib/archsight/web/views/partials/artifact/_git_info.haml +16 -0
  96. data/lib/archsight/web/views/partials/artifact/_language_stats.haml +53 -0
  97. data/lib/archsight/web/views/partials/artifact/_links.haml +24 -0
  98. data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +26 -0
  99. data/lib/archsight/web/views/partials/artifact/_repositories.haml +55 -0
  100. data/lib/archsight/web/views/partials/artifact/_team.haml +83 -0
  101. data/lib/archsight/web/views/partials/artifact/_workflow.haml +69 -0
  102. data/lib/archsight/web/views/partials/components/_activity.haml +37 -0
  103. data/lib/archsight/web/views/partials/components/_git.haml +17 -0
  104. data/lib/archsight/web/views/partials/components/_jira.haml +18 -0
  105. data/lib/archsight/web/views/partials/components/_languages.haml +29 -0
  106. data/lib/archsight/web/views/partials/components/_owner.haml +15 -0
  107. data/lib/archsight/web/views/partials/components/_repositories.haml +37 -0
  108. data/lib/archsight/web/views/partials/components/_status.haml +23 -0
  109. data/lib/archsight/web/views/partials/instance/_detail.haml +99 -0
  110. data/lib/archsight/web/views/partials/instance/_graph.haml +6 -0
  111. data/lib/archsight/web/views/partials/instance/_list.haml +84 -0
  112. data/lib/archsight/web/views/partials/instance/_relations.haml +43 -0
  113. data/lib/archsight/web/views/partials/instance/_requirements.haml +41 -0
  114. data/lib/archsight/web/views/partials/instance/_view_detail.haml +57 -0
  115. data/lib/archsight/web/views/partials/layout/_content.haml +40 -0
  116. data/lib/archsight/web/views/partials/layout/_error.haml +22 -0
  117. data/lib/archsight/web/views/partials/layout/_head.haml +24 -0
  118. data/lib/archsight/web/views/partials/layout/_navigation.haml +20 -0
  119. data/lib/archsight/web/views/partials/layout/_sidebar.haml +27 -0
  120. data/lib/archsight/web/views/search.haml +53 -0
  121. data/lib/archsight.rb +17 -0
  122. metadata +311 -0
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "errors"
4
+
5
+ class Archsight::Query::Lexer
6
+ class Token
7
+ attr_reader :type, :value, :position
8
+
9
+ def initialize(type, value, position)
10
+ @type = type
11
+ @value = value
12
+ @position = position
13
+ end
14
+
15
+ def to_s
16
+ "Token(#{@type}, #{@value.inspect}, pos=#{@position})"
17
+ end
18
+ end
19
+
20
+ # Keywords are case-insensitive
21
+ KEYWORDS = {
22
+ "and" => :AND,
23
+ "or" => :OR,
24
+ "not" => :NOT,
25
+ "kind" => :KIND,
26
+ "name" => :NAME,
27
+ "none" => :NONE,
28
+ "in" => :IN
29
+ }.freeze
30
+
31
+ # Two-character operators (must be checked before single-character)
32
+ TWO_CHAR_OPERATORS = {
33
+ "==" => :EQ,
34
+ "!=" => :NEQ,
35
+ "=~" => :MATCH,
36
+ ">=" => :GTE,
37
+ "<=" => :LTE,
38
+ "->" => :OUTGOING_DIRECT,
39
+ "~>" => :OUTGOING_TRANSITIVE,
40
+ "<-" => :INCOMING_DIRECT,
41
+ "<~" => :INCOMING_TRANSITIVE
42
+ }.freeze
43
+
44
+ # Single-character operators
45
+ SINGLE_CHAR_OPERATORS = {
46
+ ">" => :GT,
47
+ "<" => :LT,
48
+ "&" => :AND,
49
+ "|" => :OR,
50
+ "!" => :NOT
51
+ }.freeze
52
+
53
+ def initialize(input)
54
+ @input = input
55
+ @position = 0
56
+ @tokens = []
57
+ end
58
+
59
+ def tokenize
60
+ @tokens = []
61
+ while @position < @input.length
62
+ skip_whitespace
63
+ break if @position >= @input.length
64
+
65
+ token = scan_token
66
+ @tokens << token if token
67
+ end
68
+ @tokens << Token.new(:EOF, nil, @position)
69
+ @tokens
70
+ end
71
+
72
+ private
73
+
74
+ def skip_whitespace
75
+ @position += 1 while @position < @input.length && @input[@position] =~ /\s/
76
+ end
77
+
78
+ def scan_token
79
+ char = @input[@position]
80
+ start_pos = @position
81
+
82
+ case char
83
+ when "("
84
+ @position += 1
85
+ Token.new(:LPAREN, "(", start_pos)
86
+ when ")"
87
+ @position += 1
88
+ Token.new(:RPAREN, ")", start_pos)
89
+ when ":"
90
+ @position += 1
91
+ Token.new(:COLON, ":", start_pos)
92
+ when "$"
93
+ @position += 1
94
+ Token.new(:DOLLAR, "$", start_pos)
95
+ when "?"
96
+ @position += 1
97
+ Token.new(:QUESTION, "?", start_pos)
98
+ when ","
99
+ @position += 1
100
+ Token.new(:COMMA, ",", start_pos)
101
+ when "{"
102
+ @position += 1
103
+ Token.new(:LBRACE, "{", start_pos)
104
+ when "}"
105
+ @position += 1
106
+ Token.new(:RBRACE, "}", start_pos)
107
+ when '"'
108
+ scan_string
109
+ when "'"
110
+ scan_single_quoted_string
111
+ when "/"
112
+ scan_regex
113
+ when "-", "~", "=", "!", ">", "<", "&", "|"
114
+ scan_operator
115
+ else
116
+ if char =~ /[a-zA-Z_]/
117
+ scan_identifier
118
+ elsif char =~ /\d/
119
+ scan_number
120
+ else
121
+ raise Archsight::Query::LexerError.new("Unexpected character '#{char}'", position: @position, source: @input)
122
+ end
123
+ end
124
+ end
125
+
126
+ def scan_string
127
+ start_pos = @position
128
+ @position += 1 # skip opening quote
129
+ value = ""
130
+
131
+ while @position < @input.length && @input[@position] != '"'
132
+ if @input[@position] == "\\"
133
+ @position += 1
134
+ value += @input[@position] if @position < @input.length
135
+ else
136
+ value += @input[@position]
137
+ end
138
+ @position += 1
139
+ end
140
+
141
+ raise Archsight::Query::LexerError.new("Unterminated string", position: start_pos, source: @input) if @position >= @input.length
142
+
143
+ @position += 1 # skip closing quote
144
+ Token.new(:STRING, value, start_pos)
145
+ end
146
+
147
+ def scan_single_quoted_string
148
+ start_pos = @position
149
+ @position += 1 # skip opening quote
150
+ value = ""
151
+
152
+ while @position < @input.length && @input[@position] != "'"
153
+ value += @input[@position]
154
+ @position += 1
155
+ end
156
+
157
+ if @position >= @input.length
158
+ raise Archsight::Query::LexerError.new("Unterminated single-quoted string", position: start_pos,
159
+ source: @input)
160
+ end
161
+
162
+ @position += 1 # skip closing quote
163
+ Token.new(:STRING, value, start_pos)
164
+ end
165
+
166
+ def scan_regex
167
+ start_pos = @position
168
+ @position += 1 # skip opening /
169
+ pattern = ""
170
+
171
+ while @position < @input.length && @input[@position] != "/"
172
+ pattern += @input[@position]
173
+ if @input[@position] == "\\"
174
+ @position += 1
175
+ pattern += @input[@position] if @position < @input.length
176
+ end
177
+ @position += 1
178
+ end
179
+
180
+ raise Archsight::Query::LexerError.new("Unterminated regex", position: start_pos, source: @input) if @position >= @input.length
181
+
182
+ @position += 1 # skip closing /
183
+
184
+ # Check for flags
185
+ flags = ""
186
+ while @position < @input.length && @input[@position] =~ /[imx]/
187
+ flags += @input[@position]
188
+ @position += 1
189
+ end
190
+
191
+ Token.new(:REGEX, { pattern: pattern, flags: flags }, start_pos)
192
+ end
193
+
194
+ def scan_operator
195
+ start_pos = @position
196
+ one_char = @input[@position]
197
+ next_char = @position + 1 < @input.length ? @input[@position + 1] : nil
198
+
199
+ # Check for verb filter syntax: -{, ~{, <{ should NOT be consumed as two-char operators
200
+ # Instead, emit single char tokens so parser can handle verb filters
201
+ if next_char == "{"
202
+ case one_char
203
+ when "-"
204
+ @position += 1
205
+ return Token.new(:DASH, "-", start_pos)
206
+ when "~"
207
+ @position += 1
208
+ return Token.new(:TILDE, "~", start_pos)
209
+ when "<"
210
+ @position += 1
211
+ return Token.new(:LT, "<", start_pos)
212
+ end
213
+ end
214
+
215
+ # Try two-character operators
216
+ if @position + 1 < @input.length
217
+ two_char = @input[@position, 2]
218
+ if TWO_CHAR_OPERATORS[two_char]
219
+ @position += 2
220
+ return Token.new(TWO_CHAR_OPERATORS[two_char], two_char, start_pos)
221
+ end
222
+ end
223
+
224
+ # Single character operators
225
+ if SINGLE_CHAR_OPERATORS[one_char]
226
+ @position += 1
227
+ return Token.new(SINGLE_CHAR_OPERATORS[one_char], one_char, start_pos)
228
+ end
229
+
230
+ # Handle standalone - and ~ (for verb filter closing: }- and }~)
231
+ if one_char == "-"
232
+ @position += 1
233
+ return Token.new(:DASH, "-", start_pos)
234
+ elsif one_char == "~"
235
+ @position += 1
236
+ return Token.new(:TILDE, "~", start_pos)
237
+ end
238
+
239
+ raise Archsight::Query::LexerError.new("Unknown operator '#{one_char}'", position: @position, source: @input)
240
+ end
241
+
242
+ def scan_identifier
243
+ start_pos = @position
244
+ value = ""
245
+
246
+ # Allow letters, digits, underscores, hyphens, slashes, and dots in identifiers
247
+ # This supports annotation paths like "activity/status" and "scc/language/Go/loc"
248
+ # and resource names like "teleport.e"
249
+ while @position < @input.length && @input[@position] =~ %r{[a-zA-Z0-9_\-/.]}
250
+ value += @input[@position]
251
+ @position += 1
252
+ end
253
+
254
+ # Check if it's a keyword (case-insensitive)
255
+ lower = value.downcase
256
+ return Token.new(KEYWORDS[lower], value, start_pos) if KEYWORDS.key?(lower)
257
+
258
+ Token.new(:IDENTIFIER, value, start_pos)
259
+ end
260
+
261
+ def scan_number
262
+ start_pos = @position
263
+ value = ""
264
+
265
+ # Optional negative sign (handled separately as operator, but support here too)
266
+ if @input[@position] == "-"
267
+ value += "-"
268
+ @position += 1
269
+ end
270
+
271
+ # Integer part
272
+ while @position < @input.length && @input[@position] =~ /\d/
273
+ value += @input[@position]
274
+ @position += 1
275
+ end
276
+
277
+ # Decimal part
278
+ if @position < @input.length && @input[@position] == "."
279
+ value += "."
280
+ @position += 1
281
+ while @position < @input.length && @input[@position] =~ /\d/
282
+ value += @input[@position]
283
+ @position += 1
284
+ end
285
+ end
286
+
287
+ Token.new(:NUMBER, value.to_f, start_pos)
288
+ end
289
+ end