docsmith 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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rspec_status +212 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +66 -0
  8. data/Rakefile +8 -0
  9. data/USAGE.md +510 -0
  10. data/docs/superpowers/plans/2026-04-01-docsmith-full-plan.md +6459 -0
  11. data/docs/superpowers/plans/2026-04-08-parsers-remove-branches-docs.md +2112 -0
  12. data/docs/superpowers/specs/2026-04-01-docsmith-phase1-design.md +340 -0
  13. data/docsmith_spec.md +630 -0
  14. data/lib/docsmith/auto_save.rb +29 -0
  15. data/lib/docsmith/comments/anchor.rb +68 -0
  16. data/lib/docsmith/comments/comment.rb +44 -0
  17. data/lib/docsmith/comments/manager.rb +73 -0
  18. data/lib/docsmith/comments/migrator.rb +64 -0
  19. data/lib/docsmith/configuration.rb +95 -0
  20. data/lib/docsmith/diff/engine.rb +39 -0
  21. data/lib/docsmith/diff/parsers/html.rb +64 -0
  22. data/lib/docsmith/diff/parsers/markdown.rb +60 -0
  23. data/lib/docsmith/diff/renderers/base.rb +62 -0
  24. data/lib/docsmith/diff/renderers/registry.rb +41 -0
  25. data/lib/docsmith/diff/renderers.rb +10 -0
  26. data/lib/docsmith/diff/result.rb +77 -0
  27. data/lib/docsmith/diff.rb +6 -0
  28. data/lib/docsmith/document.rb +44 -0
  29. data/lib/docsmith/document_version.rb +50 -0
  30. data/lib/docsmith/errors.rb +18 -0
  31. data/lib/docsmith/events/event.rb +19 -0
  32. data/lib/docsmith/events/hook_registry.rb +14 -0
  33. data/lib/docsmith/events/notifier.rb +22 -0
  34. data/lib/docsmith/rendering/html_renderer.rb +36 -0
  35. data/lib/docsmith/rendering/json_renderer.rb +29 -0
  36. data/lib/docsmith/version.rb +5 -0
  37. data/lib/docsmith/version_manager.rb +143 -0
  38. data/lib/docsmith/version_tag.rb +25 -0
  39. data/lib/docsmith/versionable.rb +252 -0
  40. data/lib/docsmith.rb +52 -0
  41. data/lib/generators/docsmith/install/install_generator.rb +27 -0
  42. data/lib/generators/docsmith/install/templates/create_docsmith_tables.rb.erb +64 -0
  43. data/lib/generators/docsmith/install/templates/docsmith_initializer.rb.erb +19 -0
  44. data/sig/docsmith.rbs +4 -0
  45. metadata +196 -0
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docsmith
4
+ # ActiveRecord mixin that adds full versioning to any model.
5
+ #
6
+ # Usage:
7
+ # class Article < ApplicationRecord
8
+ # include Docsmith::Versionable
9
+ # docsmith_config { content_field :body; content_type :markdown }
10
+ # end
11
+ module Versionable
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ base.after_save(:_docsmith_auto_save_callback)
15
+ end
16
+
17
+ module ClassMethods
18
+ # Configure per-class Docsmith options. All keys optional.
19
+ # Unset keys fall through to global config then gem defaults.
20
+ # @yield block evaluated on a Docsmith::ClassConfig instance
21
+ # @return [Docsmith::ClassConfig]
22
+ def docsmith_config(&block)
23
+ @_docsmith_class_config ||= Docsmith::ClassConfig.new
24
+ @_docsmith_class_config.instance_eval(&block) if block_given?
25
+ @_docsmith_class_config
26
+ end
27
+
28
+ # @return [Hash] fully resolved config (read-time resolution)
29
+ def docsmith_resolved_config
30
+ Docsmith::Configuration.resolve(
31
+ @_docsmith_class_config&.settings || {},
32
+ Docsmith.configuration
33
+ )
34
+ end
35
+ end
36
+
37
+ # Create a new DocumentVersion snapshot of this record's content.
38
+ # Returns nil if content is identical to the latest version.
39
+ # Raises Docsmith::InvalidContentField if content_field returns a non-String
40
+ # and no content_extractor is configured.
41
+ #
42
+ # @param author [Object, nil]
43
+ # @param summary [String, nil]
44
+ # @return [Docsmith::DocumentVersion, nil]
45
+ def save_version!(author:, summary: nil)
46
+ _sync_docsmith_content!
47
+ Docsmith::VersionManager.save!(
48
+ _docsmith_document,
49
+ author: author,
50
+ summary: summary,
51
+ config: self.class.docsmith_resolved_config
52
+ )
53
+ end
54
+
55
+ # Debounced auto-save. Returns nil if debounce window has not elapsed
56
+ # OR content is unchanged. Both non-save cases return nil.
57
+ # auto_save: false in config causes this to always return nil.
58
+ #
59
+ # @param author [Object, nil]
60
+ # @return [Docsmith::DocumentVersion, nil]
61
+ def auto_save_version!(author: nil)
62
+ config = self.class.docsmith_resolved_config
63
+ return nil unless config[:auto_save]
64
+
65
+ _sync_docsmith_content!
66
+ Docsmith::AutoSave.call(_docsmith_document, author: author, config: config)
67
+ end
68
+
69
+ # @return [ActiveRecord::Relation<Docsmith::DocumentVersion>] ordered by version_number
70
+ def versions
71
+ _docsmith_document.document_versions
72
+ end
73
+
74
+ # @return [Docsmith::DocumentVersion, nil] latest version
75
+ def current_version
76
+ _docsmith_document.current_version
77
+ end
78
+
79
+ # @param number [Integer] 1-indexed version_number
80
+ # @return [Docsmith::DocumentVersion, nil]
81
+ def version(number)
82
+ _docsmith_document.document_versions.find_by(version_number: number)
83
+ end
84
+
85
+ # Restore to a previous version. Creates a new version with the old content.
86
+ # Syncs restored content back to the model's content_field via update_column
87
+ # (bypasses after_save to prevent a duplicate auto-save).
88
+ # Never mutates existing versions.
89
+ #
90
+ # @param number [Integer] version_number to restore from
91
+ # @param author [Object, nil]
92
+ # @return [Docsmith::DocumentVersion]
93
+ # @raise [Docsmith::VersionNotFound]
94
+ def restore_version!(number, author:)
95
+ result = Docsmith::VersionManager.restore!(
96
+ _docsmith_document,
97
+ version: number,
98
+ author: author,
99
+ config: self.class.docsmith_resolved_config
100
+ )
101
+ field = self.class.docsmith_resolved_config[:content_field]
102
+ update_column(field, _docsmith_document.reload.content)
103
+ result
104
+ end
105
+
106
+ # Tag a specific version. Names are unique per document.
107
+ # @param number [Integer] version_number to tag
108
+ # @param name [String]
109
+ # @param author [Object, nil]
110
+ # @return [Docsmith::VersionTag]
111
+ def tag_version!(number, name:, author:)
112
+ Docsmith::VersionManager.tag!(
113
+ _docsmith_document, version: number, name: name, author: author)
114
+ end
115
+
116
+ # @param tag_name [String]
117
+ # @return [Docsmith::DocumentVersion, nil]
118
+ def tagged_version(tag_name)
119
+ tag = _docsmith_document.version_tags.find_by(name: tag_name)
120
+ tag&.version
121
+ end
122
+
123
+ # @param number [Integer] version_number
124
+ # @return [Array<String>] tag names on that version
125
+ def version_tags(number)
126
+ ver = version(number)
127
+ return [] unless ver
128
+ ver.version_tags.pluck(:name)
129
+ end
130
+
131
+ # Computes a diff from version N to the current (latest) version.
132
+ #
133
+ # @param version_number [Integer]
134
+ # @return [Docsmith::Diff::Result]
135
+ # @raise [ActiveRecord::RecordNotFound] if version_number does not exist
136
+ def diff_from(version_number)
137
+ doc = _docsmith_document
138
+ v_from = Docsmith::DocumentVersion.find_by!(document: doc, version_number: version_number)
139
+ v_to = Docsmith::DocumentVersion.where(document_id: doc.id).order(version_number: :desc).first!
140
+ Docsmith::Diff.between(v_from, v_to)
141
+ end
142
+
143
+ # Computes a diff between two named versions.
144
+ #
145
+ # @param from_version [Integer]
146
+ # @param to_version [Integer]
147
+ # @return [Docsmith::Diff::Result]
148
+ # @raise [ActiveRecord::RecordNotFound] if either version does not exist
149
+ def diff_between(from_version, to_version)
150
+ doc = _docsmith_document
151
+ v_from = Docsmith::DocumentVersion.find_by!(document: doc, version_number: from_version)
152
+ v_to = Docsmith::DocumentVersion.find_by!(document: doc, version_number: to_version)
153
+ Docsmith::Diff.between(v_from, v_to)
154
+ end
155
+
156
+ # Adds a comment to a specific version of this document.
157
+ #
158
+ # @param version [Integer] version_number
159
+ # @param body [String]
160
+ # @param author [Object] polymorphic author
161
+ # @param anchor [Hash, nil] { start_offset:, end_offset: } for inline range comments
162
+ # @param parent [Comments::Comment, nil] parent comment for threading
163
+ # @return [Docsmith::Comments::Comment]
164
+ def add_comment!(version:, body:, author:, anchor: nil, parent: nil)
165
+ Comments::Manager.add!(
166
+ _docsmith_document,
167
+ version_number: version,
168
+ body: body,
169
+ author: author,
170
+ anchor: anchor,
171
+ parent: parent
172
+ )
173
+ end
174
+
175
+ # Returns all comments across all versions of this document.
176
+ #
177
+ # @return [ActiveRecord::Relation<Docsmith::Comments::Comment>]
178
+ def comments
179
+ doc = _docsmith_document
180
+ Comments::Comment.joins(:version)
181
+ .where(docsmith_versions: { document_id: doc.id })
182
+ end
183
+
184
+ # Returns comments on a specific version, optionally filtered by anchor type.
185
+ #
186
+ # @param version [Integer] version_number
187
+ # @param type [Symbol, nil] :document or :range to filter; nil = all
188
+ # @return [ActiveRecord::Relation<Docsmith::Comments::Comment>]
189
+ def comments_on(version:, type: nil)
190
+ doc = _docsmith_document
191
+ dv = Docsmith::DocumentVersion.find_by!(document: doc, version_number: version)
192
+ rel = Comments::Comment.where(version: dv)
193
+ rel = rel.where(anchor_type: type.to_s) if type
194
+ rel
195
+ end
196
+
197
+ # Returns all unresolved comments across all versions.
198
+ #
199
+ # @return [ActiveRecord::Relation<Docsmith::Comments::Comment>]
200
+ def unresolved_comments
201
+ comments.merge(Comments::Comment.unresolved)
202
+ end
203
+
204
+ # Migrates top-level comments from one version to another.
205
+ #
206
+ # @param from [Integer] source version_number
207
+ # @param to [Integer] target version_number
208
+ # @return [void]
209
+ def migrate_comments!(from:, to:)
210
+ Comments::Migrator.migrate!(_docsmith_document, from: from, to: to)
211
+ end
212
+
213
+ private
214
+
215
+ # Finds or creates the shadow Docsmith::Document for this record.
216
+ # Cached in @_docsmith_document after first lookup.
217
+ def _docsmith_document
218
+ config = self.class.docsmith_resolved_config
219
+ @_docsmith_document ||= Docsmith::Document.find_or_create_by!(subject: self) do |doc|
220
+ doc.content_type = config[:content_type].to_s
221
+ doc.title = respond_to?(:title) ? title.to_s : self.class.name
222
+ end
223
+ end
224
+
225
+ # Reads content from the model via content_extractor or content_field,
226
+ # validates it is a String, then syncs to the shadow document's content column.
227
+ def _sync_docsmith_content!
228
+ config = self.class.docsmith_resolved_config
229
+
230
+ raw = if config[:content_extractor]
231
+ config[:content_extractor].call(self)
232
+ else
233
+ public_send(config[:content_field])
234
+ end
235
+
236
+ unless raw.nil? || raw.is_a?(String)
237
+ source = config[:content_extractor] ? "content_extractor" : "content_field :#{config[:content_field]}"
238
+ raise Docsmith::InvalidContentField,
239
+ "#{source} must return a String, got #{raw.class}. " \
240
+ "Use content_extractor: ->(record) { ... } for non-string fields."
241
+ end
242
+
243
+ _docsmith_document.update_column(:content, raw.to_s)
244
+ end
245
+
246
+ def _docsmith_auto_save_callback
247
+ auto_save_version!
248
+ rescue Docsmith::InvalidContentField
249
+ nil
250
+ end
251
+ end
252
+ end
data/lib/docsmith.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "active_support"
5
+ require "active_support/core_ext/numeric/time"
6
+ require "active_support/notifications"
7
+
8
+ require_relative "docsmith/version"
9
+ require_relative "docsmith/errors"
10
+ require_relative "docsmith/configuration"
11
+ require_relative "docsmith/events/event"
12
+ require_relative "docsmith/events/hook_registry"
13
+ require_relative "docsmith/events/notifier"
14
+ require_relative "docsmith/document"
15
+ require_relative "docsmith/document_version"
16
+ require_relative "docsmith/version_tag"
17
+ require_relative "docsmith/auto_save"
18
+ require_relative "docsmith/version_manager"
19
+ require_relative "docsmith/versionable"
20
+ require_relative "docsmith/diff"
21
+ require_relative "docsmith/diff/renderers"
22
+ require_relative "docsmith/diff/renderers/base"
23
+ require_relative "docsmith/diff/renderers/registry"
24
+ require_relative "docsmith/diff/result"
25
+ require_relative "docsmith/diff/parsers/markdown"
26
+ require_relative "docsmith/diff/parsers/html"
27
+ require_relative "docsmith/diff/engine"
28
+ require_relative "docsmith/rendering/html_renderer"
29
+ require_relative "docsmith/rendering/json_renderer"
30
+ require_relative "docsmith/comments/comment"
31
+ require_relative "docsmith/comments/anchor"
32
+ require_relative "docsmith/comments/manager"
33
+ require_relative "docsmith/comments/migrator"
34
+
35
+ module Docsmith
36
+ class << self
37
+ # @yield [Docsmith::Configuration]
38
+ def configure
39
+ yield configuration
40
+ end
41
+
42
+ # @return [Docsmith::Configuration]
43
+ def configuration
44
+ @configuration ||= Configuration.new
45
+ end
46
+
47
+ # Reset to gem defaults. Call in specs via config.before(:each).
48
+ def reset_configuration!
49
+ @configuration = Configuration.new
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module Docsmith
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ source_root File.expand_path("templates", __dir__)
12
+
13
+ desc "Creates the Docsmith migration and initializer."
14
+
15
+ def create_migration
16
+ migration_template(
17
+ "create_docsmith_tables.rb.erb",
18
+ "db/migrate/create_docsmith_tables.rb"
19
+ )
20
+ end
21
+
22
+ def create_initializer
23
+ template "docsmith_initializer.rb.erb", "config/initializers/docsmith.rb"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDocsmithTables < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def change
5
+ create_table :docsmith_documents do |t|
6
+ t.string :title
7
+ t.text :content
8
+ t.string :content_type, null: false, default: "markdown"
9
+ t.integer :versions_count, null: false, default: 0
10
+ t.datetime :last_versioned_at
11
+ t.string :subject_type
12
+ t.bigint :subject_id
13
+ t.jsonb :metadata, null: false, default: {}
14
+ t.timestamps
15
+ end
16
+ add_index :docsmith_documents, %i[subject_type subject_id]
17
+
18
+ create_table :docsmith_versions do |t|
19
+ t.references :document, null: false, foreign_key: { to_table: :docsmith_documents }
20
+ t.integer :version_number, null: false
21
+ t.text :content, null: false
22
+ t.string :content_type, null: false
23
+ t.string :author_type
24
+ t.bigint :author_id
25
+ t.string :change_summary
26
+ t.jsonb :metadata, null: false, default: {}
27
+ t.datetime :created_at, null: false
28
+ end
29
+ add_index :docsmith_versions, %i[document_id version_number], unique: true
30
+ add_index :docsmith_versions, %i[author_type author_id]
31
+
32
+ create_table :docsmith_version_tags do |t|
33
+ t.references :document, null: false, foreign_key: { to_table: :docsmith_documents }
34
+ t.bigint :version_id, null: false
35
+ t.string :name, null: false
36
+ t.string :author_type
37
+ t.bigint :author_id
38
+ t.datetime :created_at, null: false
39
+ end
40
+ add_index :docsmith_version_tags, %i[document_id name], unique: true
41
+ add_index :docsmith_version_tags, [:version_id]
42
+ add_foreign_key :docsmith_version_tags, :docsmith_versions, column: :version_id
43
+
44
+ create_table :docsmith_comments do |t|
45
+ t.bigint :version_id, null: false
46
+ t.bigint :parent_id
47
+ t.string :author_type
48
+ t.bigint :author_id
49
+ t.text :body, null: false
50
+ t.string :anchor_type, null: false, default: "document"
51
+ t.jsonb :anchor_data, null: false, default: {}
52
+ t.boolean :resolved, null: false, default: false
53
+ t.string :resolved_by_type
54
+ t.bigint :resolved_by_id
55
+ t.datetime :resolved_at
56
+ t.timestamps null: false
57
+ end
58
+ add_index :docsmith_comments, :version_id
59
+ add_index :docsmith_comments, :parent_id
60
+ add_index :docsmith_comments, [:author_type, :author_id]
61
+ add_foreign_key :docsmith_comments, :docsmith_versions, column: :version_id
62
+ add_foreign_key :docsmith_comments, :docsmith_comments, column: :parent_id
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ Docsmith.configure do |config|
4
+ # Resolution order: per-class docsmith_config > this global config > gem defaults
5
+ #
6
+ # config.default_content_field = :body # gem default: :body
7
+ # config.default_content_type = :markdown # gem default: :markdown (:html, :markdown, :json)
8
+ # config.auto_save = true # gem default: true
9
+ # config.default_debounce = 30 # gem default: 30 (integer seconds)
10
+ # config.max_versions = nil # gem default: nil (unlimited)
11
+ # config.content_extractor = nil # example: ->(record) { record.body.to_html }
12
+ # config.table_prefix = "docsmith" # gem default: "docsmith"
13
+ # config.diff_context_lines = 3
14
+ #
15
+ # Event hooks (fires synchronously before AS::Notifications):
16
+ # config.on(:version_created) { |event| Rails.logger.info "v#{event.version.version_number} saved" }
17
+ # config.on(:version_restored) { |event| }
18
+ # config.on(:version_tagged) { |event| }
19
+ end
data/sig/docsmith.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Docsmith
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docsmith
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - swastik009
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: diff-lcs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: factory_bot
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '6.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '6.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.50'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.50'
111
+ description: |
112
+ Docsmith adds snapshot-based versioning to any ActiveRecord model with zero system dependencies.
113
+
114
+ • Full content snapshots (HTML, Markdown, JSON) for instant rollbacks
115
+ • Format-aware diff engine: word-level diffs for Markdown, tag-atomic diffs for HTML
116
+ • Document-level and range-anchored comments with threading and version migration
117
+ • Per-class configuration, debounced auto-save, lifecycle events, and a clean API
118
+
119
+ Perfect for wikis, CMS pages, API specs, legal documents, or any content that needs
120
+ an audit trail and inline collaboration.
121
+ email:
122
+ - swastik.thapaliya@gmail.com
123
+ executables: []
124
+ extensions: []
125
+ extra_rdoc_files: []
126
+ files:
127
+ - ".rspec"
128
+ - ".rspec_status"
129
+ - CHANGELOG.md
130
+ - CODE_OF_CONDUCT.md
131
+ - LICENSE.txt
132
+ - README.md
133
+ - Rakefile
134
+ - USAGE.md
135
+ - docs/superpowers/plans/2026-04-01-docsmith-full-plan.md
136
+ - docs/superpowers/plans/2026-04-08-parsers-remove-branches-docs.md
137
+ - docs/superpowers/specs/2026-04-01-docsmith-phase1-design.md
138
+ - docsmith_spec.md
139
+ - lib/docsmith.rb
140
+ - lib/docsmith/auto_save.rb
141
+ - lib/docsmith/comments/anchor.rb
142
+ - lib/docsmith/comments/comment.rb
143
+ - lib/docsmith/comments/manager.rb
144
+ - lib/docsmith/comments/migrator.rb
145
+ - lib/docsmith/configuration.rb
146
+ - lib/docsmith/diff.rb
147
+ - lib/docsmith/diff/engine.rb
148
+ - lib/docsmith/diff/parsers/html.rb
149
+ - lib/docsmith/diff/parsers/markdown.rb
150
+ - lib/docsmith/diff/renderers.rb
151
+ - lib/docsmith/diff/renderers/base.rb
152
+ - lib/docsmith/diff/renderers/registry.rb
153
+ - lib/docsmith/diff/result.rb
154
+ - lib/docsmith/document.rb
155
+ - lib/docsmith/document_version.rb
156
+ - lib/docsmith/errors.rb
157
+ - lib/docsmith/events/event.rb
158
+ - lib/docsmith/events/hook_registry.rb
159
+ - lib/docsmith/events/notifier.rb
160
+ - lib/docsmith/rendering/html_renderer.rb
161
+ - lib/docsmith/rendering/json_renderer.rb
162
+ - lib/docsmith/version.rb
163
+ - lib/docsmith/version_manager.rb
164
+ - lib/docsmith/version_tag.rb
165
+ - lib/docsmith/versionable.rb
166
+ - lib/generators/docsmith/install/install_generator.rb
167
+ - lib/generators/docsmith/install/templates/create_docsmith_tables.rb.erb
168
+ - lib/generators/docsmith/install/templates/docsmith_initializer.rb.erb
169
+ - sig/docsmith.rbs
170
+ homepage: https://www.altcipher.com
171
+ licenses:
172
+ - MIT
173
+ metadata:
174
+ homepage_uri: https://www.altcipher.com
175
+ source_code_uri: https://github.com/swastik009/docsmith
176
+ changelog_uri: https://github.com/swastik009/docsmith/blob/main/CHANGELOG.md
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: 3.1.0
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubygems_version: 3.3.27
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Plug-and-play document version manager for Rails
196
+ test_files: []