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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +167 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/gaggle_manifest.js +4 -0
  6. data/app/assets/stylesheets/application.css +1 -0
  7. data/app/assets/stylesheets/gaggle/tailwind.css +1740 -0
  8. data/app/controllers/gaggle/application_controller.rb +23 -0
  9. data/app/controllers/gaggle/channels/messages_controller.rb +27 -0
  10. data/app/controllers/gaggle/channels_controller.rb +84 -0
  11. data/app/controllers/gaggle/gooses/sessions_controller.rb +23 -0
  12. data/app/controllers/gaggle/gooses_controller.rb +47 -0
  13. data/app/controllers/gaggle/messages_controller.rb +7 -0
  14. data/app/controllers/gaggle/overviews_controller.rb +5 -0
  15. data/app/controllers/gaggle/sessions_controller.rb +27 -0
  16. data/app/helpers/gaggle/application_helper.rb +4 -0
  17. data/app/javascript/controllers/gaggle/application.js +9 -0
  18. data/app/javascript/controllers/gaggle/chat_field_form_controller.js +7 -0
  19. data/app/javascript/controllers/gaggle/index.js +4 -0
  20. data/app/javascript/controllers/gaggle/local_timestamp_controller.js +21 -0
  21. data/app/javascript/controllers/gaggle/text_area_auto_expand_controller.js +27 -0
  22. data/app/javascript/controllers/gaggle/transition_controller.js +102 -0
  23. data/app/javascript/gaggle/application.js +3 -0
  24. data/app/javascript/gaggle/el-transition.js +64 -0
  25. data/app/jobs/gaggle/application_job.rb +4 -0
  26. data/app/mailers/gaggle/application_mailer.rb +6 -0
  27. data/app/models/gaggle/application_record.rb +8 -0
  28. data/app/models/gaggle/channel.rb +45 -0
  29. data/app/models/gaggle/channel_goose.rb +11 -0
  30. data/app/models/gaggle/current.rb +5 -0
  31. data/app/models/gaggle/goose/personality_defaults.rb +87 -0
  32. data/app/models/gaggle/goose.rb +94 -0
  33. data/app/models/gaggle/message.rb +61 -0
  34. data/app/models/gaggle/notification.rb +47 -0
  35. data/app/models/gaggle/session.rb +138 -0
  36. data/app/views/gaggle/application/_logo.html.erb +125 -0
  37. data/app/views/gaggle/application/_mobile_header.html.erb +15 -0
  38. data/app/views/gaggle/application/_sidebar.html.erb +259 -0
  39. data/app/views/gaggle/channels/_channel.html.erb +22 -0
  40. data/app/views/gaggle/channels/_form.html.erb +70 -0
  41. data/app/views/gaggle/channels/edit.html.erb +10 -0
  42. data/app/views/gaggle/channels/gooses/index.html.erb +12 -0
  43. data/app/views/gaggle/channels/index.json.jbuilder +3 -0
  44. data/app/views/gaggle/channels/messages/new.html.erb +2 -0
  45. data/app/views/gaggle/channels/new.html.erb +8 -0
  46. data/app/views/gaggle/channels/show.html.erb +98 -0
  47. data/app/views/gaggle/channels/show.json.jbuilder +5 -0
  48. data/app/views/gaggle/channels/update.turbo_stream.erb +1 -0
  49. data/app/views/gaggle/gooses/_form.html.erb +65 -0
  50. data/app/views/gaggle/gooses/edit.html.erb +2 -0
  51. data/app/views/gaggle/gooses/index.json.jbuilder +5 -0
  52. data/app/views/gaggle/gooses/new.html.erb +2 -0
  53. data/app/views/gaggle/gooses/sessions/index.html.erb +55 -0
  54. data/app/views/gaggle/gooses/show.html.erb +41 -0
  55. data/app/views/gaggle/messages/_message.html.erb +30 -0
  56. data/app/views/gaggle/overviews/show.html.erb +17 -0
  57. data/app/views/gaggle/sessions/show.html.erb +76 -0
  58. data/app/views/layouts/gaggle/application.html.erb +19 -0
  59. data/config/importmap.rb +7 -0
  60. data/config/routes.rb +19 -0
  61. data/config/utility_classes.yml +22 -0
  62. data/db/gaggle_migrate/20250214180303_create_gaggle_tables.rb +53 -0
  63. data/db/gaggle_migrate/20250220002655_add_delivered_at_to_notification.rb +5 -0
  64. data/db/gaggle_migrate/20250220004428_create_join_table_gaggle_channel_gaggle_goose.rb +8 -0
  65. data/lib/gaggle/engine.rb +48 -0
  66. data/lib/gaggle/version.rb +3 -0
  67. data/lib/gaggle.rb +8 -0
  68. data/lib/generators/gaggle/install/install_generator.rb +7 -0
  69. data/lib/generators/gaggle/install/templates/db/gaggle_schema.rb +59 -0
  70. data/lib/generators/gaggle/update/update_generator.rb +16 -0
  71. 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,7 @@
1
+ module Gaggle
2
+ class MessagesController < ApplicationController
3
+ def destroy
4
+ render plain: "Message destroyed: #{params[:id]}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Gaggle
2
+ class OverviewsController < ApplicationController
3
+ def show; end
4
+ end
5
+ 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
+ module Gaggle
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+
3
+ const gaggleApplication = Application.start()
4
+
5
+ // Configure Stimulus development experience
6
+ gaggleApplication.debug = false
7
+ window.Stimulus = gaggleApplication
8
+
9
+ export { gaggleApplication }
@@ -0,0 +1,7 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ submitForm() {
5
+ this.element.closest('form').requestSubmit()
6
+ }
7
+ }
@@ -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,3 @@
1
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2
+ import "@hotwired/turbo-rails"
3
+ import "controllers/gaggle"
@@ -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,4 @@
1
+ module Gaggle
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Gaggle
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module Gaggle
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ include ActionView::RecordIdentifier
4
+ self.abstract_class =true
5
+ establish_connection :gaggle
6
+ connects_to database: { writing: :gaggle, reading: :gaggle }
7
+ end
8
+ end
@@ -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,5 @@
1
+ module Gaggle
2
+ class Current < ActiveSupport::CurrentAttributes
3
+ attribute :goose_user, :goose
4
+ end
5
+ 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