intent-record 1.0.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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +18 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +137 -0
  5. data/db/migrate/20260918000001_create_initial_schema.rb +68 -0
  6. data/exe/intent-record +5 -0
  7. data/lib/intent_record/application_record.rb +8 -0
  8. data/lib/intent_record/asset_version_normalizer.rb +43 -0
  9. data/lib/intent_record/asset_version_resolver.rb +76 -0
  10. data/lib/intent_record/cli/argv_parser.rb +78 -0
  11. data/lib/intent_record/cli/dispatch.rb +118 -0
  12. data/lib/intent_record/cli/stdin_json.rb +28 -0
  13. data/lib/intent_record/cli/streams.rb +9 -0
  14. data/lib/intent_record/cli/usage.rb +29 -0
  15. data/lib/intent_record/cli.rb +69 -0
  16. data/lib/intent_record/commands/attach.rb +39 -0
  17. data/lib/intent_record/commands/by_source.rb +52 -0
  18. data/lib/intent_record/commands/lookup.rb +22 -0
  19. data/lib/intent_record/commands/recent.rb +21 -0
  20. data/lib/intent_record/commands/record.rb +52 -0
  21. data/lib/intent_record/commands/search.rb +50 -0
  22. data/lib/intent_record/commands/show.rb +19 -0
  23. data/lib/intent_record/commands/systems.rb +16 -0
  24. data/lib/intent_record/config.rb +51 -0
  25. data/lib/intent_record/database.rb +74 -0
  26. data/lib/intent_record/formatter.rb +59 -0
  27. data/lib/intent_record/global_id.rb +22 -0
  28. data/lib/intent_record/input_validator.rb +69 -0
  29. data/lib/intent_record/like_pattern.rb +34 -0
  30. data/lib/intent_record/linkers/asset_version_linker.rb +43 -0
  31. data/lib/intent_record/linkers/asset_version_specs.rb +30 -0
  32. data/lib/intent_record/linkers/intent_linker.rb +35 -0
  33. data/lib/intent_record/linkers/stakeholder_linker.rb +61 -0
  34. data/lib/intent_record/models/asset_version.rb +12 -0
  35. data/lib/intent_record/models/intent_record.rb +29 -0
  36. data/lib/intent_record/models/intent_record_asset_version.rb +11 -0
  37. data/lib/intent_record/models/intent_record_link.rb +22 -0
  38. data/lib/intent_record/models/stakeholder_reference.rb +11 -0
  39. data/lib/intent_record/models/stakeholder_source.rb +12 -0
  40. data/lib/intent_record/models/stakeholder_system.rb +10 -0
  41. data/lib/intent_record/models/vcs_system.rb +10 -0
  42. data/lib/intent_record/seeds.rb +38 -0
  43. data/lib/intent_record/sqlite_connection_setup.rb +22 -0
  44. data/lib/intent_record/stakeholder_normalizer.rb +33 -0
  45. data/lib/intent_record/version.rb +3 -0
  46. data/lib/intent_record/web/app.rb +54 -0
  47. data/lib/intent_record/web/boot.rb +51 -0
  48. data/lib/intent_record/web/public/styles.css +50 -0
  49. data/lib/intent_record/web/query_router.rb +25 -0
  50. data/lib/intent_record/web/routes/commits.rb +16 -0
  51. data/lib/intent_record/web/routes/home.rb +21 -0
  52. data/lib/intent_record/web/routes/intents.rb +15 -0
  53. data/lib/intent_record/web/routes/search.rb +17 -0
  54. data/lib/intent_record/web/routes/sources.rb +19 -0
  55. data/lib/intent_record/web/views/_intent_cards.erb +43 -0
  56. data/lib/intent_record/web/views/commit.erb +12 -0
  57. data/lib/intent_record/web/views/error.erb +7 -0
  58. data/lib/intent_record/web/views/home.erb +14 -0
  59. data/lib/intent_record/web/views/intent.erb +5 -0
  60. data/lib/intent_record/web/views/layout.erb +28 -0
  61. data/lib/intent_record/web/views/search.erb +5 -0
  62. data/lib/intent_record/web/views/sources.erb +16 -0
  63. data/lib/intent_record.rb +28 -0
  64. metadata +194 -0
