collavre_github 0.3.2 → 0.4.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: a4d2aaf8fc69f915117cc6d8e60795cff69e4e89d356c32a5208d66d887916c4
4
- data.tar.gz: 2ad91c75b03292b131b9a2d101f81e5352ccfe97c33247cddca9305087fe9f2a
3
+ metadata.gz: 198ebcc760561f4ac05a67a9218db8e327bc4655cb89d412f10a5e674e2a18ed
4
+ data.tar.gz: 2ef18344c5388d0573baf3164c71ff99c891380e1c79b65284d69c8df3722221
5
5
  SHA512:
6
- metadata.gz: 23fcf2ea9ce240dfc2733a386effa67edab5c707ddffadf8470be025b8f31d78f5c60e90c3db3e42088f7030bcaa6735995b304e692a0c56eac56b28e4de8bb2
7
- data.tar.gz: dcbe67b5f301ee175d10c50b3a095965d663d52c8ad502b7aa89cd2ef9b11171a5ed4f8bc4b68cede2758aadd05b2dd2e1394bf487578d29a3bdb71fad5e159a
6
+ metadata.gz: ae9f937c945668c02e65e7befc7b9ada33990db0723cb32497a1526bdd601b812575408c398d5e6e34c3afe48b5a7b0d729a5ab9e63a5530f18d5dffc53f6097
7
+ data.tar.gz: 7486890574e6f60b3644885f0dc8f2de2b406725985df31547745b6130fbc3d17961500f9a2245b074342744c3d5d00632e9c2430fbd3b0987e30c276a3a3367
@@ -21,7 +21,38 @@ module CollavreGithub
21
21
  account.avatar_url = auth.info.image
22
22
  account.save!
23
23
 
24
- redirect_to collavre.creatives_path, notice: I18n.t("collavre_github.auth.connected")
24
+ creative_id = session.delete(:github_creative_id)
25
+ if creative_id.present?
26
+ redirect_to setup_auth_path(creative_id: creative_id)
27
+ else
28
+ redirect_to collavre.creatives_path, notice: I18n.t("collavre_github.auth.connected")
29
+ end
30
+ end
31
+
32
+ def setup
33
+ @creative_id = params[:creative_id]
34
+ unless @creative_id.present?
35
+ render plain: I18n.t("collavre_github.integration.missing_creative"), status: :bad_request
36
+ return
37
+ end
38
+
39
+ @creative = Collavre::Creative.find_by(id: @creative_id)
40
+ unless @creative
41
+ render plain: I18n.t("collavre_github.integration.missing_creative"), status: :not_found
42
+ return
43
+ end
44
+
45
+ unless @creative.has_permission?(Current.user, :admin)
46
+ render plain: I18n.t("collavre_github.errors.forbidden"), status: :forbidden
47
+ return
48
+ end
49
+
50
+ render layout: false
51
+ end
52
+
53
+ def store_creative
54
+ session[:github_creative_id] = params[:creative_id]
55
+ head :ok
25
56
  end
26
57
  end
27
58
  end
@@ -28,30 +28,12 @@ module CollavreGithub
28
28
 
29
29
  content = format_github_event(event, payload)
30
30
 
