docit 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ac8acf7fd2b736bb2c89c63a63bca482239783457d7572d864e0257a3a2c6cd
4
- data.tar.gz: 1605b07b832dc73f4e7f00e0840aa72267cbb60262e17baba6842134ee576e19
3
+ metadata.gz: 85a7407aab5a5fea2aa0c679d469360f124884ebeac2e4479265cbcdd818210d
4
+ data.tar.gz: c946eea9c5a664931630b00726f33b8e5bb296c1493e799c67cd905f330cba1d
5
5
  SHA512:
6
- metadata.gz: a72fa34e950fdf918daab680c287311401a4059fa3b8d0bf16a0ebcef210ed607b697d3ad58a3343bf7d105a2a11d717efd0f8ee239464a82f58ca5f934b1e79
7
- data.tar.gz: 96516820d968faae672a4caa7b6c52c9d79356b6366d09edaa019badb2c6949c0b4087115e6f306f559904b5dbcb02b594c2e0e58e864458c0c460e381d4d894
6
+ metadata.gz: 3dd0a62c6d0f138916839a7bcc445f98921abaf9e0ef477473e4dddba67f2033fd29ffb51a4803597d98f0807ee24c53d6b5a90a8d144d87c54669ee51155fef
7
+ data.tar.gz: d953ceef06cd91034fa79503a07da386dcefc7cdddabcb3567d1638d6546a2c484bdebd82f263d732a94b89447a9e9f6b659d7008c322d2e238e002aca286a1b
@@ -11,7 +11,7 @@ A clear and concise description of the bug.
11
11
  ## Steps to Reproduce
12
12
 
13
13
  1. Configure Docit with `...`
14
- 2. Add `swagger_doc` to `...`
14
+ 2. Add `doc_for` to `...`
15
15
  3. Visit `/api-docs`
16
16
  4. See error
17
17
 
@@ -17,7 +17,7 @@ Explain the problem this feature would solve or the workflow it would improve.
17
17
  Describe how you'd like it to work, including example DSL usage if applicable:
18
18
 
19
19
  ```ruby
20
- swagger_doc :index do
20
+ doc_for :index do
21
21
  # your proposed syntax here
22
22
  end
23
23
  ```
data/CHANGELOG.md CHANGED
@@ -1,4 +1,35 @@
1
- ## [Unreleased]
1
+ ## [0.5.0] - 2026-05-31
2
+
3
+ ### Added
4
+ - **System Map**: a local, deterministic architecture graph at `/api-docs/system` (JSON at `/api-docs/system.json`), built from Rails routes, controllers, actions, Docit docs coverage, schemas, models, and source-derived service/job/mailer nodes — no external service required
5
+ - System Map navigation alongside the Scalar and Swagger UIs
6
+ - Light and dark themes for the System Map (light by default), with a toolbar toggle persisted across visits
7
+ - **Diagram view**: interactive architecture graph with drag-to-arrange, scroll-to-zoom, pan, fit-to-screen, and PNG export; click a node to inspect its connections, with neighbouring nodes highlighted; `/` focuses node search
8
+ - Endpoints-section filter for the diagram (Users, Orders, …) that narrows the graph to one resource and everything it touches
9
+ - **Docs view**: a request/response reference grouped by resource, with human-readable endpoint titles derived from doc summaries (falling back to REST conventions), a documentation-coverage indicator per section, and a detail panel showing parameters, request body, and responses
10
+ - AI section explanations in the Docs view: "Explain section" summarises how a resource's endpoints work together, gated by a coverage warning so undocumented sections don't silently spend tokens
11
+
12
+ ## [0.4.0] - 2026-04-18
13
+
14
+ ### Added
15
+ - `operation_id` DSL method: set a custom `operationId` per endpoint for cleaner SDK codegen
16
+ - Auto-generated `operationId` for every endpoint (e.g., `index_users`, `login_auth`) when not explicitly set
17
+ - `config.license` option: add license info (`name`, `url`) to the OpenAPI spec
18
+ - `config.contact` option: add contact info (`name`, `email`, `url`) to the OpenAPI spec
19
+ - `config.terms_of_service` option: add terms of service URL to the OpenAPI spec
20
+
21
+ ### Changed
22
+ - Renamed `swagger_doc` DSL to `doc_for` (`swagger_doc` still works as a backward-compatible alias)
23
+
24
+ ### Fixed
25
+ - `TagInjector` no longer crashes when `initializer_path` is nil
26
+ - `docit:autodoc` rake task: removed broken `--dry-run` CLI flag (use `DRY_RUN=1` env var instead)
27
+ - Fixed dummy app `auth_docs.rb` summary and request body to match integration test expectations
28
+
29
+ ## [0.3.1] - 2026-04-17
30
+
31
+ ### Fixed
32
+ - Fixed gem packaging: `doc_block_validator.rb` was missing from the published 0.3.0 gem, causing `LoadError` on require
2
33
 
