brainiac 0.1.4 → 0.1.6
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/README.md +19 -0
- data/lib/brainiac/handlers/shared/inline_tags.rb +19 -1
- data/lib/brainiac/helpers.rb +17 -5
- data/lib/brainiac/version.rb +1 -1
- metadata +1 -2
- data/Gemfile.lock +0 -117
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 66eb8c75d924422aeb756ce7bc59dc503dc8fcab67095267b6c7940f461e6d8d
|
|
4
|
+
data.tar.gz: b7e22617f77bd5ee2882f3651a6622e00984e02193cd8a4554026c6e5245d6d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53d31acae1ef2a57be783175db90715adbdf0d3877beda17165feefbbd2e77df3528680984844e28ada13315582942a1aea54268895c203a9d388cb76d20c78a
|
|
7
|
+
data.tar.gz: 8b12a7305c26ac95c3a8c5682645039cac545845efeba325860859ad0299e5b6a429e0eeac157686b648746c82198392e791e55bc102b1f0fc876fe5c2493337
|
data/README.md
CHANGED
|
@@ -47,9 +47,28 @@ All four channels support inline tags in message/comment text. Tags are stripped
|
|
|
47
47
|
| `[branch:branch-name]` | ✓ | ✓ | ✓ | Target an existing branch — reuses its worktree if a work item exists |
|
|
48
48
|
| `[workitem:wi-abc123]` | ✓ | ✓ | ✓ | Target a specific work item by ID — agent works in its worktree |
|
|
49
49
|
| `[plan]` | ✓ | ✓ | — | Activate planning mode — agent gathers requirements before coding |
|
|
50
|
+
| `[fork]` `[fork:topic]` | — | ✓ | — | Fork conversation to a new thread — starts unbound (no worktree until implementation) |
|
|
50
51
|
|
|
51
52
|
Model keys come from the project's `allowed_models` config. Fizzy also supports model selection via card tags (e.g. adding an `opus` tag to the card).
|
|
52
53
|
|
|
54
|
+
### Conversation Branching (Discord)
|
|
55
|
+
|
|
56
|
+
Sometimes you want to spin off a tangent without polluting the current conversation's context. Brainiac supports two ways to branch:
|
|
57
|
+
|
|
58
|
+
**🌿 Reaction** — React with 🌿 on any message to fork from that point. The message becomes the root of a new thread.
|
|
59
|
+
|
|
60
|
+
**`[fork]` Tag** — Include `[fork]` or `[fork:topic]` in your message to immediately branch:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
@Galen [fork:belt-bug] Hey, unrelated to what we're working on, but I think belt deploy has a bug...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Both methods create a new thread that starts **unbound** — no worktree, no project lock-in. The agent can discuss, search brain, and read files across any registered project. When you ask for actual implementation work, the worktree is created at that point, scoped to the right repo.
|
|
67
|
+
|
|
68
|
+
This solves the "orphaned worktree" problem: if you notice a belt bug while working on brainiac, the branch doesn't create a useless brainiac worktree. The project is resolved when implementation starts.
|
|
69
|
+
|
|
70
|
+
The original conversation continues undisturbed. The branched thread gets its own memory file and context.
|
|
71
|
+
|
|
53
72
|
### Planning Mode
|
|
54
73
|
|
|
55
74
|
Add a `[plan]` tag to a Discord message or a `plan` tag to a Fizzy card to activate planning mode. Instead of jumping straight into implementation, the agent:
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
# fresh: true/false,
|
|
23
23
|
# deploy_intent: "dev01" / :auto / nil,
|
|
24
24
|
# worktree_override: "branch-name" or nil,
|
|
25
|
+
# fork_branch: "topic" or true or nil (fork conversation to a new thread),
|
|
25
26
|
# clean_text: "the message with all tags stripped"
|
|
26
27
|
# }
|
|
27
28
|
def parse_inline_tags(text)
|
|
@@ -38,6 +39,7 @@ def parse_inline_tags(text)
|
|
|
38
39
|
worktree_override: nil,
|
|
39
40
|
work_item: nil,
|
|
40
41
|
branch_override: nil,
|
|
42
|
+
fork_branch: nil,
|
|
41
43
|
clean_text: text.dup
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -110,7 +112,8 @@ def parse_work_item_tags(result)
|
|
|
110
112
|
result[:clean_text].sub!(match[0], "")
|
|
111
113
|
end
|
|
112
114
|
|
|
113
|
-
# [branch:branch-name] — preferred syntax for targeting a branch/worktree
|
|
115
|
+
# [branch:branch-name] — preferred syntax for targeting a branch/worktree.
|
|
116
|
+
# Note: bare [branch] (no colon/value) is handled below as a fork request.
|
|
114
117
|
if (match = result[:clean_text].match(/\[branch:([^\]]+)\]/i))
|
|
115
118
|
result[:branch_override] = match[1].strip
|
|
116
119
|
# Also set worktree_override for backward compat with plugins that read it
|
|
@@ -123,6 +126,21 @@ def parse_work_item_tags(result)
|
|
|
123
126
|
result[:work_item] = match[1].strip
|
|
124
127
|
result[:clean_text].sub!(match[0], "")
|
|
125
128
|
end
|
|
129
|
+
|
|
130
|
+
# [fork], [fork:topic], or bare [branch] — fork the conversation into a new thread.
|
|
131
|
+
# The new thread starts unbound (no worktree) until implementation work begins.
|
|
132
|
+
# Topic becomes the thread title; without a topic, the message content is used.
|
|
133
|
+
# - [fork:belt-bug] → fork with "belt-bug" as the topic
|
|
134
|
+
# - [fork] → fork with auto-generated topic from message content
|
|
135
|
+
# - [branch] → alias for [fork] (bare [branch] with no value)
|
|
136
|
+
if (match = result[:clean_text].match(/\[fork(?::([^\]]+))?\]/i))
|
|
137
|
+
result[:fork_branch] = match[1]&.strip || true
|
|
138
|
+
result[:clean_text].sub!(match[0], "")
|
|
139
|
+
elsif result[:clean_text].match?(/\[branch\]/i) && !result[:branch_override]
|
|
140
|
+
# Bare [branch] with no value acts as a fork request
|
|
141
|
+
result[:fork_branch] = true
|
|
142
|
+
result[:clean_text].sub!(/\[branch\]/i, "")
|
|
143
|
+
end
|
|
126
144
|
end
|
|
127
145
|
|
|
128
146
|
def parse_model_tag(result)
|
data/lib/brainiac/helpers.rb
CHANGED
|
@@ -353,10 +353,10 @@ def register_work_item(branch:, worktree: nil, project: nil, agent: nil, source:
|
|
|
353
353
|
work_item_id
|
|
354
354
|
end
|
|
355
355
|
|
|
356
|
-
# Update dispatch overrides on a work item (cli_provider, model, effort).
|
|
356
|
+
# Update dispatch overrides on a work item (cli_provider, model, effort, profile).
|
|
357
357
|
# Only non-nil values are stored; nil values are removed from existing overrides.
|
|
358
358
|
# Returns true if the work item was found and updated, false otherwise.
|
|
359
|
-
def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
|
|
359
|
+
def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, profile: nil, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
|
|
360
360
|
map = load_work_item_map
|
|
361
361
|
|
|
362
362
|
target_id = work_item_id
|
|
@@ -375,6 +375,7 @@ def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, work_
|
|
|
375
375
|
overrides["cli_provider"] = cli_provider if cli_provider
|
|
376
376
|
overrides["model"] = model if model
|
|
377
377
|
overrides["effort"] = effort if effort
|
|
378
|
+
overrides["profile"] = profile if profile
|
|
378
379
|
overrides.compact!
|
|
379
380
|
|
|
380
381
|
if overrides.empty?
|
|
@@ -417,16 +418,26 @@ end
|
|
|
417
418
|
# inline_cli_provider: — [cli:X] from current message (nil if not specified)
|
|
418
419
|
# inline_model: — resolved model from current message (nil if not specified)
|
|
419
420
|
# inline_effort: — [effort:X] from current message (nil if not specified)
|
|
421
|
+
# inline_profile: — [profile:X]/[p:X] from current message (nil if not specified)
|
|
420
422
|
#
|
|
421
|
-
#
|
|
422
|
-
|
|
423
|
+
# A profile sticks to the work item like model/effort do: [p:k+] once and every later
|
|
424
|
+
# untagged reply keeps running on k+ until a different [p:X] (e.g. [p:q]) replaces it.
|
|
425
|
+
# Unknown profile names are never persisted, so a typo can't pin a work item to a
|
|
426
|
+
# profile that doesn't exist (profile_spawn_env ignores it for this dispatch too).
|
|
427
|
+
#
|
|
428
|
+
# Returns: { cli_provider:, model:, effort:, profile: } (each String or nil)
|
|
429
|
+
def resolve_work_item_overrides(work_item_id: nil, branch: nil, inline_cli_provider: nil, inline_model: nil, inline_effort: nil,
|
|
430
|
+
inline_profile: nil)
|
|
423
431
|
stored = work_item_overrides_for(work_item_id: work_item_id, branch: branch)
|
|
432
|
+
inline_profile = inline_profile&.to_s&.downcase
|
|
433
|
+
persist_profile = inline_profile && profile_entry(inline_profile) ? inline_profile : nil
|
|
424
434
|
|
|
425
435
|
# Inline tags override stored values
|
|
426
436
|
resolved = {
|
|
427
437
|
cli_provider: inline_cli_provider || stored["cli_provider"],
|
|
428
438
|
model: inline_model || stored["model"],
|
|
429
|
-
effort: inline_effort || stored["effort"]
|
|
439
|
+
effort: inline_effort || stored["effort"],
|
|
440
|
+
profile: inline_profile || stored["profile"]
|
|
430
441
|
}
|
|
431
442
|
|
|
432
443
|
# Persist any new inline overrides to the work item for future dispatches
|
|
@@ -434,6 +445,7 @@ def resolve_work_item_overrides(work_item_id: nil, branch: nil, inline_cli_provi
|
|
|
434
445
|
new_overrides[:cli_provider] = inline_cli_provider if inline_cli_provider
|
|
435
446
|
new_overrides[:model] = inline_model if inline_model
|
|
436
447
|
new_overrides[:effort] = inline_effort if inline_effort
|
|
448
|
+
new_overrides[:profile] = persist_profile if persist_profile
|
|
437
449
|
|
|
438
450
|
update_work_item_overrides(work_item_id: work_item_id, branch: branch, **new_overrides) if new_overrides.any? && (work_item_id || branch)
|
|
439
451
|
|
data/lib/brainiac/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brainiac
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
@@ -117,7 +117,6 @@ extra_rdoc_files: []
|
|
|
117
117
|
files:
|
|
118
118
|
- CHANGELOG.md
|
|
119
119
|
- Gemfile
|
|
120
|
-
- Gemfile.lock
|
|
121
120
|
- README.md
|
|
122
121
|
- Rakefile
|
|
123
122
|
- bin/brainiac
|
data/Gemfile.lock
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
brainiac (0.1.4)
|
|
5
|
-
puma (~> 7.2)
|
|
6
|
-
rackup (~> 2.3)
|
|
7
|
-
sinatra (~> 4.1)
|
|
8
|
-
|
|
9
|
-
GEM
|
|
10
|
-
remote: https://rubygems.org/
|
|
11
|
-
specs:
|
|
12
|
-
ast (2.4.3)
|
|
13
|
-
base64 (0.3.0)
|
|
14
|
-
json (2.21.2)
|
|
15
|
-
language_server-protocol (3.17.0.6)
|
|
16
|
-
lint_roller (1.1.0)
|
|
17
|
-
logger (1.7.0)
|
|
18
|
-
minitest (5.27.0)
|
|
19
|
-
mustermann (3.1.1)
|
|
20
|
-
nio4r (2.7.5)
|
|
21
|
-
parallel (2.2.0)
|
|
22
|
-
parser (3.3.12.0)
|
|
23
|
-
ast (~> 2.4.1)
|
|
24
|
-
racc
|
|
25
|
-
prism (1.9.0)
|
|
26
|
-
puma (7.2.1)
|
|
27
|
-
nio4r (~> 2.0)
|
|
28
|
-
racc (1.8.1)
|
|
29
|
-
rack (3.2.7)
|
|
30
|
-
rack-protection (4.2.1)
|
|
31
|
-
base64 (>= 0.1.0)
|
|
32
|
-
logger (>= 1.6.0)
|
|
33
|
-
rack (>= 3.0.0, < 4)
|
|
34
|
-
rack-session (2.1.2)
|
|
35
|
-
base64 (>= 0.1.0)
|
|
36
|
-
rack (>= 3.0.0)
|
|
37
|
-
rackup (2.3.1)
|
|
38
|
-
rack (>= 3)
|
|
39
|
-
rainbow (3.1.1)
|
|
40
|
-
rake (13.4.2)
|
|
41
|
-
regexp_parser (2.12.0)
|
|
42
|
-
rubocop (1.88.0)
|
|
43
|
-
json (~> 2.3)
|
|
44
|
-
language_server-protocol (~> 3.17.0.2)
|
|
45
|
-
lint_roller (~> 1.1.0)
|
|
46
|
-
parallel (>= 1.10)
|
|
47
|
-
parser (>= 3.3.0.2)
|
|
48
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
49
|
-
regexp_parser (>= 2.9.3, < 3.0)
|
|
50
|
-
rubocop-ast (>= 1.49.0, < 2.0)
|
|
51
|
-
ruby-progressbar (~> 1.7)
|
|
52
|
-
unicode-display_width (>= 2.4.0, < 4.0)
|
|
53
|
-
rubocop-ast (1.50.0)
|
|
54
|
-
parser (>= 3.3.7.2)
|
|
55
|
-
prism (~> 1.7)
|
|
56
|
-
rubocop-performance (1.26.1)
|
|
57
|
-
lint_roller (~> 1.1)
|
|
58
|
-
rubocop (>= 1.75.0, < 2.0)
|
|
59
|
-
rubocop-ast (>= 1.47.1, < 2.0)
|
|
60
|
-
ruby-progressbar (1.13.0)
|
|
61
|
-
sinatra (4.2.1)
|
|
62
|
-
logger (>= 1.6.0)
|
|
63
|
-
mustermann (~> 3.0)
|
|
64
|
-
rack (>= 3.0.0, < 4)
|
|
65
|
-
rack-protection (= 4.2.1)
|
|
66
|
-
rack-session (>= 2.0.0, < 3)
|
|
67
|
-
tilt (~> 2.0)
|
|
68
|
-
tilt (2.9.0)
|
|
69
|
-
unicode-display_width (3.2.0)
|
|
70
|
-
unicode-emoji (~> 4.1)
|
|
71
|
-
unicode-emoji (4.2.0)
|
|
72
|
-
|
|
73
|
-
PLATFORMS
|
|
74
|
-
ruby
|
|
75
|
-
x86_64-linux
|
|
76
|
-
|
|
77
|
-
DEPENDENCIES
|
|
78
|
-
brainiac!
|
|
79
|
-
minitest (~> 5.25)
|
|
80
|
-
rake (~> 13.0)
|
|
81
|
-
rubocop (~> 1.75)
|
|
82
|
-
rubocop-performance (~> 1.25)
|
|
83
|
-
|
|
84
|
-
CHECKSUMS
|
|
85
|
-
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
86
|
-
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
87
|
-
brainiac (0.1.4)
|
|
88
|
-
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
89
|
-
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
90
|
-
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
91
|
-
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
92
|
-
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
93
|
-
mustermann (3.1.1) sha256=4c6170c7234d5499c345562ba7c7dfe73e1754286dcc1abb053064d66a127198
|
|
94
|
-
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
|
|
95
|
-
parallel (2.2.0) sha256=e1059c5fd7b649558a0aec38a769f06a42942bdb40503d005a59c352fe011cd8
|
|
96
|
-
parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
|
|
97
|
-
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
98
|
-
puma (7.2.1) sha256=d7bf0e9cabd532e0d401e142cd94e3ac531e993610e2d80e6fbf9c26961414b0
|
|
99
|
-
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
100
|
-
rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7
|
|
101
|
-
rack-protection (4.2.1) sha256=cf6e2842df8c55f5e4d1a4be015e603e19e9bc3a7178bae58949ccbb58558bac
|
|
102
|
-
rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
|
|
103
|
-
rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
|
|
104
|
-
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
105
|
-
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
106
|
-
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
107
|
-
rubocop (1.88.0) sha256=e420ddf1662d0ef34bc8a2910ac4b396a7ddda0b51a708264405241734b08e0b
|
|
108
|
-
rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
|
|
109
|
-
rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
|
|
110
|
-
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
111
|
-
sinatra (4.2.1) sha256=b7aeb9b11d046b552972ade834f1f9be98b185fa8444480688e3627625377080
|
|
112
|
-
tilt (2.9.0) sha256=da5735d0280bba96e9a91041bb14aee435ccad5c17b0fa519249ae543d9aa3a5
|
|
113
|
-
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
114
|
-
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
115
|
-
|
|
116
|
-
BUNDLED WITH
|
|
117
|
-
4.0.10
|