31
- comment = creative.comments.create!(
31
+ creative.comments.create!(
32
32
  user: nil,
33
33
  content: content,
34
34
  private: false
35
35
  )
36
-
37
- # Dispatch event for AI Agent routing
38
- Collavre::SystemEvents::Dispatcher.dispatch("comment_created", {
39
- comment: {
40
- id: comment.id,
41
- content: comment.content,
42
- user_id: nil
43
- },
44
- creative: {
45
- id: creative.id,
46
- description: creative.description
47
- },
48
- topic: {
49
- id: comment.topic_id
50
- },
51
- chat: {
52
- content: comment.content
53
- }
54
- })
36
+ # Dispatch handled by Comment#after_create_commit (skipped for system messages with user: nil)
55
37
  end
56
38
 
57
39
  def format_github_event(event, payload)
@@ -1,7 +1,12 @@
1
1
  module CollavreGithub
2
2
  class Client
3
+ MOCK_SERVER_DEFAULT = "http://localhost:4567"
4
+
3
5
  def initialize(account)
4
- @client = Octokit::Client.new(access_token: account.token)
6
+ options = { access_token: account.token }
7
+ api_endpoint = resolve_api_endpoint
8
+ options[:api_endpoint] = api_endpoint if api_endpoint.present?
9
+ @client = Octokit::Client.new(options)
5
10
  @client.auto_paginate = true
6
11
  end
7
12
 
@@ -106,5 +111,15 @@ module CollavreGithub
106
111
  private
107
112
 
108
113
  attr_reader :client
114
+
115
+ # Use GITHUB_API_ENDPOINT env var if set, otherwise fall back to mock server
116
+ # in development when no real GitHub credentials are configured.
117
+ def resolve_api_endpoint
118
+ return ENV["GITHUB_API_ENDPOINT"] if ENV["GITHUB_API_ENDPOINT"].present?
119
+
120
+ if Rails.env.development? && ENV["GITHUB_CLIENT_ID"].blank?
121
+ MOCK_SERVER_DEFAULT
122
+ end
123
+ end
109
124
  end
110
125
  end
@@ -0,0 +1,389 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="csrf-token" content="<%= form_authenticity_token %>">
7
+ <title><%= t('collavre_github.integration.title') %></title>
8
+ <style>
9
+ * { box-sizing: border-box; margin: 0; padding: 0; }
10
+ body {
11
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
12
+ background: #f5f5f5;
13
+ color: #333;
14
+ padding: 2em;
15
+ line-height: 1.5;
16
+ }
17
+ .wizard-container {
18
+ max-width: 520px;
19
+ margin: 0 auto;
20
+ background: #fff;
21
+ border-radius: 12px;
22
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
23
+ padding: 2em;
24
+ }
25
+ h2 { margin-bottom: 0.5em; font-size: 1.3em; }
26
+ .step { display: none; }
27
+ .step.active { display: block; }
28
+ .step-subtext { color: #666; margin-bottom: 1em; font-size: 0.95em; }
29
+ .list-box {
30
+ max-height: 280px;
31
+ overflow-y: auto;
32
+ border: 1px solid #ddd;
33
+ border-radius: 8px;
34
+ padding: 0.5em;
35
+ margin-bottom: 1em;
36
+ }
37
+ .list-item {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 0.5em;
41
+ padding: 0.5em 0.4em;
42
+ cursor: pointer;
43
+ border-radius: 4px;
44
+ }
45
+ .list-item:hover { background: #f0f0f0; }
46
+ .list-item input { flex-shrink: 0; }
47
+ .btn {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ padding: 0.6em 1.2em;
52
+ border-radius: 6px;
53
+ border: 1px solid #ccc;
54
+ background: #fff;
55
+ cursor: pointer;
56
+ font-size: 0.95em;
57
+ transition: background 0.15s;
58
+ }
59
+ .btn:hover { background: #f0f0f0; }
60
+ .btn-primary { background: #2563eb; color: #fff; border-color: #2563eb; }
61
+ .btn-primary:hover { background: #1d4ed8; }
62
+ .btn-primary:disabled { background: #93c5fd; cursor: not-allowed; }
63
+ .footer { display: flex; justify-content: space-between; margin-top: 1.5em; }
64
+ .footer-right { display: flex; gap: 0.5em; margin-left: auto; }
65
+ .error-msg { color: #dc2626; font-weight: 600; margin: 0.5em 0; display: none; }
66
+ .success-msg { color: #16a34a; font-weight: 600; margin: 0.5em 0; text-align: center; }
67
+ .loading { text-align: center; padding: 2em; color: #999; }
68
+ .summary-list { padding-left: 1.2em; }
69
+ .summary-list li { margin-bottom: 0.3em; }
70
+ .webhook-detail { margin-top: 0.3em; font-size: 0.85em; }
71
+ .webhook-detail code { background: #f1f5f9; padding: 0.2em 0.4em; border-radius: 3px; word-break: break-all; }
72
+ .close-btn { margin-top: 1em; width: 100%; }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div class="wizard-container" id="wizard"
77
+ data-creative-id="<%= @creative_id %>"
78
+ data-success-message="<%= t('collavre_github.integration.saved') %>"
79
+ data-webhook-url-label="<%= t('collavre_github.integration.webhook_url_label') %>"
80
+ data-webhook-secret-label="<%= t('collavre_github.integration.webhook_secret_label') %>"
81
+ data-no-orgs="<%= t('collavre_github.setup.no_orgs') %>"
82
+ data-no-repos="<%= t('collavre_github.setup.no_repos') %>"
83
+ data-load-orgs-error="<%= t('collavre_github.setup.load_orgs_error') %>"
84
+ data-load-repos-error="<%= t('collavre_github.setup.load_repos_error') %>"
85
+ data-save-error="<%= t('collavre_github.setup.save_error') %>">
86
+ <h2><%= t('collavre_github.integration.title') %></h2>
87
+
88
+ <!-- Step 1: Organization -->
89
+ <div class="step active" id="step-organization">
90
+ <p class="step-subtext"><%= t('collavre_github.integration.choose_org') %></p>
91
+ <div id="org-list" class="list-box">
92
+ <div class="loading"><%= t('collavre_github.setup.loading', default: 'Loading...') %></div>
93
+ </div>
94
+ </div>
95
+
96
+ <!-- Step 2: Repositories -->
97
+ <div class="step" id="step-repositories">
98
+ <p class="step-subtext"><%= t('collavre_github.integration.choose_repo') %></p>
99
+ <div id="repo-list" class="list-box">
100
+ <div class="loading"><%= t('collavre_github.setup.loading', default: 'Loading...') %></div>
101
+ </div>
102
+ </div>
103
+
104
+ <!-- Step 3: Summary -->
105
+ <div class="step" id="step-summary">
106
+ <p class="step-subtext"><%= t('collavre_github.integration.summary') %></p>
107
+ <p id="webhook-instructions" class="step-subtext" style="display:none;"><%= t('collavre_github.integration.webhook_instructions') %></p>
108
+ <ul id="summary-repos" class="summary-list"></ul>
109
+ <p id="summary-empty" style="display:none;color:#999;"><%= t('collavre_github.integration.summary_empty') %></p>
110
+ </div>
111
+
112
+ <!-- Step 4: Done -->
113
+ <div class="step" id="step-done">
114
+ <p class="success-msg"><%= t('collavre_github.integration.saved') %></p>
115
+ <button type="button" id="close-btn" class="btn btn-primary close-btn"><%= t('collavre_github.setup.close', default: 'Close') %></button>
116
+ </div>
117
+
118
+ <div id="wizard-error" class="error-msg"></div>
119
+
120
+ <div class="footer" id="wizard-footer">
121
+ <button type="button" id="prev-btn" class="btn" style="display:none;"><%= t('app.previous', default: 'Previous') %></button>
122
+ <div class="footer-right">
123
+ <button type="button" id="next-btn" class="btn btn-primary" disabled><%= t('app.next', default: 'Next') %></button>
124
+ <button type="button" id="finish-btn" class="btn btn-primary" style="display:none;"><%= t('app.finish', default: 'Finish') %></button>
125
+ </div>
126
+ </div>
127
+ </div>
128
+
129
+ <script>
130
+ (function() {
131
+ var wizard = document.getElementById('wizard');
132
+ var creativeId = wizard.dataset.creativeId;
133
+ var successMessage = wizard.dataset.successMessage;
134
+ var webhookUrlLabel = wizard.dataset.webhookUrlLabel;
135
+ var webhookSecretLabel = wizard.dataset.webhookSecretLabel;
136
+ var noOrgsMsg = wizard.dataset.noOrgs;
137
+ var noReposMsg = wizard.dataset.noRepos;
138
+ var loadOrgsError = wizard.dataset.loadOrgsError;
139
+ var loadReposError = wizard.dataset.loadReposError;
140
+ var saveErrorMsg = wizard.dataset.saveError;
141
+
142
+ var steps = ['organization', 'repositories', 'summary', 'done'];
143
+ var currentStepIndex = 0;
144
+ var selectedOrg = null;
145
+ var selectedRepos = new Set();
146
+ var webhookDetails = {};
147
+
148
+ var prevBtn = document.getElementById('prev-btn');
149
+ var nextBtn = document.getElementById('next-btn');
150
+ var finishBtn = document.getElementById('finish-btn');
151
+ var errorEl = document.getElementById('wizard-error');
152
+ var footer = document.getElementById('wizard-footer');
153
+
154
+ function csrfToken() {
155
+ return document.querySelector('meta[name="csrf-token"]')?.content;
156
+ }
157
+
158
+ function showStep(index) {
159
+ currentStepIndex = index;
160
+ steps.forEach(function(s, i) {
161
+ var el = document.getElementById('step-' + s);
162
+ if (el) el.className = 'step' + (i === index ? ' active' : '');
163
+ });
164
+
165
+ if (steps[index] === 'done') {
166
+ footer.style.display = 'none';
167
+ return;
168
+ }
169
+ footer.style.display = 'flex';
170
+
171
+ prevBtn.style.display = index > 0 ? 'inline-flex' : 'none';
172
+
173
+ if (steps[index] === 'summary') {
174
+ nextBtn.style.display = 'none';
175
+ finishBtn.style.display = 'inline-flex';
176
+ updateSummary();
177
+ } else {
178
+ nextBtn.style.display = 'inline-flex';
179
+ finishBtn.style.display = 'none';
180
+ nextBtn.disabled = (steps[index] === 'organization' && !selectedOrg);
181
+ }
182
+ }
183
+
184
+ function showError(msg) {
185
+ errorEl.textContent = msg;
186
+ errorEl.style.display = 'block';
187
+ }
188
+
189
+ function clearError() {
190
+ errorEl.textContent = '';
191
+ errorEl.style.display = 'none';
192
+ }
193
+
194
+ function loadOrganizations() {
195
+ var orgList = document.getElementById('org-list');
196
+ orgList.innerHTML = '<div class="loading">Loading...</div>';
197
+
198
+ fetch('/github/account/organizations', { headers: { Accept: 'application/json' } })
199
+ .then(function(r) { return r.json(); })
200
+ .then(function(data) {
201
+ var orgs = data.organizations || [];
202
+ orgList.innerHTML = '';
203
+ if (!orgs.length) {
204
+ orgList.innerHTML = '<p style="padding:0.5em;color:#999;">' + noOrgsMsg + '</p>';
205
+ return;
206
+ }
207
+ orgs.forEach(function(org) {
208
+ var label = document.createElement('label');
209
+ label.className = 'list-item';
210
+
211
+ var input = document.createElement('input');
212
+ input.type = 'radio';
213
+ input.name = 'org';
214
+ input.value = org.login;
215
+ input.addEventListener('change', function() {
216
+ selectedOrg = org.login;
217
+ nextBtn.disabled = false;
218
+ });
219
+
220
+ var span = document.createElement('span');
221
+ span.textContent = org.name || org.login;
222
+
223
+ label.appendChild(input);
224
+ label.appendChild(span);
225
+ orgList.appendChild(label);
226
+ });
227
+ })
228
+ .catch(function() {
229
+ orgList.innerHTML = '';
230
+ showError(loadOrgsError);
231
+ });
232
+ }
233
+
234
+ function loadRepositories() {
235
+ var repoList = document.getElementById('repo-list');
236
+ repoList.innerHTML = '<div class="loading">Loading...</div>';
237
+
238
+ var params = new URLSearchParams({ organization: selectedOrg });
239
+ if (creativeId) params.append('creative_id', creativeId);
240
+
241
+ fetch('/github/account/repositories?' + params.toString(), { headers: { Accept: 'application/json' } })
242
+ .then(function(r) { return r.json(); })
243
+ .then(function(data) {
244
+ var repos = data.repositories || [];
245
+ repoList.innerHTML = '';
246
+ if (!repos.length) {
247
+ repoList.innerHTML = '<p style="padding:0.5em;color:#999;">' + noReposMsg + '</p>';
248
+ return;
249
+ }
250
+ repos.forEach(function(repo) {
251
+ var label = document.createElement('label');
252
+ label.className = 'list-item';
253
+
254
+ var input = document.createElement('input');
255
+ input.type = 'checkbox';
256
+ input.value = repo.full_name;
257
+ input.checked = selectedRepos.has(repo.full_name) || repo.selected;
258
+ if (input.checked) selectedRepos.add(repo.full_name);
259
+ input.addEventListener('change', function() {
260
+ if (input.checked) {
261
+ selectedRepos.add(repo.full_name);
262
+ } else {
263
+ selectedRepos.delete(repo.full_name);
264
+ }
265
+ });
266
+
267
+ var span = document.createElement('span');
268
+ span.textContent = repo.full_name;
269
+
270
+ label.appendChild(input);
271
+ label.appendChild(span);
272
+ repoList.appendChild(label);
273
+ });
274
+ })
275
+ .catch(function() {
276
+ repoList.innerHTML = '';
277
+ showError(loadReposError);
278
+ });
279
+ }
280
+
281
+ function updateSummary() {
282
+ var summaryList = document.getElementById('summary-repos');
283
+ var summaryEmpty = document.getElementById('summary-empty');
284
+ var instructions = document.getElementById('webhook-instructions');
285
+ summaryList.innerHTML = '';
286
+
287
+ var repos = Array.from(selectedRepos);
288
+ if (!repos.length) {
289
+ summaryEmpty.style.display = 'block';
290
+ if (instructions) instructions.style.display = 'none';
291
+ return;
292
+ }
293
+ summaryEmpty.style.display = 'none';
294
+ if (instructions) instructions.style.display = 'block';
295
+
296
+ repos.forEach(function(fullName) {
297
+ var li = document.createElement('li');
298
+ var strong = document.createElement('strong');
299
+ strong.textContent = fullName;
300
+ li.appendChild(strong);
301
+
302
+ var details = webhookDetails[fullName];
303
+ if (details) {
304
+ if (details.url) {
305
+ var urlDiv = document.createElement('div');
306
+ urlDiv.className = 'webhook-detail';
307
+ urlDiv.innerHTML = '<span style="font-weight:600;">' + webhookUrlLabel + ': </span><code>' + details.url + '</code>';
308
+ li.appendChild(urlDiv);
309
+ }
310
+ if (details.secret) {
311
+ var secretDiv = document.createElement('div');
312
+ secretDiv.className = 'webhook-detail';
313
+ secretDiv.innerHTML = '<span style="font-weight:600;">' + webhookSecretLabel + ': </span><code>' + details.secret + '</code>';
314
+ li.appendChild(secretDiv);
315
+ }
316
+ }
317
+ summaryList.appendChild(li);
318
+ });
319
+ }
320
+
321
+ function saveSelection() {
322
+ clearError();
323
+ var payload = { repositories: Array.from(selectedRepos) };
324
+
325
+ fetch('/github/creatives/' + creativeId + '/integration', {
326
+ method: 'PATCH',
327
+ headers: {
328
+ 'Content-Type': 'application/json',
329
+ 'X-CSRF-Token': csrfToken()
330
+ },
331
+ body: JSON.stringify(payload)
332
+ })
333
+ .then(function(r) { return r.json().then(function(body) { return { ok: r.ok, body: body }; }); })
334
+ .then(function(result) {
335
+ if (!result.ok) {
336
+ showError(result.body.error || saveErrorMsg);
337
+ return;
338
+ }
339
+ webhookDetails = result.body.webhooks || {};
340
+
341
+ // Notify parent window and show done step
342
+ notifyParent();
343
+ showStep(steps.indexOf('done'));
344
+ })
345
+ .catch(function() {
346
+ showError(saveErrorMsg);
347
+ });
348
+ }
349
+
350
+ function notifyParent() {
351
+ try {
352
+ if (window.opener) {
353
+ window.opener.postMessage({ type: 'githubConnected' }, window.location.origin);
354
+ }
355
+ } catch (e) {
356
+ // Ignore cross-origin errors
357
+ }
358
+ }
359
+
360
+ // Navigation
361
+ prevBtn.addEventListener('click', function() {
362
+ clearError();
363
+ if (currentStepIndex > 0) {
364
+ showStep(currentStepIndex - 1);
365
+ if (steps[currentStepIndex] === 'repositories') loadRepositories();
366
+ }
367
+ });
368
+
369
+ nextBtn.addEventListener('click', function() {
370
+ clearError();
371
+ var nextIndex = currentStepIndex + 1;
372
+ showStep(nextIndex);
373
+ if (steps[nextIndex] === 'repositories') loadRepositories();
374
+ });
375
+
376
+ finishBtn.addEventListener('click', function() {
377
+ saveSelection();
378
+ });
379
+
380
+ document.getElementById('close-btn').addEventListener('click', function() {
381
+ window.close();
382
+ });
383
+
384
+ // Initial load
385
+ loadOrganizations();
386
+ })();
387
+ </script>
388
+ </body>
389
+ </html>
@@ -23,6 +23,14 @@ en:
23
23
  summary: "The following repositories will be linked to this Creative:"
24
24
  webhook_instructions: "Configure each repository with the webhook details below."
25
25
  summary_empty: "No repositories selected."
26
+ setup:
27
+ loading: "Loading..."
28
+ close: "Close"
29
+ no_orgs: "No organizations found."
30
+ no_repos: "No repositories found."
31
+ load_orgs_error: "Failed to load organizations."
32
+ load_repos_error: "Failed to load repositories."
33
+ save_error: "Failed to save integration."
26
34
  auth:
27
35
  login_first: "Please log in first to connect your GitHub account"
28
36
  connected: "GitHub account connected successfully"
@@ -23,6 +23,14 @@ ko:
23
23
  summary: "다음 저장소가 이 Creative에 연동됩니다:"
24
24
  webhook_instructions: "각 저장소에 아래 웹훅 정보를 설정하세요."
25
25
  summary_empty: "선택된 저장소가 없습니다."
26
+ setup:
27
+ loading: "불러오는 중..."
28
+ close: "닫기"
29
+ no_orgs: "조직을 찾을 수 없습니다."
30
+ no_repos: "저장소를 찾을 수 없습니다."
31
+ load_orgs_error: "조직 목록을 불러오지 못했습니다."
32
+ load_repos_error: "저장소 목록을 불러오지 못했습니다."
33
+ save_error: "연동 저장에 실패했습니다."
26
34
  auth:
27
35
  login_first: "GitHub 계정을 연결하려면 먼저 로그인하세요"
28
36
  connected: "GitHub 계정이 연결되었습니다"
data/config/routes.rb CHANGED
@@ -4,6 +4,10 @@ CollavreGithub::Engine.routes.draw do
4
4
  post "webhook", to: "webhooks#create", as: :webhook
5
5
  post "webhooks", to: "webhooks#create"
6
6
 
7
+ # OAuth setup flow (popup wizard after callback)
8
+ get "auth/setup", to: "auth#setup", as: :setup_auth
9
+ post "auth/store_creative", to: "auth#store_creative", as: :store_creative_auth
10
+
7
11
  # Account endpoints
8
12
  resource :account, only: [ :show ] do
9
13
  get :organizations
@@ -0,0 +1,5 @@
1
+ class RemoveGithubGeminiPromptFromCreatives < ActiveRecord::Migration[8.1]
2
+ def change
3
+ remove_column :creatives, :github_gemini_prompt, :text
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module CollavreGithub
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :collavre_github do
4
+ desc "Start GitHub API mock server for development (port: GITHUB_MOCK_PORT, default 4567)"
5
+ task :mock_server do
6
+ load CollavreGithub::Engine.root.join("script/mock_server.rb")
7
+ end
8
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collavre_github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collavre
@@ -75,6 +75,7 @@ files:
75
75
  - app/services/collavre_github/tools/github_pr_details_service.rb
76
76
  - app/services/collavre_github/tools/github_pr_diff_service.rb
77
77
  - app/services/collavre_github/webhook_provisioner.rb
78
+ - app/views/collavre_github/auth/setup.html.erb
78
79
  - app/views/collavre_github/integrations/_modal.html.erb
79
80
  - config/locales/en.yml
80
81
  - config/locales/ko.yml
@@ -82,10 +83,12 @@ files:
82
83
  - db/migrate/20250925000000_create_github_integrations.rb
83
84
  - db/migrate/20250927000000_add_webhook_secret_to_github_repository_links.rb
84
85
  - db/migrate/20250928105957_add_github_gemini_prompt_to_creatives.rb
86
+ - db/migrate/20260219095224_remove_github_gemini_prompt_from_creatives.rb
85
87
  - db/seeds.rb
86
88
  - lib/collavre_github.rb
87
89
  - lib/collavre_github/engine.rb
88
90
  - lib/collavre_github/version.rb
91
+ - lib/tasks/mock_server.rake
89
92
  homepage: https://collavre.com
90
93
  licenses:
91
94
  - AGPL