3
34
  ## [0.3.0] - 2026-04-16
4
35
 
@@ -51,7 +82,7 @@
51
82
  ## [0.1.0] - 2026-04-08
52
83
 
53
84
  - Initial release
54
- - DSL: `swagger_doc` macro for inline controller documentation
85
+ - DSL: `doc_for` macro for inline controller documentation
55
86
  - DSL: `use_docs` + `Docit::DocFile` for separate doc files
56
87
  - Builders: request body, response, and parameter builders with nested object/array support
57
88
  - Schema `$ref` components via `Docit.define_schema`
data/CONTRIBUTING.md CHANGED
@@ -55,7 +55,7 @@ bundle exec rspec spec/docit/v2_features_spec.rb:30
55
55
  lib/docit.rb # Entry point, configuration, schema registry
56
56
  lib/docit/configuration.rb # Config class (title, auth, tags)
57
57
  lib/docit/registry.rb # Global operation store
58
- lib/docit/dsl.rb # swagger_doc macro
58
+ lib/docit/dsl.rb # doc_for macro
59
59
  lib/docit/operation.rb # Single endpoint documentation
60
60
  lib/docit/builders/ # DSL builders (response, request_body, parameter)
61
61
  lib/docit/schema_definition.rb # Reusable $ref schema definitions
data/README.md CHANGED
@@ -11,6 +11,8 @@ Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs wi
11
11
  ### Swagger
12
12
  ![Swagger UI](docs/images/swagger_image.png)
13
13
 
