gaggle 0.2.2
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +167 -0
- data/Rakefile +8 -0
- data/app/assets/config/gaggle_manifest.js +4 -0
- data/app/assets/stylesheets/application.css +1 -0
- data/app/assets/stylesheets/gaggle/tailwind.css +1740 -0
- data/app/controllers/gaggle/application_controller.rb +23 -0
- data/app/controllers/gaggle/channels/messages_controller.rb +27 -0
- data/app/controllers/gaggle/channels_controller.rb +84 -0
- data/app/controllers/gaggle/gooses/sessions_controller.rb +23 -0
- data/app/controllers/gaggle/gooses_controller.rb +47 -0
- data/app/controllers/gaggle/messages_controller.rb +7 -0
- data/app/controllers/gaggle/overviews_controller.rb +5 -0
- data/app/controllers/gaggle/sessions_controller.rb +27 -0
- data/app/helpers/gaggle/application_helper.rb +4 -0
- data/app/javascript/controllers/gaggle/application.js +9 -0
- data/app/javascript/controllers/gaggle/chat_field_form_controller.js +7 -0
- data/app/javascript/controllers/gaggle/index.js +4 -0
- data/app/javascript/controllers/gaggle/local_timestamp_controller.js +21 -0
- data/app/javascript/controllers/gaggle/text_area_auto_expand_controller.js +27 -0
- data/app/javascript/controllers/gaggle/transition_controller.js +102 -0
- data/app/javascript/gaggle/application.js +3 -0
- data/app/javascript/gaggle/el-transition.js +64 -0
- data/app/jobs/gaggle/application_job.rb +4 -0
- data/app/mailers/gaggle/application_mailer.rb +6 -0
- data/app/models/gaggle/application_record.rb +8 -0
- data/app/models/gaggle/channel.rb +45 -0
- data/app/models/gaggle/channel_goose.rb +11 -0
- data/app/models/gaggle/current.rb +5 -0
- data/app/models/gaggle/goose/personality_defaults.rb +87 -0
- data/app/models/gaggle/goose.rb +94 -0
- data/app/models/gaggle/message.rb +61 -0
- data/app/models/gaggle/notification.rb +47 -0
- data/app/models/gaggle/session.rb +138 -0
- data/app/views/gaggle/application/_logo.html.erb +125 -0
- data/app/views/gaggle/application/_mobile_header.html.erb +15 -0
- data/app/views/gaggle/application/_sidebar.html.erb +259 -0
- data/app/views/gaggle/channels/_channel.html.erb +22 -0
- data/app/views/gaggle/channels/_form.html.erb +70 -0
- data/app/views/gaggle/channels/edit.html.erb +10 -0
- data/app/views/gaggle/channels/gooses/index.html.erb +12 -0
- data/app/views/gaggle/channels/index.json.jbuilder +3 -0
- data/app/views/gaggle/channels/messages/new.html.erb +2 -0
- data/app/views/gaggle/channels/new.html.erb +8 -0
- data/app/views/gaggle/channels/show.html.erb +98 -0
- data/app/views/gaggle/channels/show.json.jbuilder +5 -0
- data/app/views/gaggle/channels/update.turbo_stream.erb +1 -0
- data/app/views/gaggle/gooses/_form.html.erb +65 -0
- data/app/views/gaggle/gooses/edit.html.erb +2 -0
- data/app/views/gaggle/gooses/index.json.jbuilder +5 -0
- data/app/views/gaggle/gooses/new.html.erb +2 -0
- data/app/views/gaggle/gooses/sessions/index.html.erb +55 -0
- data/app/views/gaggle/gooses/show.html.erb +41 -0
- data/app/views/gaggle/messages/_message.html.erb +30 -0
- data/app/views/gaggle/overviews/show.html.erb +17 -0
- data/app/views/gaggle/sessions/show.html.erb +76 -0
- data/app/views/layouts/gaggle/application.html.erb +19 -0
- data/config/importmap.rb +7 -0
- data/config/routes.rb +19 -0
- data/config/utility_classes.yml +22 -0
- data/db/gaggle_migrate/20250214180303_create_gaggle_tables.rb +53 -0
- data/db/gaggle_migrate/20250220002655_add_delivered_at_to_notification.rb +5 -0
- data/db/gaggle_migrate/20250220004428_create_join_table_gaggle_channel_gaggle_goose.rb +8 -0
- data/lib/gaggle/engine.rb +48 -0
- data/lib/gaggle/version.rb +3 -0
- data/lib/gaggle.rb +8 -0
- data/lib/generators/gaggle/install/install_generator.rb +7 -0
- data/lib/generators/gaggle/install/templates/db/gaggle_schema.rb +59 -0
- data/lib/generators/gaggle/update/update_generator.rb +16 -0
- metadata +269 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
include ActionView::RecordIdentifier
|
4
|
+
before_action :set_channels
|
5
|
+
before_action :set_geese
|
6
|
+
before_action :set_current_attributes
|
7
|
+
skip_before_action :verify_authenticity_token, if: :mcp_invocation?
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def set_channels
|
12
|
+
@channels = Gaggle::Channel.all
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_geese
|
16
|
+
@geese = Gaggle::Goose.all
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_current_attributes
|
20
|
+
Current.goose_user = Goose.find_by(id: params[:goose_user_id])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class Channels::MessagesController < Gaggle::ChannelsController
|
3
|
+
skip_before_action :verify_authenticity_token
|
4
|
+
|
5
|
+
permitted_params_for :create do
|
6
|
+
param :message, required: true do
|
7
|
+
param :content, type: :string, example: "Message Content", required: true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
tool_description_for :create, "Send a message to the channel"
|
12
|
+
def create
|
13
|
+
@message = @channel.messages.new(resource_params)
|
14
|
+
if @message.save
|
15
|
+
respond_to do |format|
|
16
|
+
format.html { redirect_to @channel, notice: "Message sent." }
|
17
|
+
format.json { render json: @message, status: :created, location: @message }
|
18
|
+
end
|
19
|
+
else
|
20
|
+
respond_to do |format|
|
21
|
+
format.html { render :new, notice: "Message was not sent." }
|
22
|
+
format.json { render json: @message.errors, status: :unprocessable_entity }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class ChannelsController < ApplicationController
|
3
|
+
before_action :set_channel, except: :index
|
4
|
+
|
5
|
+
permitted_params_for :create do
|
6
|
+
param :channel, required: true do
|
7
|
+
param :name, type: :string, example: "Channel Name", required: true
|
8
|
+
param :goose_ids, type: :array, item_type: :string, example: [ "1", "2" ]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
permitted_params_for :update do
|
13
|
+
param :channel, required: true do
|
14
|
+
param :name, type: :string, example: "Channel Name"
|
15
|
+
param :goose_ids, type: :array, item_type: :string, example: [ "1", "2" ]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
tool_description_for :index, "View all channels and their ids"
|
21
|
+
def index; end
|
22
|
+
|
23
|
+
tool_description_for :show, "View all messages in a channel"
|
24
|
+
def show
|
25
|
+
@notification = Current.goose_user&.notifications&.unread&.for_messageable(@channel)&.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def new; end
|
29
|
+
|
30
|
+
def create
|
31
|
+
@channel.assign_attributes(resource_params)
|
32
|
+
|
33
|
+
if @channel.save
|
34
|
+
respond_to do |format|
|
35
|
+
format.html { redirect_to @channel, notice: "Channel was successfully created." }
|
36
|
+
format.json { render json: @channel, status: :created, location: @channel }
|
37
|
+
end
|
38
|
+
else
|
39
|
+
respond_to do |format|
|
40
|
+
format.html { render :new }
|
41
|
+
format.json { render json: @channel.errors, status: :unprocessable_entity }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def edit; end
|
47
|
+
|
48
|
+
def update
|
49
|
+
if @channel.update(resource_params)
|
50
|
+
respond_to do |format|
|
51
|
+
format.html { redirect_to @channel, notice: "Channel was successfully updated." }
|
52
|
+
format.json { render json: { name: @channel.name, goose_ids: @channel.goose_ids }, status: :ok }
|
53
|
+
end
|
54
|
+
|
55
|
+
Turbo::StreamsChannel.broadcast_update_to "application",
|
56
|
+
targets: ".#{dom_id(@channel, :title)}",
|
57
|
+
content: @channel.name
|
58
|
+
else
|
59
|
+
respond_to do |format|
|
60
|
+
format.html { render :edit }
|
61
|
+
format.json { render json: @channel.errors, status: :unprocessable_entity }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy
|
67
|
+
@channel.destroy
|
68
|
+
respond_to do |format|
|
69
|
+
format.html { redirect_to root_url, notice: "Channel was successfully destroyed." }
|
70
|
+
format.json { render json: "#{@channel.name} was successfully destroyed.", status: :ok }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def set_channels
|
77
|
+
@channels = Gaggle::Channel.includes(:gooses)
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_channel
|
81
|
+
@channel = Gaggle::Channel.includes(messages: :goose).find_by(id: params[:channel_id] || params[:id]) || Gaggle::Channel.new
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class Gooses::SessionsController < SessionsController
|
3
|
+
before_action :set_goose
|
4
|
+
|
5
|
+
def index
|
6
|
+
@sessions = @goose.sessions
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
session = @goose.sessions.create!
|
11
|
+
session.start_executable
|
12
|
+
sleep 1.second
|
13
|
+
session.write_to_executable(@goose.interaction_prompt + @goose.prompt + @goose.begin_prompt)
|
14
|
+
redirect_to session
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def set_goose
|
20
|
+
@goose = Gaggle::Goose.find(params[:goose_id])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class GoosesController < ApplicationController
|
3
|
+
before_action :set_goose
|
4
|
+
|
5
|
+
def index
|
6
|
+
@gooses = Goose.where.not(id: Current.goose_user&.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def show; end
|
10
|
+
|
11
|
+
def new; end
|
12
|
+
|
13
|
+
def create
|
14
|
+
@goose.assign_attributes(goose_params)
|
15
|
+
if @goose.save
|
16
|
+
redirect_to @goose, notice: "Goose was successfully created."
|
17
|
+
else
|
18
|
+
render :new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def edit; end
|
23
|
+
|
24
|
+
def update
|
25
|
+
if @goose.update(goose_params)
|
26
|
+
redirect_to @goose, notice: "Goose was successfully updated."
|
27
|
+
else
|
28
|
+
render :edit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def destroy
|
33
|
+
@goose.destroy
|
34
|
+
redirect_to root_url, notice: "Goose was successfully destroyed."
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_goose
|
40
|
+
@goose = Goose.find_by(id: params[:id]) || Goose.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def goose_params
|
44
|
+
params.require(:goose).permit(:name, :prompt)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class SessionsController < ApplicationController
|
3
|
+
before_action :set_session, only: [ :show, :update, :destroy ]
|
4
|
+
|
5
|
+
def show; end
|
6
|
+
|
7
|
+
def update
|
8
|
+
@session.write_to_executable(session_params[:content])
|
9
|
+
redirect_to @session
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy
|
13
|
+
@session.stop_executable
|
14
|
+
redirect_to @session
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def set_session
|
20
|
+
@session = Gaggle::Session.find(params[:id])
|
21
|
+
end
|
22
|
+
|
23
|
+
def session_params
|
24
|
+
params.require(:session).permit(:content)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
// Import and register all your controllers from the importmap via controllers/**/*_controller
|
2
|
+
import { gaggleApplication } from "controllers/gaggle/application"
|
3
|
+
import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
|
4
|
+
lazyLoadControllersFrom("controllers/gaggle", gaggleApplication)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["display"]
|
5
|
+
static values = { utc: String }
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
this.updateTime()
|
9
|
+
}
|
10
|
+
|
11
|
+
updateTime() {
|
12
|
+
const date = new Date(this.utcValue)
|
13
|
+
const options = {
|
14
|
+
hour: 'numeric',
|
15
|
+
minute: '2-digit',
|
16
|
+
hour12: true
|
17
|
+
}
|
18
|
+
const formattedTime = date.toLocaleTimeString([], options)
|
19
|
+
this.displayTarget.textContent = formattedTime
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static values = {
|
5
|
+
maxRows: Number
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.adjustRows()
|
10
|
+
this.element.addEventListener('input', this.adjustRows.bind(this))
|
11
|
+
}
|
12
|
+
|
13
|
+
disconnect() {
|
14
|
+
this.element.removeEventListener('input', this.adjustRows.bind(this))
|
15
|
+
}
|
16
|
+
|
17
|
+
adjustRows() {
|
18
|
+
const textarea = this.element
|
19
|
+
const lines = textarea.value.split('\n').length
|
20
|
+
|
21
|
+
if (this.maxRowsValue) {
|
22
|
+
textarea.rows = Math.max(1, Math.min(this.maxRowsValue, lines))
|
23
|
+
} else {
|
24
|
+
textarea.rows = lines
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import {transition, enter, leave} from "gaggle/el-transition"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ['listener']
|
6
|
+
static values = {
|
7
|
+
open: Boolean,
|
8
|
+
startAnimation: Boolean,
|
9
|
+
skipHidden: Boolean
|
10
|
+
}
|
11
|
+
|
12
|
+
initialize() {
|
13
|
+
this.startAnimationValue && this.update()
|
14
|
+
}
|
15
|
+
|
16
|
+
transitionClasses(element, animation, direction) {
|
17
|
+
const animationClass = animation ? `${animation}-${direction}` : direction
|
18
|
+
let transition = `transition${direction.charAt(0).toUpperCase() + direction.slice(1)}`
|
19
|
+
const end = element.dataset[`${transition}End`] ? element.dataset[`${transition}End`].split(" ") : [`${animationClass}-end`]
|
20
|
+
const final = element.dataset[`${transition}Final`] ? element.dataset[`${transition}Final`].split(" ") : [`${animationClass}-final`]
|
21
|
+
return [end, final].flat()
|
22
|
+
}
|
23
|
+
|
24
|
+
removeLastTransition(element, animation, direction) {
|
25
|
+
const classes = this.transitionClasses(element, animation, direction)
|
26
|
+
removeClasses(element, classes)
|
27
|
+
}
|
28
|
+
|
29
|
+
async enter(element, transitionName = null) {
|
30
|
+
let skipHidden = (this.skipHiddenValue || element.hasAttribute('data-transition-skip-hidden'))
|
31
|
+
|
32
|
+
if (skipHidden) {
|
33
|
+
this.removeLastTransition(element, transitionName, 'leave')
|
34
|
+
} else {
|
35
|
+
element.classList.remove('hidden')
|
36
|
+
}
|
37
|
+
this.element.dispatchEvent(new Event('toggler:enter-start'))
|
38
|
+
|
39
|
+
await transition('enter', element, transitionName, skipHidden)
|
40
|
+
}
|
41
|
+
|
42
|
+
async leave(element, transitionName = null) {
|
43
|
+
let skipHidden = (this.skipHiddenValue || element.hasAttribute('data-transition-skip-hidden'))
|
44
|
+
|
45
|
+
if (skipHidden) {
|
46
|
+
this.removeLastTransition(element, transitionName, 'enter')
|
47
|
+
}
|
48
|
+
this.element.dispatchEvent(new Event('toggler:leave-start'))
|
49
|
+
await transition('leave', element, transitionName, skipHidden)
|
50
|
+
!skipHidden && element.classList.add('hidden')
|
51
|
+
}
|
52
|
+
|
53
|
+
async toggle(element, transitionName = null) {
|
54
|
+
if (this.openValue) {
|
55
|
+
this.openValue = !this.openValue
|
56
|
+
await enter(element, transitionName)
|
57
|
+
} else {
|
58
|
+
this.openValue = !this.openValue
|
59
|
+
await leave(element, transitionName)
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
toggle() {
|
64
|
+
this.openValue = !this.openValue
|
65
|
+
this.update()
|
66
|
+
}
|
67
|
+
|
68
|
+
close() {
|
69
|
+
if (this.openValue) {
|
70
|
+
this.openValue = false
|
71
|
+
this.update()
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
closeImmediately() {
|
76
|
+
this.openValue = false
|
77
|
+
this.listenerTargets.forEach((listener) => {
|
78
|
+
this.removeLastTransition(listener, null, 'enter')
|
79
|
+
const classes = this.transitionClasses(listener, null, 'leave')
|
80
|
+
addClasses(listener, classes)
|
81
|
+
})
|
82
|
+
}
|
83
|
+
|
84
|
+
open() {
|
85
|
+
if (!this.openValue) {
|
86
|
+
this.openValue = true
|
87
|
+
}
|
88
|
+
this.update()
|
89
|
+
}
|
90
|
+
|
91
|
+
update() {
|
92
|
+
this.listenerTargets.forEach((listener) => {
|
93
|
+
if (this.openValue) {
|
94
|
+
this.enter(listener)
|
95
|
+
.then(() => this.element.dispatchEvent(new Event('toggler:entered')))
|
96
|
+
} else {
|
97
|
+
this.leave(listener)
|
98
|
+
.then(() => this.element.dispatchEvent(new Event('toggler:left')))
|
99
|
+
}
|
100
|
+
})
|
101
|
+
}
|
102
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
export async function enter(element, transitionName = null) {
|
2
|
+
element.classList.remove('hidden')
|
3
|
+
await transition('enter', element, transitionName)
|
4
|
+
}
|
5
|
+
|
6
|
+
export async function leave(element, transitionName = null) {
|
7
|
+
await transition('leave', element, transitionName)
|
8
|
+
element.classList.add('hidden')
|
9
|
+
}
|
10
|
+
|
11
|
+
export async function toggle(element, transitionName = null) {
|
12
|
+
if (element.classList.contains('hidden')) {
|
13
|
+
await enter(element, transitionName)
|
14
|
+
} else {
|
15
|
+
await leave(element, transitionName)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
export async function transition(direction, element, animation, keepEnd = false) {
|
20
|
+
const dataset = element.dataset
|
21
|
+
const animationClass = animation ? `${animation}-${direction}` : direction
|
22
|
+
let transition = `transition${direction.charAt(0).toUpperCase() + direction.slice(1)}`
|
23
|
+
const genesis = dataset[transition] ? dataset[transition].split(" ") : [animationClass]
|
24
|
+
const start = dataset[`${transition}Start`] ? dataset[`${transition}Start`].split(" ") : [`${animationClass}-start`]
|
25
|
+
const end = dataset[`${transition}End`] ? dataset[`${transition}End`].split(" ") : [`${animationClass}-end`]
|
26
|
+
const final = dataset[`${transition}Final`] ? dataset[`${transition}Final`].split(" ") : [`${animationClass}-final`]
|
27
|
+
|
28
|
+
addClasses(element, genesis)
|
29
|
+
addClasses(element, start)
|
30
|
+
await nextFrame()
|
31
|
+
removeClasses(element, start)
|
32
|
+
addClasses(element, end);
|
33
|
+
await afterTransition(element)
|
34
|
+
!keepEnd && removeClasses(element, end)
|
35
|
+
removeClasses(element, genesis)
|
36
|
+
addClasses(element, final);
|
37
|
+
}
|
38
|
+
|
39
|
+
export function addClasses(element, classes) {
|
40
|
+
element.classList.add(...classes)
|
41
|
+
}
|
42
|
+
|
43
|
+
export function removeClasses(element, classes) {
|
44
|
+
element.classList.remove(...classes)
|
45
|
+
}
|
46
|
+
|
47
|
+
function nextFrame() {
|
48
|
+
return new Promise(resolve => {
|
49
|
+
requestAnimationFrame(() => {
|
50
|
+
requestAnimationFrame(resolve)
|
51
|
+
});
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
function afterTransition(element) {
|
56
|
+
return new Promise(resolve => {
|
57
|
+
// safari return string with comma separate values
|
58
|
+
const computedDuration = getComputedStyle(element).transitionDuration.split(",")[0]
|
59
|
+
const duration = Number(computedDuration.replace('s', '')) * 1000;
|
60
|
+
setTimeout(() => {
|
61
|
+
resolve()
|
62
|
+
}, duration)
|
63
|
+
});
|
64
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class Channel < ApplicationRecord
|
3
|
+
self.table_name = "gaggle_channels"
|
4
|
+
|
5
|
+
has_many :messages, class_name: "Gaggle::Message", as: :messageable, dependent: :destroy
|
6
|
+
has_and_belongs_to_many :gooses
|
7
|
+
has_many :notifications, class_name: "Gaggle::Notification", dependent: :destroy, as: :messageable
|
8
|
+
|
9
|
+
validates :name, presence: true
|
10
|
+
|
11
|
+
before_validation :assign_goose_user, if: -> { Current.goose_user }
|
12
|
+
after_create_commit :broadcast_create
|
13
|
+
after_update_commit :broadcast_update
|
14
|
+
after_destroy_commit :broadcast_destroy
|
15
|
+
|
16
|
+
def broadcast_create
|
17
|
+
broadcast_before_to "gaggle",
|
18
|
+
targets: ".new-channel",
|
19
|
+
content: ApplicationController.render(
|
20
|
+
partial: "gaggle/channels/channel",
|
21
|
+
locals: { channel: self }
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def broadcast_update
|
26
|
+
broadcast_replace_to "gaggle",
|
27
|
+
targets: ".#{dom_id(self, :sidebar)}",
|
28
|
+
partial: "gaggle/channels/channel",
|
29
|
+
content: ApplicationController.render(
|
30
|
+
partial: "gaggle/channels/channel",
|
31
|
+
locals: { channel: self }
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def broadcast_destroy
|
36
|
+
broadcast_remove_to "gaggle", targets: ".#{dom_id(self, :sidebar)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def assign_goose_user
|
42
|
+
gooses << Current.goose_user
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Gaggle
|
2
|
+
class ChannelGoose < ApplicationRecord
|
3
|
+
self.table_name = "gaggle_channels_gooses"
|
4
|
+
|
5
|
+
belongs_to :channel, class_name: "Gaggle::Channel", dependent: :destroy
|
6
|
+
belongs_to :goose, class_name: "Gaggle::Goose", dependent: :destroy
|
7
|
+
|
8
|
+
validates :channel_id, uniqueness: { scope: :goose_id }, presence: true
|
9
|
+
validates :goose_id, uniqueness: { scope: :channel_id }, presence: true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Gaggle
|
2
|
+
module Goose::PersonalityDefaults
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def personalities
|
7
|
+
[
|
8
|
+
{
|
9
|
+
name: "Project Manager",
|
10
|
+
prompt: project_manager_prompt
|
11
|
+
},
|
12
|
+
{
|
13
|
+
name: "Engineer",
|
14
|
+
prompt: engineer_prompt
|
15
|
+
}
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
def project_manager_prompt
|
20
|
+
<<~TEXT
|
21
|
+
You are an experienced Project Manager AI assistant. Your role is to coordinate and oversee a team of specialized AI assistants working on a software development project. Your responsibilities include:
|
22
|
+
|
23
|
+
1. Understanding project requirements and objectives
|
24
|
+
2. Breaking down tasks and assigning them to appropriate AI team members
|
25
|
+
3. Facilitating communication between team members
|
26
|
+
4. Monitoring progress and ensuring deadlines are met
|
27
|
+
5. Identifying and addressing potential risks or roadblocks
|
28
|
+
6. Providing status updates and summaries
|
29
|
+
|
30
|
+
Remember:
|
31
|
+
- Do not write or generate any code yourself
|
32
|
+
- Focus on project management, coordination, and communication
|
33
|
+
- Engage in discussions about solutions, but defer technical implementation to the specialized AI team members
|
34
|
+
|
35
|
+
When interacting with the Human or other AI assistants:
|
36
|
+
1. Ask clarifying questions to fully understand tasks and requirements
|
37
|
+
2. Provide clear, concise instructions and expectations
|
38
|
+
3. Encourage collaboration and knowledge sharing among team members
|
39
|
+
4. Summarize discussions and decisions for documentation purposes
|
40
|
+
5. Prioritize tasks based on project goals and timelines
|
41
|
+
6. Identify potential bottlenecks or dependencies between tasks
|
42
|
+
|
43
|
+
Your communication style should be:
|
44
|
+
- Professional and authoritative, but approachable
|
45
|
+
- Clear and concise, avoiding technical jargon when possible
|
46
|
+
- Focused on keeping the project on track and team members aligned
|
47
|
+
|
48
|
+
Your main goals will be decided by "Human". You should ask the user questions in the channel "Game Plan". If the channel doesn't exist, create it.
|
49
|
+
TEXT
|
50
|
+
end
|
51
|
+
|
52
|
+
def engineer_prompt
|
53
|
+
<<~TEXT
|
54
|
+
You are an experienced AI Software Engineer assistant. Your primary role is to implement code and follow instructions from the Project Manager AI or the user. Your responsibilities include:
|
55
|
+
|
56
|
+
1. Writing high-quality, efficient code based on given specifications
|
57
|
+
2. Debugging and troubleshooting issues in the codebase
|
58
|
+
3. Collaborating with other AI team members on complex tasks
|
59
|
+
4. Providing technical insights and recommendations when asked
|
60
|
+
5. Implementing best practices in software development
|
61
|
+
6. Ensuring code maintainability and scalability
|
62
|
+
|
63
|
+
Key behaviors:
|
64
|
+
- Always ask clarifying questions before starting any task
|
65
|
+
- Seek additional information if requirements are unclear or incomplete
|
66
|
+
- Provide detailed explanations of your code and thought process
|
67
|
+
- Be proactive in identifying potential technical challenges or limitations
|
68
|
+
- Suggest alternative approaches when appropriate, explaining pros and cons
|
69
|
+
|
70
|
+
When interacting with the Project Manager AI or user:
|
71
|
+
1. Confirm your understanding of each task before beginning implementation
|
72
|
+
2. Provide regular updates on your progress
|
73
|
+
3. Clearly communicate any roadblocks or dependencies you encounter
|
74
|
+
4. Ask for feedback on your work and be open to constructive criticism
|
75
|
+
5. Offer time estimates for tasks when requested
|
76
|
+
|
77
|
+
Your communication style should be:
|
78
|
+
- Technical but clear, explaining complex concepts in an understandable manner
|
79
|
+
- Precise and detailed in your questions and responses
|
80
|
+
- Professional and collaborative, focusing on problem-solving
|
81
|
+
|
82
|
+
You will follow instructions by either the Human or Project Manager
|
83
|
+
TEXT
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|