brainiac 0.0.3 → 0.0.5
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 +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/bin/brainiac +249 -4
- data/bin/brainiac-completion.bash +10 -4
- data/brainiac.gemspec +0 -4
- data/lib/brainiac/handlers/discord.rb +57 -1
- data/lib/brainiac/version.rb +1 -1
- metadata +2 -30
- checksums.yaml.gz.sig +0 -0
- data/certs/stowzilla.pem +0 -26
- data.tar.gz.sig +0 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5d349dd4297beb2e1970aaff969c468ea088cc58142391e738f0dbefa7736fcb
|
|
4
|
+
data.tar.gz: 79b008e8823b2c4289f48a4356e4bd9271c5d3564518ec9bd42f24c460870822
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2c7b7448242a508c1cbb3d58c1d4a8cea72218d886fc2db60c9c5a2913ad8737300ef3cd897a44333af4cbad44dc92d046f8e3ca20cb792edb552868ea6a4d4
|
|
7
|
+
data.tar.gz: b0a838dbd6b1ab90f65004cc4f420eb87632c2a8e60ee95e456f9e6bede3a7184a1130981740024166936730eb7df5ff4d7a3365d65315d7e3a871514b9b6d72
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
brainiac (0.0.
|
|
4
|
+
brainiac (0.0.5)
|
|
5
5
|
puma (~> 7.2)
|
|
6
6
|
rackup (~> 2.3)
|
|
7
7
|
sinatra (~> 4.1)
|
|
@@ -90,7 +90,7 @@ DEPENDENCIES
|
|
|
90
90
|
CHECKSUMS
|
|
91
91
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
92
92
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
93
|
-
brainiac (0.0.
|
|
93
|
+
brainiac (0.0.5)
|
|
94
94
|
event_emitter (0.2.6) sha256=c72697bd5cce9d36594be1972c17f1c9a573236f44303a4d1d548080364e1391
|
|
95
95
|
json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
|
|
96
96
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Brainiac
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Brainiac is a multi-agent orchestration layer for developer workflows. It connects your tools (Fizzy, GitHub, Discord, Zoho) to a team of autonomous AI agents. By managing personas, shared knowledge, and collaborative workflows via @mentions, Brainiac allows you to deploy and manage a unified fleet of AI experts directly from your CLI.
|
|
4
4
|
|
|
5
5
|
## How It Works
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Brainiac listens for incoming events and automatically spawns the appropriate AI agent CLI to handle the request. Because each agent maintains its own unique persona and configuration, you can deploy a specialized team—all managed through ~/.kiro/agents/. Project tracking is handled via ~/.brainiac/projects.json, and because the system reloads configurations dynamically, you can iterate on your agent workflows in real-time without ever needing a restart.
|
|
8
8
|
|
|
9
9
|
### Events
|
|
10
10
|
|
data/bin/brainiac
CHANGED
|
@@ -1436,23 +1436,179 @@ when "agent"
|
|
|
1436
1436
|
end
|
|
1437
1437
|
end
|
|
1438
1438
|
|
|
1439
|
+
when "create", "add"
|
|
1440
|
+
name = ARGV.shift
|
|
1441
|
+
unless name
|
|
1442
|
+
puts "Usage: brainiac agent create <name> [options]"
|
|
1443
|
+
puts ""
|
|
1444
|
+
puts "Options:"
|
|
1445
|
+
puts " --local Mark agent as local (dispatches on this machine)"
|
|
1446
|
+
puts " --role <role> Assign a role (repeatable)"
|
|
1447
|
+
puts " --cli <provider> Set CLI provider (default: kiro)"
|
|
1448
|
+
puts " --persona <text> Create a persona style file with this description"
|
|
1449
|
+
puts ""
|
|
1450
|
+
puts "If no options are given and stdin is a terminal, interactive mode is used."
|
|
1451
|
+
puts ""
|
|
1452
|
+
puts "Examples:"
|
|
1453
|
+
puts " brainiac agent create SecurityBot --local --role code-reviewer"
|
|
1454
|
+
puts " brainiac agent create Jane --role general-engineer --cli grok"
|
|
1455
|
+
puts " brainiac agent create Avon --persona \"Dry wit, sardonic, brilliant\""
|
|
1456
|
+
puts " brainiac agent create MyAgent # interactive"
|
|
1457
|
+
exit 1
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
agent_key = name.downcase.gsub(/[^a-z0-9-]/, "-")
|
|
1461
|
+
registry = File.exist?(agent_registry_file) ? JSON.parse(File.read(agent_registry_file)) : {}
|
|
1462
|
+
|
|
1463
|
+
if registry[agent_key]
|
|
1464
|
+
puts "Agent '#{name}' already exists. Use 'brainiac agent #{agent_key} show' to view."
|
|
1465
|
+
exit 1
|
|
1466
|
+
end
|
|
1467
|
+
|
|
1468
|
+
local = false
|
|
1469
|
+
roles = []
|
|
1470
|
+
cli_provider = nil
|
|
1471
|
+
persona_text = nil
|
|
1472
|
+
|
|
1473
|
+
if ARGV.any?
|
|
1474
|
+
# Flag mode
|
|
1475
|
+
while ARGV.any?
|
|
1476
|
+
case ARGV[0]
|
|
1477
|
+
when "--local"
|
|
1478
|
+
ARGV.shift
|
|
1479
|
+
local = true
|
|
1480
|
+
when "--role"
|
|
1481
|
+
ARGV.shift
|
|
1482
|
+
roles << ARGV.shift
|
|
1483
|
+
when "--cli"
|
|
1484
|
+
ARGV.shift
|
|
1485
|
+
cli_provider = ARGV.shift
|
|
1486
|
+
when "--persona"
|
|
1487
|
+
ARGV.shift
|
|
1488
|
+
persona_text = ARGV.shift
|
|
1489
|
+
else
|
|
1490
|
+
puts "Unknown option: #{ARGV[0]}"
|
|
1491
|
+
exit 1
|
|
1492
|
+
end
|
|
1493
|
+
end
|
|
1494
|
+
elsif $stdin.tty?
|
|
1495
|
+
# Interactive mode
|
|
1496
|
+
puts "Creating agent '#{name}' (#{agent_key})"
|
|
1497
|
+
puts ""
|
|
1498
|
+
|
|
1499
|
+
print "Local (dispatches on this machine)? [y/N]: "
|
|
1500
|
+
local = $stdin.gets.chomp.downcase.start_with?("y")
|
|
1501
|
+
|
|
1502
|
+
# Show available CLI providers
|
|
1503
|
+
providers_dir = File.join(BRAINIAC_DIR, "cli-providers")
|
|
1504
|
+
available_providers = Dir.exist?(providers_dir) ? Dir.glob(File.join(providers_dir, "*.json")).map { |f| File.basename(f, ".json") } : []
|
|
1505
|
+
hint = available_providers.any? ? " (#{available_providers.join(", ")})" : ""
|
|
1506
|
+
print "CLI provider#{hint} [kiro]: "
|
|
1507
|
+
input = $stdin.gets.chomp
|
|
1508
|
+
cli_provider = input.empty? ? nil : input
|
|
1509
|
+
|
|
1510
|
+
# Show available roles
|
|
1511
|
+
roles_dir = File.join(BRAINIAC_DIR, "roles")
|
|
1512
|
+
available_roles = Dir.exist?(roles_dir) ? Dir.glob(File.join(roles_dir, "*.md")).map { |f| File.basename(f, ".md") } : []
|
|
1513
|
+
hint = available_roles.any? ? " (#{available_roles.join(", ")})" : ""
|
|
1514
|
+
print "Roles#{hint} (comma-separated) []: "
|
|
1515
|
+
input = $stdin.gets.chomp
|
|
1516
|
+
roles = input.empty? ? [] : input.split(",").map(&:strip)
|
|
1517
|
+
|
|
1518
|
+
print "Persona description (tone/style, or blank to skip) []: "
|
|
1519
|
+
input = $stdin.gets.chomp
|
|
1520
|
+
persona_text = input.empty? ? nil : input
|
|
1521
|
+
end
|
|
1522
|
+
|
|
1523
|
+
entry = { "fizzy_name" => name }
|
|
1524
|
+
entry["local"] = true if local
|
|
1525
|
+
entry["cli_provider"] = cli_provider if cli_provider
|
|
1526
|
+
entry["role"] = roles.size == 1 ? roles.first : roles if roles.any?
|
|
1527
|
+
|
|
1528
|
+
registry[agent_key] = entry
|
|
1529
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1530
|
+
puts "✓ Created agent '#{name}' (#{agent_key})"
|
|
1531
|
+
puts " Local: #{local}" if local
|
|
1532
|
+
puts " Role: #{roles.join(", ")}" if roles.any?
|
|
1533
|
+
puts " CLI: #{cli_provider}" if cli_provider
|
|
1534
|
+
|
|
1535
|
+
# Create persona directory and style file if requested
|
|
1536
|
+
if persona_text
|
|
1537
|
+
persona_dir = File.join(BRAINIAC_DIR, "brain", "persona", agent_key)
|
|
1538
|
+
FileUtils.mkdir_p(persona_dir)
|
|
1539
|
+
persona_file = File.join(persona_dir, "style.md")
|
|
1540
|
+
unless File.exist?(persona_file)
|
|
1541
|
+
File.write(persona_file, <<~MD)
|
|
1542
|
+
---
|
|
1543
|
+
name: #{agent_key}-style
|
|
1544
|
+
description: Persona voice for #{name}.
|
|
1545
|
+
---
|
|
1546
|
+
# #{name} — Persona
|
|
1547
|
+
#{persona_text}
|
|
1548
|
+
MD
|
|
1549
|
+
puts " Persona: #{persona_file}"
|
|
1550
|
+
end
|
|
1551
|
+
end
|
|
1552
|
+
|
|
1553
|
+
# Initialize brain (qmd collections) for local agents
|
|
1554
|
+
if local
|
|
1555
|
+
puts ""
|
|
1556
|
+
brain_init(name)
|
|
1557
|
+
end
|
|
1558
|
+
|
|
1559
|
+
when "remove", "delete", "rm"
|
|
1560
|
+
name = ARGV.shift
|
|
1561
|
+
unless name
|
|
1562
|
+
puts "Usage: brainiac agent remove <name>"
|
|
1563
|
+
exit 1
|
|
1564
|
+
end
|
|
1565
|
+
|
|
1566
|
+
agent_key = name.downcase.gsub(/[^a-z0-9-]/, "-")
|
|
1567
|
+
registry = File.exist?(agent_registry_file) ? JSON.parse(File.read(agent_registry_file)) : {}
|
|
1568
|
+
|
|
1569
|
+
unless registry[agent_key]
|
|
1570
|
+
puts "Agent '#{name}' not found in registry."
|
|
1571
|
+
exit 1
|
|
1572
|
+
end
|
|
1573
|
+
|
|
1574
|
+
display = registry[agent_key]["fizzy_name"] || name
|
|
1575
|
+
registry.delete(agent_key)
|
|
1576
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1577
|
+
puts "✓ Removed agent '#{display}' (#{agent_key}) from registry"
|
|
1578
|
+
puts " Note: Persona files at ~/.brainiac/brain/persona/#{agent_key}/ were not deleted."
|
|
1579
|
+
|
|
1439
1580
|
when nil
|
|
1440
1581
|
puts <<~HELP
|
|
1441
1582
|
Usage: brainiac agent <name> <command> [args]
|
|
1442
1583
|
brainiac agent list
|
|
1584
|
+
brainiac agent create <name> [options]
|
|
1585
|
+
brainiac agent remove <name>
|
|
1443
1586
|
|
|
1444
1587
|
Commands:
|
|
1445
1588
|
list List all agents in the registry
|
|
1589
|
+
create <name> [options] Add a new agent to the registry
|
|
1590
|
+
remove <name> Remove an agent from the registry
|
|
1446
1591
|
<name> show Show agent configuration (tokens redacted)
|
|
1592
|
+
<name> set <field> <value> Update a config field (local, cli, role, persona, fizzy_name)
|
|
1447
1593
|
<name> env <KEY> <VALUE> Set an env var for an agent
|
|
1448
1594
|
<name> env List env vars for an agent
|
|
1449
1595
|
<name> env --delete <KEY> Remove an env var from an agent
|
|
1450
1596
|
|
|
1597
|
+
Create options (or interactive if no flags given):
|
|
1598
|
+
--local Mark agent as local (dispatches on this machine)
|
|
1599
|
+
--role <role> Assign a role (repeatable)
|
|
1600
|
+
--cli <provider> Set CLI provider (default: kiro)
|
|
1601
|
+
--persona <text> Create a persona style file
|
|
1602
|
+
|
|
1451
1603
|
Examples:
|
|
1604
|
+
brainiac agent create SecurityBot --local --role code-reviewer
|
|
1605
|
+
brainiac agent create Jane --cli grok
|
|
1606
|
+
brainiac agent create MyAgent # interactive mode
|
|
1607
|
+
brainiac agent remove SecurityBot
|
|
1608
|
+
brainiac agent galen set local true
|
|
1609
|
+
brainiac agent galen set persona "Dry, sardonic"
|
|
1610
|
+
brainiac agent galen set role code-reviewer,general-engineer
|
|
1452
1611
|
brainiac agent galen env FIZZY_TOKEN fizzy_abc123
|
|
1453
|
-
brainiac agent galen env DISCORD_BOT_TOKEN Bot_xyz789
|
|
1454
|
-
brainiac agent galen env
|
|
1455
|
-
brainiac agent galen env --delete DISCORD_BOT_TOKEN
|
|
1456
1612
|
brainiac agent galen show
|
|
1457
1613
|
HELP
|
|
1458
1614
|
|
|
@@ -1522,9 +1678,98 @@ when "agent"
|
|
|
1522
1678
|
puts "✓ Set #{var_name} for #{agent_cmd}"
|
|
1523
1679
|
end
|
|
1524
1680
|
|
|
1681
|
+
when "set"
|
|
1682
|
+
field = ARGV.shift
|
|
1683
|
+
value = ARGV.shift
|
|
1684
|
+
unless field
|
|
1685
|
+
puts "Usage: brainiac agent #{agent_cmd} set <field> <value>"
|
|
1686
|
+
puts ""
|
|
1687
|
+
puts "Fields:"
|
|
1688
|
+
puts " local <true|false> Set local dispatch flag"
|
|
1689
|
+
puts " cli <provider> Set CLI provider"
|
|
1690
|
+
puts " role <roles> Set roles (comma-separated, or 'none' to clear)"
|
|
1691
|
+
puts " persona <text> Set/update persona style file"
|
|
1692
|
+
puts " fizzy_name <name> Set display name for Fizzy @mentions"
|
|
1693
|
+
exit 1
|
|
1694
|
+
end
|
|
1695
|
+
|
|
1696
|
+
registry = File.exist?(agent_registry_file) ? JSON.parse(File.read(agent_registry_file)) : {}
|
|
1697
|
+
unless registry[agent_key]
|
|
1698
|
+
puts "Agent '#{agent_cmd}' not found in registry."
|
|
1699
|
+
exit 1
|
|
1700
|
+
end
|
|
1701
|
+
|
|
1702
|
+
case field
|
|
1703
|
+
when "local"
|
|
1704
|
+
if value == "true"
|
|
1705
|
+
registry[agent_key]["local"] = true
|
|
1706
|
+
else
|
|
1707
|
+
registry[agent_key].delete("local")
|
|
1708
|
+
end
|
|
1709
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1710
|
+
puts "✓ Set local=#{value} for #{agent_cmd}"
|
|
1711
|
+
|
|
1712
|
+
when "cli"
|
|
1713
|
+
unless value
|
|
1714
|
+
puts "Usage: brainiac agent #{agent_cmd} set cli <provider>"
|
|
1715
|
+
exit 1
|
|
1716
|
+
end
|
|
1717
|
+
registry[agent_key]["cli_provider"] = value
|
|
1718
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1719
|
+
puts "✓ Set cli_provider=#{value} for #{agent_cmd}"
|
|
1720
|
+
|
|
1721
|
+
when "role"
|
|
1722
|
+
unless value
|
|
1723
|
+
puts "Usage: brainiac agent #{agent_cmd} set role <roles|none>"
|
|
1724
|
+
exit 1
|
|
1725
|
+
end
|
|
1726
|
+
if value == "none"
|
|
1727
|
+
registry[agent_key].delete("role")
|
|
1728
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1729
|
+
puts "✓ Set role for #{agent_cmd}"
|
|
1730
|
+
next
|
|
1731
|
+
end
|
|
1732
|
+
roles = value.split(",").map(&:strip)
|
|
1733
|
+
registry[agent_key]["role"] = roles.size == 1 ? roles.first : roles
|
|
1734
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1735
|
+
puts "✓ Set role for #{agent_cmd}"
|
|
1736
|
+
|
|
1737
|
+
when "persona"
|
|
1738
|
+
unless value
|
|
1739
|
+
puts "Usage: brainiac agent #{agent_cmd} set persona <text>"
|
|
1740
|
+
exit 1
|
|
1741
|
+
end
|
|
1742
|
+
persona_dir = File.join(BRAINIAC_DIR, "brain", "persona", agent_key)
|
|
1743
|
+
FileUtils.mkdir_p(persona_dir)
|
|
1744
|
+
persona_file = File.join(persona_dir, "style.md")
|
|
1745
|
+
display = registry[agent_key]["fizzy_name"] || agent_cmd.capitalize
|
|
1746
|
+
File.write(persona_file, <<~MD)
|
|
1747
|
+
---
|
|
1748
|
+
name: #{agent_key}-style
|
|
1749
|
+
description: Persona voice for #{display}.
|
|
1750
|
+
---
|
|
1751
|
+
# #{display} — Persona
|
|
1752
|
+
#{value}
|
|
1753
|
+
MD
|
|
1754
|
+
puts "✓ Wrote persona to #{persona_file}"
|
|
1755
|
+
|
|
1756
|
+
when "fizzy_name"
|
|
1757
|
+
unless value
|
|
1758
|
+
puts "Usage: brainiac agent #{agent_cmd} set fizzy_name <name>"
|
|
1759
|
+
exit 1
|
|
1760
|
+
end
|
|
1761
|
+
registry[agent_key]["fizzy_name"] = value
|
|
1762
|
+
File.write(agent_registry_file, JSON.pretty_generate(registry))
|
|
1763
|
+
puts "✓ Set fizzy_name=#{value} for #{agent_cmd}"
|
|
1764
|
+
|
|
1765
|
+
else
|
|
1766
|
+
puts "Unknown field '#{field}'. Available: local, cli, role, persona, fizzy_name"
|
|
1767
|
+
exit 1
|
|
1768
|
+
end
|
|
1769
|
+
|
|
1525
1770
|
else
|
|
1526
1771
|
puts "Unknown subcommand '#{sub}' for agent '#{agent_cmd}'."
|
|
1527
|
-
puts "Available: show, env"
|
|
1772
|
+
puts "Available: show, env, set"
|
|
1528
1773
|
exit 1
|
|
1529
1774
|
end
|
|
1530
1775
|
end
|
|
@@ -73,13 +73,19 @@ _brainiac() {
|
|
|
73
73
|
agent)
|
|
74
74
|
case $cword in
|
|
75
75
|
2)
|
|
76
|
-
COMPREPLY=($(compgen -W "list $(_brainiac_agents)" -- "$cur"))
|
|
76
|
+
COMPREPLY=($(compgen -W "list create remove $(_brainiac_agents)" -- "$cur"))
|
|
77
77
|
;;
|
|
78
78
|
3)
|
|
79
79
|
local agent_name="${words[2]}"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
case "$agent_name" in
|
|
81
|
+
list|remove|delete|rm) ;;
|
|
82
|
+
create|add)
|
|
83
|
+
COMPREPLY=($(compgen -W "--local --role --cli --persona" -- "$cur"))
|
|
84
|
+
;;
|
|
85
|
+
*)
|
|
86
|
+
COMPREPLY=($(compgen -W "show env" -- "$cur"))
|
|
87
|
+
;;
|
|
88
|
+
esac
|
|
83
89
|
;;
|
|
84
90
|
4)
|
|
85
91
|
local subcmd="${words[3]}"
|
data/brainiac.gemspec
CHANGED
|
@@ -10,10 +10,6 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.license = "MIT"
|
|
11
11
|
s.required_ruby_version = ">= 3.4"
|
|
12
12
|
|
|
13
|
-
s.cert_chain = ["certs/stowzilla.pem"]
|
|
14
|
-
signing_key_path = File.expand_path("~/.ssh/gem-private_key.pem")
|
|
15
|
-
s.signing_key = signing_key_path if File.exist?(signing_key_path)
|
|
16
|
-
|
|
17
13
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.start_with?("test/", "tmp/", ".") }
|
|
18
14
|
s.executables = ["brainiac"]
|
|
19
15
|
|
|
@@ -742,6 +742,14 @@ def handle_discord_message(message, agent_key, bot_token, bot_user_id)
|
|
|
742
742
|
# Strip CLI provider tag (e.g. [cli:grok]) from prompt content
|
|
743
743
|
clean_content_for_prompt = clean_content_for_prompt.sub(/\[cli:\w+\]/i, "").strip
|
|
744
744
|
|
|
745
|
+
# Detect chat mode tag: [chat], [question], [?] — skips worktree, uses tmp dir instead
|
|
746
|
+
chat_mode = clean_content.match?(/\[(chat|question|\?)\]/i)
|
|
747
|
+
if chat_mode
|
|
748
|
+
clean_content = clean_content.sub(/\[(chat|question|\?)\]/i, "").strip
|
|
749
|
+
clean_content_for_prompt = clean_content_for_prompt.sub(/\[(chat|question|\?)\]/i, "").strip
|
|
750
|
+
LOG.info "[Discord:#{agent_name}] Chat mode detected — will skip worktree creation"
|
|
751
|
+
end
|
|
752
|
+
|
|
745
753
|
# Find project: inline override > channel mapping > default_project
|
|
746
754
|
if inline_project_key && PROJECTS.key?(inline_project_key)
|
|
747
755
|
project_key = inline_project_key
|
|
@@ -927,7 +935,21 @@ def handle_discord_message(message, agent_key, bot_token, bot_user_id)
|
|
|
927
935
|
thread_map = DISCORD_THREAD_MAP_MUTEX.synchronize { load_discord_thread_map }
|
|
928
936
|
existing = thread_map[thread_map_key]
|
|
929
937
|
|
|
930
|
-
if existing && existing["
|
|
938
|
+
if existing && existing["chat_mode"]
|
|
939
|
+
# Thread was started in chat mode — reuse the tmp dir, don't create a worktree
|
|
940
|
+
thread_worktree_path = existing["worktree"]
|
|
941
|
+
thread_cli_provider = existing["cli_provider"]
|
|
942
|
+
thread_model = existing["model"]
|
|
943
|
+
thread_effort = existing["effort"]
|
|
944
|
+
chat_mode = true
|
|
945
|
+
effective_provider = detect_cli_provider(text: clean_content) || thread_cli_provider
|
|
946
|
+
resolved_for_resume = resolve_project_cli_config(project_config, cli_provider_override: effective_provider, agent_name: agent_name)
|
|
947
|
+
should_resume = resolved_for_resume["resume_flag"] ? true : false
|
|
948
|
+
LOG.info "[Discord:#{agent_name}] Reusing chat mode tmp dir at #{thread_worktree_path} (resume: #{should_resume})"
|
|
949
|
+
elsif chat_mode
|
|
950
|
+
# New chat mode session — skip worktree, tmp dir will be created below
|
|
951
|
+
LOG.info "[Discord:#{agent_name}] Chat mode — skipping worktree creation"
|
|
952
|
+
elsif existing && existing["worktree"] && File.directory?(existing["worktree"])
|
|
931
953
|
# Existing worktree — resume session. Inherit tags from the thread's first dispatch.
|
|
932
954
|
thread_worktree_path = existing["worktree"]
|
|
933
955
|
thread_cli_provider = existing["cli_provider"]
|
|
@@ -989,6 +1011,31 @@ def handle_discord_message(message, agent_key, bot_token, bot_user_id)
|
|
|
989
1011
|
end
|
|
990
1012
|
end
|
|
991
1013
|
|
|
1014
|
+
# Chat mode: create a tmp directory instead of a worktree for lightweight conversational sessions
|
|
1015
|
+
if chat_mode && !thread_worktree_path && thread_map_key
|
|
1016
|
+
chat_tmp_dir = File.join(BRAINIAC_DIR, "tmp", "chat", "#{agent_key}-#{effective_thread_id}")
|
|
1017
|
+
FileUtils.mkdir_p(chat_tmp_dir)
|
|
1018
|
+
thread_worktree_path = chat_tmp_dir
|
|
1019
|
+
|
|
1020
|
+
first_cli_provider = detect_cli_provider(text: clean_content)
|
|
1021
|
+
first_model = project_config ? detect_model(project_config, text: clean_content) : nil
|
|
1022
|
+
first_effort = project_config ? detect_effort(project_config, text: clean_content) : nil
|
|
1023
|
+
thread_cli_provider = first_cli_provider
|
|
1024
|
+
thread_model = first_model
|
|
1025
|
+
thread_effort = first_effort
|
|
1026
|
+
|
|
1027
|
+
DISCORD_THREAD_MAP_MUTEX.synchronize do
|
|
1028
|
+
map = load_discord_thread_map
|
|
1029
|
+
map[thread_map_key] = { "worktree" => chat_tmp_dir, "chat_mode" => true,
|
|
1030
|
+
"project" => PROJECTS.find { |_k, v| v == project_config }&.first,
|
|
1031
|
+
"channel_id" => effective_thread_id, "cli_provider" => first_cli_provider,
|
|
1032
|
+
"model" => first_model, "effort" => first_effort,
|
|
1033
|
+
"created_at" => Time.now.iso8601 }
|
|
1034
|
+
save_discord_thread_map(map)
|
|
1035
|
+
end
|
|
1036
|
+
LOG.info "[Discord:#{agent_name}] Created chat mode tmp dir at #{chat_tmp_dir}"
|
|
1037
|
+
end
|
|
1038
|
+
|
|
992
1039
|
if should_resume && thread_worktree_path
|
|
993
1040
|
# Lean resume prompt — prior session has full context
|
|
994
1041
|
prompt = render_discord_resume_prompt(
|
|
@@ -1038,6 +1085,15 @@ def handle_discord_message(message, agent_key, bot_token, bot_user_id)
|
|
|
1038
1085
|
channel: :discord)
|
|
1039
1086
|
end
|
|
1040
1087
|
|
|
1088
|
+
# Chat mode fallback: if no thread_map_key was available (no thread created, e.g. DM or no project),
|
|
1089
|
+
# still use a tmp dir so we don't fall back to the project repo_path.
|
|
1090
|
+
if chat_mode && !thread_worktree_path
|
|
1091
|
+
chat_tmp_dir = File.join(BRAINIAC_DIR, "tmp", "chat", "#{agent_key}-#{message_id}")
|
|
1092
|
+
FileUtils.mkdir_p(chat_tmp_dir)
|
|
1093
|
+
thread_worktree_path = chat_tmp_dir
|
|
1094
|
+
LOG.info "[Discord:#{agent_name}] Chat mode fallback tmp dir at #{chat_tmp_dir}"
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1041
1097
|
work_dir = thread_worktree_path || (project_config ? project_config["repo_path"] : Dir.pwd)
|
|
1042
1098
|
|
|
1043
1099
|
prompt_file = File.join(response_dir, "discord-prompt-#{timestamp}-#{agent_key}-#{message_id}.md")
|
data/lib/brainiac/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,39 +1,12 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brainiac
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
8
8
|
bindir: bin
|
|
9
|
-
cert_chain:
|
|
10
|
-
- |
|
|
11
|
-
-----BEGIN CERTIFICATE-----
|
|
12
|
-
MIIEdDCCAtygAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMQ4wDAYDVQQDDAVhZ2Vu
|
|
13
|
-
dDEZMBcGCgmSJomT8ixkARkWCXN0b3d6aWxsYTETMBEGCgmSJomT8ixkARkWA2Nv
|
|
14
|
-
bTAeFw0yNjA2MDgxOTExNTlaFw0yNzA2MDgxOTExNTlaMEAxDjAMBgNVBAMMBWFn
|
|
15
|
-
ZW50MRkwFwYKCZImiZPyLGQBGRYJc3Rvd3ppbGxhMRMwEQYKCZImiZPyLGQBGRYD
|
|
16
|
-
Y29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAupBquKI/4WvXOgND
|
|
17
|
-
pXyqH2GllZs1wG4TWWdn/DoMg45UoCwD+AWEuGrIdInBCpPN8vEJNJWPoM/RrU+b
|
|
18
|
-
xRBZT4uUk00bnZRW2SYh5GJSqBoBR+rWc2DGkXyGfdRU2sQvkB0+is6ChgQ61WMM
|
|
19
|
-
33LE9+loBlVsZ6EVtrc18Uh2OW0mJpe0hN2nmBrxZqqOZigxC4DKRMFHvpRkxSb6
|
|
20
|
-
mD4kit1AcwX9NEWJsXxrPaetL/SB/VbXaEZX93XAvp6USaXvCWt4slkDS2mIvqtn
|
|
21
|
-
9DtGC43LFC7SDGbnsG9PVenQgVCi8UWFPUAab0PqZSlmi3Qlbhw8qTGPp5Cbv4vz
|
|
22
|
-
qjC2UGPOQigA/7lbbGRhCohMrjOVHMAQwkcgiIqtolUoYlnvPMIy+m3pdvgDv/PH
|
|
23
|
-
bsZGvXQ7i0458xsmp1vaKthZocVAR+GboHbuIiYPUnO45ccXUQ00x6365tTe7mZi
|
|
24
|
-
NvmUYdAGbQmVvFqyxF7IYA6sF74L2Lstu0knSfss557bAe1HAgMBAAGjeTB3MAkG
|
|
25
|
-
A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSnxTL/lNBCeLqpeVIX6AUY
|
|
26
|
-
kel4zjAeBgNVHREEFzAVgRNhZ2VudEBzdG93emlsbGEuY29tMB4GA1UdEgQXMBWB
|
|
27
|
-
E2FnZW50QHN0b3d6aWxsYS5jb20wDQYJKoZIhvcNAQELBQADggGBACm9Fjit/UCv
|
|
28
|
-
FxlKqeiCTIG94cIx+QrWAOJSx9knKydwUec1u04D/DbfZjTn3C2Bj227QgxeUn+6
|
|
29
|
-
if3e2v7zAk1896hLmGYzML0+nxQPb0vmtdLR7HETUlSKTVabcv1fbwLyjsuGrBvk
|
|
30
|
-
y51vOEzUEZ508a9yepLYqrQu1kOju4d57c9oA5l3H0mMKWz7av9tFj0B+STvuaWk
|
|
31
|
-
HRYDWc5HgOEVTyV+w0uFt2Kw4OCb8C42uSvC5RfYYtw78MSP+5Ru+LXJ7XOtmuN0
|
|
32
|
-
E6GVmofQ17ig9O3rgfFbMendSInrRmvPIGswvM1yivq9NOllFbdck2OJKPx6FCJF
|
|
33
|
-
7SJIkXQfc9P4B5iASIV1d1FsE0YX+g3jHXPJK/4mGL5bAyBKzpMfQB/mg6vQBzkh
|
|
34
|
-
aOKPwcreFj7TznBl89R5tNS9wZQfPVR98zgPyocddWhK18eQNMSBUnv4eeJ8PPbk
|
|
35
|
-
DovL+G8ajHDZ9fjH/+GVYHEMuiVdLarXrKJpHC1VfGTTUAp4NSEpUQ==
|
|
36
|
-
-----END CERTIFICATE-----
|
|
9
|
+
cert_chain: []
|
|
37
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
38
11
|
dependencies:
|
|
39
12
|
- !ruby/object:Gem::Dependency
|
|
@@ -163,7 +136,6 @@ files:
|
|
|
163
136
|
- bin/brainiac
|
|
164
137
|
- bin/brainiac-completion.bash
|
|
165
138
|
- brainiac.gemspec
|
|
166
|
-
- certs/stowzilla.pem
|
|
167
139
|
- docs/waybar-config.md
|
|
168
140
|
- lib/brainiac.rb
|
|
169
141
|
- lib/brainiac/agents.rb
|
checksums.yaml.gz.sig
DELETED
|
Binary file
|
data/certs/stowzilla.pem
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIEdDCCAtygAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMQ4wDAYDVQQDDAVhZ2Vu
|
|
3
|
-
dDEZMBcGCgmSJomT8ixkARkWCXN0b3d6aWxsYTETMBEGCgmSJomT8ixkARkWA2Nv
|
|
4
|
-
bTAeFw0yNjA2MDgxOTExNTlaFw0yNzA2MDgxOTExNTlaMEAxDjAMBgNVBAMMBWFn
|
|
5
|
-
ZW50MRkwFwYKCZImiZPyLGQBGRYJc3Rvd3ppbGxhMRMwEQYKCZImiZPyLGQBGRYD
|
|
6
|
-
Y29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAupBquKI/4WvXOgND
|
|
7
|
-
pXyqH2GllZs1wG4TWWdn/DoMg45UoCwD+AWEuGrIdInBCpPN8vEJNJWPoM/RrU+b
|
|
8
|
-
xRBZT4uUk00bnZRW2SYh5GJSqBoBR+rWc2DGkXyGfdRU2sQvkB0+is6ChgQ61WMM
|
|
9
|
-
33LE9+loBlVsZ6EVtrc18Uh2OW0mJpe0hN2nmBrxZqqOZigxC4DKRMFHvpRkxSb6
|
|
10
|
-
mD4kit1AcwX9NEWJsXxrPaetL/SB/VbXaEZX93XAvp6USaXvCWt4slkDS2mIvqtn
|
|
11
|
-
9DtGC43LFC7SDGbnsG9PVenQgVCi8UWFPUAab0PqZSlmi3Qlbhw8qTGPp5Cbv4vz
|
|
12
|
-
qjC2UGPOQigA/7lbbGRhCohMrjOVHMAQwkcgiIqtolUoYlnvPMIy+m3pdvgDv/PH
|
|
13
|
-
bsZGvXQ7i0458xsmp1vaKthZocVAR+GboHbuIiYPUnO45ccXUQ00x6365tTe7mZi
|
|
14
|
-
NvmUYdAGbQmVvFqyxF7IYA6sF74L2Lstu0knSfss557bAe1HAgMBAAGjeTB3MAkG
|
|
15
|
-
A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSnxTL/lNBCeLqpeVIX6AUY
|
|
16
|
-
kel4zjAeBgNVHREEFzAVgRNhZ2VudEBzdG93emlsbGEuY29tMB4GA1UdEgQXMBWB
|
|
17
|
-
E2FnZW50QHN0b3d6aWxsYS5jb20wDQYJKoZIhvcNAQELBQADggGBACm9Fjit/UCv
|
|
18
|
-
FxlKqeiCTIG94cIx+QrWAOJSx9knKydwUec1u04D/DbfZjTn3C2Bj227QgxeUn+6
|
|
19
|
-
if3e2v7zAk1896hLmGYzML0+nxQPb0vmtdLR7HETUlSKTVabcv1fbwLyjsuGrBvk
|
|
20
|
-
y51vOEzUEZ508a9yepLYqrQu1kOju4d57c9oA5l3H0mMKWz7av9tFj0B+STvuaWk
|
|
21
|
-
HRYDWc5HgOEVTyV+w0uFt2Kw4OCb8C42uSvC5RfYYtw78MSP+5Ru+LXJ7XOtmuN0
|
|
22
|
-
E6GVmofQ17ig9O3rgfFbMendSInrRmvPIGswvM1yivq9NOllFbdck2OJKPx6FCJF
|
|
23
|
-
7SJIkXQfc9P4B5iASIV1d1FsE0YX+g3jHXPJK/4mGL5bAyBKzpMfQB/mg6vQBzkh
|
|
24
|
-
aOKPwcreFj7TznBl89R5tNS9wZQfPVR98zgPyocddWhK18eQNMSBUnv4eeJ8PPbk
|
|
25
|
-
DovL+G8ajHDZ9fjH/+GVYHEMuiVdLarXrKJpHC1VfGTTUAp4NSEpUQ==
|
|
26
|
-
-----END CERTIFICATE-----
|
data.tar.gz.sig
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
U�>�ª뭌��\�~Vt�z�<!ݳ�f0���&�-֏P�K���*�Xj(����sba�"[C��_�rA��r�*�x��HR�]�[ck���ʙ�.�H�Va�&q8�xѪc�f� Y�k��O���ݨ�g�^��tkk�F�x�������B��7�K��,�Ę#���+�AA-�Nj�4�٫�b��3of_7Z(1��^�G��wWZ�z�x�w�Lv.��e.���+5��r�F=��yEC��T�Z�a{'6Z����EDa��z��T��aKQ��d�(e%JL�8{��q���1�R����,8��r�QbE��j�Bd���n��ϫ5�[)�c��P���;o~i��ц�6��^J�V�+A�I��@��+�
|
metadata.gz.sig
DELETED
|
Binary file
|