14
+ > **Full documentation:** [doc-it.dev/docs](https://doc-it.dev/docs)
15
+
14
16
  ## Table Of Contents
15
17
 
16
18
  - Getting started
@@ -39,6 +41,7 @@ Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs wi
39
41
  - [What the AI generates](#what-the-ai-generates)
40
42
  - Runtime and development
41
43
  - [Documentation UIs](#documentation-uis)
44
+ - [System Map](#system-map)
42
45
  - [How it works](#how-it-works)
43
46
  - [Mounting at a different path](#mounting-at-a-different-path)
44
47
  - [JSON spec only](#json-spec-only)
@@ -105,6 +108,15 @@ Docit.configure do |config|
105
108
  config.server "https://api.example.com", description: "Production"
106
109
  config.server "https://staging.example.com", description: "Staging"
107
110
  config.server "http://localhost:3000", description: "Development"
111
+
112
+ # License information:
113
+ config.license name: "MIT", url: "https://opensource.org/licenses/MIT"
114
+
115
+ # Contact information:
116
+ config.contact name: "API Team", email: "api@example.com", url: "https://example.com/support"
117
+
118
+ # Terms of service:
119
+ config.terms_of_service "https://example.com/tos"
108
120
  end
109
121
  ```
110
122
 
@@ -114,11 +126,11 @@ Docit supports two styles for documenting endpoints. Choose whichever fits your
114
126
 
115
127
  ### Style 1: Inline (simple APIs)
116
128
 
117
- Add `swagger_doc` blocks directly in your controller:
129
+ Add `doc_for` blocks directly in your controller:
118
130
 
119
131
  ```ruby
120
132
  class Api::V1::UsersController < ApplicationController
121
- swagger_doc :index do
133
+ doc_for :index do
122
134
  summary "List all users"
123
135
  tags "Users"
124
136
  response 200, "Users retrieved"
@@ -129,6 +141,8 @@ class Api::V1::UsersController < ApplicationController
129
141
  end
130
142
  ```
131
143
 
144
+ > **Upgrading?** The previous `swagger_doc` method still works as an alias for `doc_for`, so existing code won't break.
145
+
132
146
  ### Style 2: Separate doc files (recommended for larger APIs)
133
147
 
134
148
  Keep controllers clean by defining docs in dedicated files:
@@ -189,13 +203,13 @@ class Api::V1::UsersController < ApplicationController
189
203
  end
190
204
  ```
191
205
 
192
- You can also mix both styles — use `use_docs` for most actions and add inline `swagger_doc` for one-offs:
206
+ You can also mix both styles — use `use_docs` for most actions and add inline `doc_for` for one-offs:
193
207
 
194
208
  ```ruby
195
209
  class Api::V1::UsersController < ApplicationController
196
210
  use_docs Api::V1::UsersDocs # loads :index and :create from doc file
197
211
 
198
- swagger_doc :destroy do # inline doc for this one action
212
+ doc_for :destroy do # inline doc for this one action
199
213
  summary "Delete user"
200
214
  tags "Users"
201
215
  response 204, "Deleted"
@@ -209,12 +223,12 @@ end
209
223
 
210
224
  ### Endpoint documentation DSL
211
225
 
212
- The following examples work in both `swagger_doc` blocks and `doc` blocks.
226
+ The following examples work in both `doc_for` blocks and `doc` blocks.
213
227
 
214
228
  ### Request bodies
215
229
 
216
230
  ```ruby
217
- swagger_doc :create do
231
+ doc_for :create do
218
232
  summary "Create a user"
219
233
  tags "Users"
220
234
 
@@ -247,7 +261,7 @@ end
247
261
  ### Path parameters
248
262
 
249
263
  ```ruby
250
- swagger_doc :show do
264
+ doc_for :show do
251
265
  summary "Get a user"
252
266
  tags "Users"
253
267
 
@@ -271,7 +285,7 @@ end
271
285
  ### Enums
272
286
 
273
287
  ```ruby
274
- swagger_doc :index do
288
+ doc_for :index do
275
289
  summary "List orders"
276
290
  tags "Orders"
277
291
 
@@ -293,7 +307,7 @@ end
293
307
  Mark endpoints as requiring authentication:
294
308
 
295
309
  ```ruby
296
- swagger_doc :destroy do
310
+ doc_for :destroy do
297
311
  summary "Delete a user"
298
312
  tags "Users"
299
313
  security :bearer_auth # references the scheme from your config
@@ -306,7 +320,7 @@ end
306
320
  ### Deprecated endpoints
307
321
 
308
322
  ```ruby
309
- swagger_doc :legacy_search do
323
+ doc_for :legacy_search do
310
324
  summary "Search (legacy)"
311
325
  tags "Search"
312
326
  deprecated
@@ -373,7 +387,7 @@ end
373
387
  Reference them in any endpoint with `schema ref:`:
374
388
 
375
389
  ```ruby
376
- swagger_doc :show do
390
+ doc_for :show do
377
391
  summary "Get user"
378
392
  tags "Users"
379
393
 
@@ -386,7 +400,7 @@ swagger_doc :show do
386
400
  end
387
401
  end
388
402
 
389
- swagger_doc :create do
403
+ doc_for :create do
390
404
  summary "Create user"
391
405
  tags "Users"
392
406
 
@@ -407,7 +421,7 @@ This outputs `$ref: '#/components/schemas/User'` in the spec — Swagger UI reso
407
421
  Use `type: :file` with `content_type: "multipart/form-data"` for file upload endpoints:
408
422
 
409
423
  ```ruby
410
- swagger_doc :upload_avatar do
424
+ doc_for :upload_avatar do
411
425
  summary "Upload avatar"
412
426
  tags "Users"
413
427
 
@@ -486,15 +500,28 @@ Docit ships with two documentation UIs, both reading from the same OpenAPI spec:
486
500
  | `/api-docs` | Default (Scalar) | Configurable via `config.default_ui` |
487
501
  | `/api-docs/scalar` | Scalar API Reference | Modern UI with built-in API client, dark mode, code samples |
488
502
  | `/api-docs/swagger` | Swagger UI | Classic OpenAPI explorer |
503
+ | `/api-docs/system` | System Map | Local architecture graph for routes, controllers, docs coverage, and app structure |
489
504
  | `/api-docs/spec` | Raw JSON | OpenAPI 3.0.3 spec |
490
505
 
491
506
  Both UIs include a navigation bar to switch between them. Set `config.default_ui = :swagger` to make Swagger the default at `/api-docs`.
492
507
 
508
+ ## System Map
509
+
510
+ Docit includes a local system map for understanding how your Rails API fits together. It builds a **deterministic** graph from Rails routes, controller actions, Docit docs, schemas, models, and source references — no external service is required to view it.
511
+
512
+ Visit `/api-docs/system` to open it. It has two views, a light/dark theme toggle, and PNG export:
513
+
514
+ - **Diagram** — an interactive architecture graph. Drag nodes to rearrange, scroll to zoom, and click a node to inspect its connections (its neighbours highlight while the rest fade back). Filter to a single resource with the section dropdown, or press `/` to search nodes.
515
+ - **Docs** — a request/response reference grouped by resource. Each endpoint shows a human-readable title, its method and path, parameters, request body, and responses, drawn from your Docit docs. A coverage indicator shows how many endpoints in each section are documented.
516
+
517
+ If you have an AI provider configured, the Docs view's **Explain section** button summarises how a resource's endpoints work together. It warns before running on sections with undocumented endpoints, so documenting first gives a better result.
518
+
493
519
  ## How it works
494
520
 
495
- 1. `swagger_doc` registers an **Operation** for each controller action in a global **Registry**
521
+ 1. `doc_for` registers an **Operation** for each controller action in a global **Registry**
496
522
  2. When someone visits `/api-docs/spec`, Docit's **SchemaGenerator** combines all registered operations with your Rails routes (via **RouteInspector**) to produce an OpenAPI 3.0.3 JSON document
497
523
  3. The **Engine** serves the configured documentation UI at `/api-docs`, pointing it at the generated spec
524
+ 4. The **SystemGraph** builds a local architecture graph for `/api-docs/system`
498
525
 
499
526
  The DSL is included in all controllers automatically via a Rails Engine initializer — no manual `include` needed if you're using `ActionController::API` or `ActionController::Base`.
500
527
 
@@ -16,6 +16,31 @@ module Docit
16
16
  render_ui(:scalar)
17
17
  end
18
18
 
19
+ def system
20
+ render_ui(:system)
21
+ end
22
+
23
+ def system_spec
24
+ unless Docit.configuration.system_graph_enabled
25
+ return render(json: { error: "System graph disabled" }, status: :not_found)
26
+ end
27
+
28
+ RouteInspector.eager_load_controllers!
29
+ render json: SystemGraph::Generator.generate
30
+ end
31
+
32
+ def system_insights
33
+ graph = system_graph
34
+ insight = Ai::SystemInsightGenerator.new(
35
+ graph: graph,
36
+ selected_node_ids: selected_node_ids,
37
+ mode: insight_mode
38
+ ).generate
39
+ render json: { insight: insight }
40
+ rescue Docit::Error => e
41
+ render json: { error: e.message }, status: :unprocessable_entity
42
+ end
43
+
19
44
  def spec
20
45
  RouteInspector.eager_load_controllers!
21
46
  render json: SchemaGenerator.generate
@@ -25,21 +50,50 @@ module Docit
25
50
 
26
51
  RENDERERS = {
27
52
  swagger: UI::SwaggerRenderer,
28
- scalar: UI::ScalarRenderer
53
+ scalar: UI::ScalarRenderer,
54
+ system: UI::SystemRenderer
29
55
  }.freeze
30
56
 
31
57
  def render_ui(ui_name)
32
- renderer = RENDERERS.fetch(ui_name).new(spec_url: spec_url, nav_paths: nav_paths)
58
+ renderer = RENDERERS.fetch(ui_name).new(
59
+ spec_url: spec_url,
60
+ system_url: system_url,
61
+ system_insights_url: system_insights_url,
62
+ nav_paths: nav_paths
63
+ )
33
64
  render html: renderer.render.html_safe, layout: false
34
65
  end
35
66
 
67
+ def system_graph
68
+ RouteInspector.eager_load_controllers!
69
+ SystemGraph::Generator.generate
70
+ end
71
+
72
+ def selected_node_ids
73
+ params.fetch(:node_ids, "").to_s.split(",").reject(&:empty?)
74
+ end
75
+
76
+ # Validate at the boundary: only the known modes are honored, anything
77
+ # else falls back to the safe per-node default.
78
+ def insight_mode
79
+ params[:mode].to_s == "section" ? :section : :nodes
80
+ end
81
+
36
82
  def spec_url
37
83
  "#{request.base_url}#{Docit::Engine.routes.url_helpers.spec_path}"
38
84
  end
39
85
 
86
+ def system_url
87
+ "#{request.base_url}#{Docit::Engine.routes.url_helpers.system_spec_path}"
88
+ end
89
+
90
+ def system_insights_url
91
+ "#{request.base_url}#{Docit::Engine.routes.url_helpers.system_insights_path}"
92
+ end
93
+
40
94
  def nav_paths
41
95
  helpers = Docit::Engine.routes.url_helpers
42
- { swagger: helpers.swagger_path, scalar: helpers.scalar_path }
96
+ { swagger: helpers.swagger_path, scalar: helpers.scalar_path, system: helpers.system_path }
43
97
  end
44
98
  end
45
99
  end
data/config/routes.rb CHANGED
@@ -4,5 +4,8 @@ Docit::Engine.routes.draw do
4
4
  root to: "ui#index"
5
5
  get "swagger", to: "ui#swagger"
6
6
  get "scalar", to: "ui#scalar"
7
+ get "system.json", to: "ui#system_spec", defaults: { format: :json }, as: :system_spec
8
+ get "system/insights", to: "ui#system_insights", defaults: { format: :json }, as: :system_insights
9
+ get "system", to: "ui#system", as: :system
7
10
  get "spec", to: "ui#spec", defaults: { format: :json }
8
11
  end
@@ -62,11 +62,11 @@ module Docit
62
62
  end
63
63
 
64
64
  routes_file = Rails.root.join("config", "routes.rb")
65
- if File.exist?(routes_file) && !File.read(routes_file).include?("Docit::Engine")
66
- @output.puts "Warning: Docit engine is not mounted in config/routes.rb"
67
- @output.puts " Run: rails generate docit:install (or add: mount Docit::Engine => \"/api-docs\")"
68
- @output.puts ""
69
- end
65
+ return unless File.exist?(routes_file) && !File.read(routes_file).include?("Docit::Engine")
66
+
67
+ @output.puts "Warning: Docit engine is not mounted in config/routes.rb"
68
+ @output.puts " Run: rails generate docit:install (or add: mount Docit::Engine => \"/api-docs\")"
69
+ @output.puts ""
70
70
  end
71
71
 
72
72
  def detect_gaps
@@ -84,20 +84,20 @@ module Docit
84
84
  @output.puts "Docit will send controller source code to #{config.provider.capitalize} to generate documentation."
85
85
  @output.puts "Review the endpoints first if they contain secrets or proprietary logic."
86
86
 
87
- if @input.respond_to?(:tty?) && @input.tty?
88
- loop do
89
- @output.print "Continue? (y/n): "
90
- choice = @input.gets.to_s.strip.downcase
91
-
92
- case choice
93
- when "y", "yes"
94
- @output.puts ""
95
- return
96
- when "n", "no"
97
- raise Docit::Error, "Autodoc cancelled."
98
- else
99
- @output.puts "Please enter y or n."
100
- end
87
+ return unless @input.respond_to?(:tty?) && @input.tty?
88
+
89
+ loop do
90
+ @output.print "Continue? (y/n): "
91
+ choice = @input.gets.to_s.strip.downcase
92
+
93
+ case choice
94
+ when "y", "yes"
95
+ @output.puts ""
96
+ return
97
+ when "n", "no"
98
+ raise Docit::Error, "Autodoc cancelled."
99
+ else
100
+ @output.puts "Please enter y or n."
101
101
  end
102
102
  end
103
103
  end
@@ -138,6 +138,7 @@ module Docit
138
138
  invalid_output_retries += 1
139
139
  if invalid_output_retries <= MAX_INVALID_OUTPUT_RETRIES
140
140
  validation_error = e.message
141
+ @output.puts " invalid output, retrying"
141
142
  retry
142
143
  end
143
144
 
@@ -184,11 +185,11 @@ module Docit
184
185
 
185
186
  def inject_tags(generated)
186
187
  all_tags = generated.values.flatten.join("\n").scan(/tags\s+["']([^"']+)["']/).flatten
187
- if all_tags.any?
188
- injected = TagInjector.new(tags: all_tags).inject
189
- injected.each { |tag| @output.puts " Added tag \"#{tag}\" to config/initializers/docit.rb" }
190
- @results[:tags] = injected
191
- end
188
+ return unless all_tags.any?
189
+
190
+ injected = TagInjector.new(tags: all_tags).inject
191
+ injected.each { |tag| @output.puts " Added tag \"#{tag}\" to config/initializers/docit.rb" }
192
+ @results[:tags] = injected
192
193
  end
193
194
 
194
195
  def strip_markdown_fences(text)
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Docit
6
+ module Ai
7
+ class SystemInsightGenerator
8
+ # mode: :nodes -> explain an arbitrary selection (diagram "AI Explain")
9
+ # :section -> explain one resource and how its endpoints work together
10
+ def initialize(graph:, selected_node_ids: [], mode: :nodes)
11
+ @graph = graph
12
+ @selected_node_ids = selected_node_ids
13
+ @mode = mode
14
+ end
15
+
16
+ def generate
17
+ config = Configuration.load
18
+ Client.for(config).generate(prompt)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :graph, :selected_node_ids, :mode
24
+
25
+ def prompt
26
+ mode == :section ? section_prompt : nodes_prompt
27
+ end
28
+
29
+ def nodes_prompt
30
+ <<~PROMPT
31
+ You are a senior engineer explaining a Rails system architecture to a developer. Keep your explanation extremely concise, professional, and clear. Avoid fluff, unnecessary details, or general tutorial information. Focus only on the provided components.
32
+
33
+ FORMAT YOUR RESPONSE EXACTLY LIKE THIS:
34
+
35
+ ## Overview
36
+ A 1-2 sentence plain-English summary of what this component/action does.
37
+
38
+ ## Data Flow
39
+ Show a simple, linear flow diagram using text and arrows (→). Keep it to 1 line if possible.
40
+ Example:
41
+ Client → GET /api/v1/users → UsersController#index → queries User model
42
+
43
+ ## Connections & Interactions
44
+ List only the direct, relevant relationships from the graph (max 3 bullets):
45
+ - **Component** (type): Action details/purpose.
46
+
47
+ Do not invent or assume anything outside of the provided graph. Keep the total response under 150 words.
48
+
49
+ ---
50
+
51
+ Selected node ids:
52
+ #{selected_node_ids.join("\n")}
53
+
54
+ Graph JSON:
55
+ #{JSON.pretty_generate(compact_graph)}
56
+ PROMPT
57
+ end
58
+
59
+ def section_prompt
60
+ <<~PROMPT
61
+ You are a senior engineer writing the introduction to a section of API documentation. The section covers ONE resource and all of its endpoints. Explain, for a developer new to this codebase, what the resource is for and how its endpoints work together as a workflow.
62
+
63
+ Use the documented summaries where available. Where an endpoint has no documentation, infer cautiously from its HTTP method and path, and do not fabricate behavior.
64
+
65
+ FORMAT YOUR RESPONSE EXACTLY LIKE THIS:
66
+
67
+ ## What this section does
68
+ 1-2 sentences on the resource and its overall purpose.
69
+
70
+ ## How the endpoints work together
71
+ A short narrative (2-4 sentences) describing the typical flow across these endpoints — e.g. how a client lists, creates, then updates this resource. Reference endpoints by their HTTP method and path.
72
+
73
+ ## Notes
74
+ Up to 2 bullets on related models/services or anything a consumer must know. Omit this section if there is nothing concrete to say.
75
+
76
+ Do not invent endpoints, fields, or behavior not present in the graph. Keep the total response under 180 words.
77
+
78
+ ---
79
+
80
+ Endpoint node ids in this section:
81
+ #{selected_node_ids.join("\n")}
82
+
83
+ Graph JSON:
84
+ #{JSON.pretty_generate(compact_graph)}
85
+ PROMPT
86
+ end
87
+
88
+ def compact_graph
89
+ nodes = selected_nodes
90
+ node_ids = nodes.map { |node| node[:id] }
91
+
92
+ # Include edges where at least one end is in the selection
93
+ related_edges = graph[:edges].select do |edge|
94
+ node_ids.include?(edge[:source]) || node_ids.include?(edge[:target])
95
+ end
96
+
97
+ # Also include neighbor nodes (one hop away) for context
98
+ neighbor_ids = Set.new(node_ids)
99
+ related_edges.each do |edge|
100
+ neighbor_ids.add(edge[:source])
101
+ neighbor_ids.add(edge[:target])
102
+ end
103
+
104
+ neighbor_nodes = graph[:nodes].select { |node| neighbor_ids.include?(node[:id]) }
105
+
106
+ {
107
+ selected_nodes: nodes.map { |node| compact_hash(node, %i[id type label status file metadata]) },
108
+ context_nodes: (neighbor_nodes - nodes).map { |node| compact_hash(node, %i[id type label status]) },
109
+ edges: related_edges.map { |edge| compact_hash(edge, %i[source target type confidence evidence]) },
110
+ stats: graph[:stats]
111
+ }
112
+ end
113
+
114
+ def selected_nodes
115
+ return graph[:nodes] if selected_node_ids.empty?
116
+
117
+ graph[:nodes].select { |node| selected_node_ids.include?(node[:id]) }
118
+ end
119
+
120
+ def compact_hash(hash, keys)
121
+ keys.each_with_object({}) do |key, result|
122
+ result[key] = hash[key] if hash.key?(key)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -8,7 +8,7 @@ module Docit
8
8
  end
9
9
 
10
10
  def inject
11
- return [] if initializer_path && File.exist?(initializer_path) == false
11
+ return [] unless initializer_path && File.exist?(initializer_path)
12
12
 
13
13
  content = File.read(initializer_path)
14
14
  existing_tags = content.scan(/config\.tag\s+["']([^"']+)["']/).flatten
data/lib/docit/ai.rb CHANGED
@@ -12,3 +12,4 @@ require_relative "ai/doc_writer"
12
12
  require_relative "ai/tag_injector"
13
13
  require_relative "ai/autodoc_runner"
14
14
  require_relative "ai/scaffold_generator"
15
+ require_relative "ai/system_insight_generator"
@@ -5,7 +5,8 @@ module Docit
5
5
  class Configuration
6
6
  SUPPORTED_UIS = %i[scalar swagger].freeze
7
7
 
8
- attr_accessor :title, :version, :description, :base_url
8
+ attr_accessor :title, :version, :description, :base_url,
9
+ :system_graph_enabled, :system_graph_excluded_paths
9
10
  attr_reader :default_ui
10
11
 
11
12
  def initialize
@@ -13,10 +14,15 @@ module Docit
13
14
  @version = "1.0.0"
14
15
  @description = "Welcome to the API documentation. Browse the endpoints below to get started."
15
16
  @base_url = "/"
17
+ @system_graph_enabled = true
18
+ @system_graph_excluded_paths = []
16
19
  @default_ui = :scalar
17
20
  @security_schemes = {}
18
21
  @tags = []
19
22
  @servers = []
23
+ @license = nil
24
+ @contact = nil
25
+ @terms_of_service = nil
20
26
  end
21
27
 
22
28
  def default_ui=(value)
@@ -75,5 +81,35 @@ module Docit
75
81
  def servers
76
82
  @servers.dup
77
83
  end
84
+
85
+ def license(name:, url: nil)
86
+ entry = { name: name }
87
+ entry[:url] = url if url
88
+ @license = entry
89
+ end
90
+
91
+ def license_info
92
+ @license&.dup
93
+ end
94
+
95
+ def contact(name: nil, email: nil, url: nil)
96
+ entry = {}
97
+ entry[:name] = name if name
98
+ entry[:email] = email if email
99
+ entry[:url] = url if url
100
+ @contact = entry
101
+ end
102
+
103
+ def contact_info
104
+ @contact&.dup
105
+ end
106
+
107
+ def terms_of_service(url)
108
+ @terms_of_service = url
109
+ end
110
+
111
+ def terms_of_service_url
112
+ @terms_of_service
113
+ end
78
114
  end
79
115
  end
@@ -40,7 +40,7 @@ module Docit
40
40
  base.instance_variable_set(:@_docs, {})
41
41
  end
42
42
 
43
- # The block receives the same DSL as swagger_doc.
43
+ # The block receives the same DSL as doc_for.
44
44
  def doc(action, &block)
45
45
  @_docs[action.to_sym] = block
46
46
  end