@@ -0,0 +1,19 @@
1
+ require_relative "../../commands/by_source"
2
+
3
+ module IntentRecord
4
+ module Web
5
+ module Routes
6
+ module Sources
7
+ EMPTY = { "intents" => [], "asset_versions" => [], "sources" => [] }.freeze
8
+
9
+ def self.registered(app)
10
+ app.get "/sources" do
11
+ uri = params[:uri].to_s
12
+ result = uri.strip.empty? ? EMPTY : Commands::BySource.new(uri: uri, contains: true).call
13
+ erb :sources, locals: { uri: uri, intents: result["intents"], versions: result["asset_versions"] }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ <% if records.empty? %>
2
+ <p class="empty">No intent records found.</p>
3
+ <% end %>
4
+ <% records.each do |r| %>
5
+ <article class="card intent">
6
+ <header>
7
+ <h2><a href="/intents/<%= h r["intent_id"] %>"><%= h r["summary"] %></a></h2>
8
+ <div class="meta">
9
+ <% if r["author"] %><span class="author"><%= h r["author"] %></span> · <% end %>
10
+ <span class="timestamp"><%= h r["created_at"] %></span>
11
+ · <code class="id"><%= h r["intent_id"] %></code>
12
+ </div>
13
+ </header>
14
+ <div class="body"><%= h r["body"] %></div>
15
+ <% if r["asset_versions"].any? %>
16
+ <div class="links">
17
+ <span class="label">Commits</span>
18
+ <% r["asset_versions"].each do |v| %>
19
+ <a class="chip" href="/commits/<%= h v["external_id"] %>?vcs=<%= h v["vcs"] %>"><%= h v["vcs"] %> <code><%= h v["external_id"][0, 12] %></code></a>
20
+ <% end %>
21
+ </div>
22
+ <% end %>
23
+ <% if r["stakeholder_references"].any? %>
24
+ <div class="links">
25
+ <span class="label">Stakeholders</span>
26
+ <% r["stakeholder_references"].each do |s| %>
27
+ <a class="chip ext" href="<%= h s["uri"] %>" target="_blank" rel="noopener"><%= h s["system"] %>: <%= h(s["title"] || s["uri"]) %></a>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
31
+ <% if r["related_intents"].any? || r["related_by_intents"].any? %>
32
+ <div class="links">
33
+ <span class="label">Related</span>
34
+ <% r["related_intents"].each do |x| %>
35
+ <a class="chip" href="/intents/<%= h x["intent_id"] %>">builds on: <%= h x["summary"] %></a>
36
+ <% end %>
37
+ <% r["related_by_intents"].each do |x| %>
38
+ <a class="chip" href="/intents/<%= h x["intent_id"] %>">followed by: <%= h x["summary"] %></a>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
42
+ </article>
43
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <header>
2
+ <h1><%= h version["vcs"] %> <code><%= h version["external_id"] %></code></h1>
3
+ <% if intents.size > 1 %>
4
+ <p class="subtitle"><%= intents.size %> intent records for this asset version, recorded in this order. Later records usually refine earlier ones.</p>
5
+ <% else %>
6
+ <p class="subtitle"><%= intents.size %> intent record for this asset version</p>
7
+ <% end %>
8
+ </header>
9
+ <% intents.each_with_index do |intent, i| %>
10
+ <% if intents.size > 1 %><h3><%= i + 1 %> of <%= intents.size %></h3><% end %>
11
+ <%= intent_cards([intent]) %>
12
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <header>
2
+ <h1><%= h title %></h1>
3
+ <p class="subtitle"><%= h message %></p>
4
+ </header>
5
+ <section class="card muted">
6
+ <p>No intent records could be shown. Try a longer commit prefix, a ticket URL, or a few words from the change.</p>
7
+ </section>
@@ -0,0 +1,14 @@
1
+ <header>
2
+ <h1>What was this change for?</h1>
3
+ <p class="subtitle">Paste a commit hash above to read the intent recorded against it. Ticket URLs and free text work too.</p>
4
+ </header>
5
+ <% if recent.empty? %>
6
+ <section class="card muted">
7
+ <h2>Nothing recorded yet</h2>
8
+ <p>Agents record intent from the CLI:</p>
9
+ <pre>echo '{"summary": "Add retry to fetcher", "body": "Why: ...", "commits": ["&lt;git hash&gt;"]}' | intent-record record</pre>
10
+ </section>
11
+ <% else %>
12
+ <h3>Recent intents</h3>
13
+ <%= intent_cards(recent) %>
14
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <header>
2
+ <h1><%= h intent["summary"] %></h1>
3
+ <p class="subtitle">Intent record <code><%= h intent["intent_id"] %></code></p>
4
+ </header>
5
+ <%= intent_cards([intent]) %>
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>intent-record</title>
7
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22><circle cx=%228%22 cy=%228%22 r=%227%22 fill=%22%239BC4E2%22/></svg>">
8
+ <link rel="stylesheet" href="/styles.css">
9
+ </head>
10
+ <body>
11
+ <header class="topnav">
12
+ <a class="brand" href="/">intent-record</a>
13
+ <form class="lookup" method="get" action="/go">
14
+ <input type="text" name="q" placeholder="Commit hash, ticket URL, or words" value="<%= h(params[:q] || params[:uri] || "") %>" autofocus>
15
+ <button type="submit">Look up</button>
16
+ </form>
17
+ <div class="who">localhost</div>
18
+ </header>
19
+ <main class="page">
20
+ <%= yield %>
21
+ </main>
22
+ <footer class="footer">
23
+ Records are written by agents and people from the command line:
24
+ <code>echo '{"summary": "...", "body": "...", "commits": ["&lt;hash&gt;"]}' | intent-record record</code>
25
+ · run <code>intent-record --help</code> for the rest.
26
+ </footer>
27
+ </body>
28
+ </html>
@@ -0,0 +1,5 @@
1
+ <header>
2
+ <h1>Search: <%= h query %></h1>
3
+ <p class="subtitle">Intents whose summary, body, or linked ticket URL or title contain every word</p>
4
+ </header>
5
+ <%= intent_cards(intents) %>
@@ -0,0 +1,16 @@
1
+ <header>
2
+ <h1>Stakeholder source: <%= h uri %></h1>
3
+ <p class="subtitle">What was built for this, and why. Matches any source whose URI contains this text.</p>
4
+ </header>
5
+ <% if versions.any? %>
6
+ <section class="card tint">
7
+ <h2>Commits</h2>
8
+ <ul class="commit-list">
9
+ <% versions.each do |v| %>
10
+ <li><span class="chip"><%= h v["vcs"] %></span> <a href="/commits/<%= h v["external_id"] %>?vcs=<%= h v["vcs"] %>" class="commit-list-item"><%= h v["external_id"] %></a></li>
11
+ <% end %>
12
+ </ul>
13
+ </section>
14
+ <% end %>
15
+ <h3>Intents</h3>
16
+ <%= intent_cards(intents) %>
@@ -0,0 +1,28 @@
1
+ require "active_record"
2
+ require "active_support"
3
+ require "json"
4
+ require "time"
5
+
6
+ require "intent_record/version"
7
+
8
+ module IntentRecord
9
+ class Error < StandardError; end
10
+ class ValidationError < Error; end
11
+ class NotFoundError < Error; end
12
+ class ConfigError < Error; end
13
+ class DatabaseError < Error; end
14
+ end
15
+
16
+ require "intent_record/config"
17
+ require "intent_record/database"
18
+ require "intent_record/global_id"
19
+ require "intent_record/application_record"
20
+ require "intent_record/models/vcs_system"
21
+ require "intent_record/models/asset_version"
22
+ require "intent_record/models/intent_record"
23
+ require "intent_record/models/intent_record_asset_version"
24
+ require "intent_record/models/stakeholder_system"
25
+ require "intent_record/models/stakeholder_source"
26
+ require "intent_record/models/stakeholder_reference"
27
+ require "intent_record/models/intent_record_link"
28
+ require "intent_record/cli"
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: intent-record
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Erik T. Madsen
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '8.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '8.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activesupport
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '8.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '8.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: puma
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '6.4'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '6.4'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rackup
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: sinatra
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '4.0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '4.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: sqlite3
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.0'
96
+ description: A local, agent-first store that links the written intent behind a code
97
+ change to its VCS commit and to stakeholder systems such as Jira, Confluence and
98
+ Linear. JSON in, JSON out on the CLI, plus a small read-oriented web GUI. A Claude
99
+ Code skill that drives it is at https://github.com/beatmadsen/claude-skills/tree/main/skills/intent-record.
100
+ email: []
101
+ executables:
102
+ - intent-record
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - CHANGELOG.md
107
+ - LICENSE.txt
108
+ - README.md
109
+ - db/migrate/20260918000001_create_initial_schema.rb
110
+ - exe/intent-record
111
+ - lib/intent_record.rb
112
+ - lib/intent_record/application_record.rb
113
+ - lib/intent_record/asset_version_normalizer.rb
114
+ - lib/intent_record/asset_version_resolver.rb
115
+ - lib/intent_record/cli.rb
116
+ - lib/intent_record/cli/argv_parser.rb
117
+ - lib/intent_record/cli/dispatch.rb
118
+ - lib/intent_record/cli/stdin_json.rb
119
+ - lib/intent_record/cli/streams.rb
120
+ - lib/intent_record/cli/usage.rb
121
+ - lib/intent_record/commands/attach.rb
122
+ - lib/intent_record/commands/by_source.rb
123
+ - lib/intent_record/commands/lookup.rb
124
+ - lib/intent_record/commands/recent.rb
125
+ - lib/intent_record/commands/record.rb
126
+ - lib/intent_record/commands/search.rb
127
+ - lib/intent_record/commands/show.rb
128
+ - lib/intent_record/commands/systems.rb
129
+ - lib/intent_record/config.rb
130
+ - lib/intent_record/database.rb
131
+ - lib/intent_record/formatter.rb
132
+ - lib/intent_record/global_id.rb
133
+ - lib/intent_record/input_validator.rb
134
+ - lib/intent_record/like_pattern.rb
135
+ - lib/intent_record/linkers/asset_version_linker.rb
136
+ - lib/intent_record/linkers/asset_version_specs.rb
137
+ - lib/intent_record/linkers/intent_linker.rb
138
+ - lib/intent_record/linkers/stakeholder_linker.rb
139
+ - lib/intent_record/models/asset_version.rb
140
+ - lib/intent_record/models/intent_record.rb
141
+ - lib/intent_record/models/intent_record_asset_version.rb
142
+ - lib/intent_record/models/intent_record_link.rb
143
+ - lib/intent_record/models/stakeholder_reference.rb
144
+ - lib/intent_record/models/stakeholder_source.rb
145
+ - lib/intent_record/models/stakeholder_system.rb
146
+ - lib/intent_record/models/vcs_system.rb
147
+ - lib/intent_record/seeds.rb
148
+ - lib/intent_record/sqlite_connection_setup.rb
149
+ - lib/intent_record/stakeholder_normalizer.rb
150
+ - lib/intent_record/version.rb
151
+ - lib/intent_record/web/app.rb
152
+ - lib/intent_record/web/boot.rb
153
+ - lib/intent_record/web/public/styles.css
154
+ - lib/intent_record/web/query_router.rb
155
+ - lib/intent_record/web/routes/commits.rb
156
+ - lib/intent_record/web/routes/home.rb
157
+ - lib/intent_record/web/routes/intents.rb
158
+ - lib/intent_record/web/routes/search.rb
159
+ - lib/intent_record/web/routes/sources.rb
160
+ - lib/intent_record/web/views/_intent_cards.erb
161
+ - lib/intent_record/web/views/commit.erb
162
+ - lib/intent_record/web/views/error.erb
163
+ - lib/intent_record/web/views/home.erb
164
+ - lib/intent_record/web/views/intent.erb
165
+ - lib/intent_record/web/views/layout.erb
166
+ - lib/intent_record/web/views/search.erb
167
+ - lib/intent_record/web/views/sources.erb
168
+ homepage: https://github.com/beatmadsen/intent-record
169
+ licenses:
170
+ - MIT
171
+ metadata:
172
+ homepage_uri: https://github.com/beatmadsen/intent-record
173
+ changelog_uri: https://github.com/beatmadsen/intent-record/blob/main/CHANGELOG.md
174
+ bug_tracker_uri: https://github.com/beatmadsen/intent-record/issues
175
+ documentation_uri: https://github.com/beatmadsen/intent-record#readme
176
+ rubygems_mfa_required: 'true'
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '3.2'
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubygems_version: 4.0.9
192
+ specification_version: 4
193
+ summary: Local records storage for the expressed intent behind individual code changes.
194
+ test_files: []