Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/mlc_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
},
{
"pattern": "^https://engineer-brain.dev"
},
{
"pattern": "workshop-brainstack-day0\\.md"
}
],
"timeout": "10000",
Expand Down
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ website/.docusaurus/
# Team Brain secrets (per-workspace only — do not blanket-ignore all of .team-brain/)
.team-brain/credentials.json
.team-brain/**/credentials.json
.team-brain/team.yaml
.team-brain/**/team.yaml
.team-brain/share-bundle.txt
.team-brain/**/share-bundle.txt
.team-brain/cache/
.team-brain/**/cache/
.team-brain/sync/
.team-brain/**/sync/
!.team-brain/**/.gitkeep
# Commit-safe pin (.team-brain/project.json) SHOULD be committed — never put anon/api_key/invite there.
# team.yaml often holds URL+anon for local use — prefer env / project.public.env; do not commit anon.
# team.yaml holds URL+anon locally — gitignored above; prefer supabase/project.public.env.
supabase/project.public.env

# Bootstrap-generated combined SQL (may be large; never commit crew secrets beside it)
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Shared AI memory for crews working on the same Jira initiative. When one enginee

```mermaid
flowchart LR
subgraph Crew["Crew on AAP-81423"]
subgraph Crew["Crew on YOU_JIRA_TICKET_HERE"]
A1[Alice's Agent]
A2[Bob's Agent]
A3[Carol's Agent]
Expand All @@ -172,7 +172,7 @@ flowchart LR
end

subgraph Local["Each Engineer"]
CACHE["cache/AAP-81423.json"]
CACHE["cache/YOU_JIRA_TICKET_HERE.json"]
MCP[MCP Tools]
end

Expand All @@ -199,10 +199,10 @@ flowchart LR
### How It Works

1. **Admin creates team** — `register "Team Atlas" "Alice"` → Supabase project + invite code
2. **Teammates join** — `onboard <invite> "Bob" AAP-81423` → credentials + Jira key
3. **Start sync mode** — "I'm starting on AAP-81423 — start Team Brain sync"
2. **Teammates join** — `onboard <invite> "Bob" YOU_JIRA_TICKET_HERE` → credentials + Jira key
3. **Start sync mode** — "I'm starting on YOU_JIRA_TICKET_HERE — start Team Brain sync"
4. **Agents collaborate** — `remember` findings → peers get realtime push → `recall` when needed
5. **Generate artifacts** — `breakdown AAP-81423` → story/spike draft from crew memory
5. **Generate artifacts** — `breakdown YOU_JIRA_TICKET_HERE` → story/spike draft from crew memory

```
Alice researches auth options → remember "prefer OAuth2 over SAML for SSO"
Expand Down Expand Up @@ -411,10 +411,10 @@ bash install.sh continue-dev ~/my-workspace
"team-brain register …" → admin creates team once

# Sync mode (Cursor chat — one line to start crew work):
"I'm starting on AAP-81423 — start Team Brain sync."
"Wake Team Brain sync for AAP-81423 and continue."
"Stop Team Brain sync for AAP-81423."
"Breakdown AAP-81423 from Team Brain memory."
"I'm starting on YOU_JIRA_TICKET_HERE — start Team Brain sync."
"Wake Team Brain sync for YOU_JIRA_TICKET_HERE and continue."
"Stop Team Brain sync for YOU_JIRA_TICKET_HERE."
"Breakdown YOU_JIRA_TICKET_HERE from Team Brain memory."
```


Expand All @@ -424,9 +424,9 @@ bash install.sh continue-dev ~/my-workspace
bash install.sh cursor ~/my-workspace
# Personal: /engineer-brain sync
# Admin once: bash core/scripts/team-brain-api.sh register "Team Atlas" "You"
# Teammate: bash core/scripts/team-brain-api.sh onboard <INVITE> "Name" AAP-81423
# Teammate: bash core/scripts/team-brain-api.sh onboard <INVITE> "Name" YOU_JIRA_TICKET_HERE
# (Admin: own Supabase project → fill local project.public.env; joiners get URL+anon+invite)
# Then in Cursor: I'm starting on AAP-81423 — start Team Brain sync.
# Then in Cursor: I'm starting on YOU_JIRA_TICKET_HERE — start Team Brain sync.
```

---
Expand Down
164 changes: 164 additions & 0 deletions core/scripts/team-brain-admin-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
# Team Brain — one-shot ADMIN setup
#
# Creates the crew (register → admin role). Colleagues use team-brain-member-setup.sh.
#
# DEMO DEFAULTS (Brainstack Day 0 workshop only — change for your crew):
# Epic: KAN-4 · Crew: Workshop Crew
# Override: --jira YOUR-EPIC --team "Your Crew" --jira-site https://your-org.atlassian.net
# Or env: TEAM_BRAIN_DEMO_JIRA_KEY, TEAM_BRAIN_DEMO_TEAM_NAME, TEAM_BRAIN_DEMO_JIRA_SITE
#
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BOOTSTRAP="$SCRIPT_DIR/team-brain-bootstrap.sh"
API="$SCRIPT_DIR/team-brain-api.sh"

# shellcheck source=team-brain-secret-utils.sh
source "$SCRIPT_DIR/team-brain-secret-utils.sh"

# --- demo defaults (override for production crews) ---
DEFAULT_JIRA_KEY="${TEAM_BRAIN_DEMO_JIRA_KEY:-KAN-4}"
DEFAULT_JIRA_TITLE="${TEAM_BRAIN_DEMO_JIRA_TITLE:-Brainstack context layer — workshop spike}"
DEFAULT_JIRA_SITE="${TEAM_BRAIN_DEMO_JIRA_SITE:-https://brainstack-org.atlassian.net}"
DEFAULT_TEAM_NAME="${TEAM_BRAIN_DEMO_TEAM_NAME:-Workshop Crew}"

ADMIN_NAME="${TEAM_BRAIN_ADMIN_NAME:-}"
TEAM_NAME="$DEFAULT_TEAM_NAME"
JIRA_KEY="$DEFAULT_JIRA_KEY"
JIRA_TITLE="$DEFAULT_JIRA_TITLE"
JIRA_SITE="$DEFAULT_JIRA_SITE"
SKIP_START=0
DRY_RUN=0
BOOTSTRAP_ARGS=()

die() { echo "error: $*" >&2; exit 1; }
info() { echo "→ $*" >&2; }
ok() { echo "✓ $*" >&2; }
warn() { echo "⚠ $*" >&2; }

print_demo_notice() {
cat >&2 <<EOF

──────────────────────────────────────────────────────────────────
Demo defaults (workshop): epic ${JIRA_KEY} · crew "${TEAM_NAME}"
For your own crew, pass --jira YOUR-EPIC --team "Your Crew"
(and --jira-site if not brainstack-org). See docs/workshop-brainstack-day0.md
──────────────────────────────────────────────────────────────────

EOF
}

usage() {
cat <<EOF
Team Brain — one-shot ADMIN setup

bash core/scripts/team-brain-admin-setup.sh --admin "Your Name" [options]

Required:
--admin, -a "Name" Admin display name (creates crew + admin role)

Demo defaults (override for your epic):
--jira KEY Default: $DEFAULT_JIRA_KEY (demo only)
--team NAME Default: $DEFAULT_TEAM_NAME (demo only)
--jira-site URL Default: $DEFAULT_JIRA_SITE

Bootstrap options (passed through):
--url URL --anon KEY --db-url URL --local --skip-migrations --dry-run

Colleagues then run:
bash core/scripts/team-brain-member-setup.sh --invite <CODE> --name "Bob" --role member

See: docs/workshop-brainstack-day0.md
EOF
}

while [ $# -gt 0 ]; do
case "$1" in
--admin|-a) ADMIN_NAME="${2:-}"; shift 2 ;;
--team) TEAM_NAME="${2:-}"; shift 2 ;;
--jira) JIRA_KEY="${2:-}"; shift 2 ;;
--jira-site) JIRA_SITE="${2:-}"; shift 2 ;;
--jira-title) JIRA_TITLE="${2:-}"; shift 2 ;;
--skip-start) SKIP_START=1; shift ;;
--dry-run) DRY_RUN=1; BOOTSTRAP_ARGS+=(--dry-run); shift ;;
--help|-h) usage; exit 0 ;;
--url|--anon|--db-url)
[ $# -ge 2 ] || die "$1 requires a value"
BOOTSTRAP_ARGS+=("$1" "$2"); shift 2 ;;
--local|--skip-migrations)
BOOTSTRAP_ARGS+=("$1"); shift ;;
*)
die "unknown option: $1 (colleagues use team-brain-member-setup.sh)"
;;
esac
done

[ -n "$ADMIN_NAME" ] || die "--admin \"Your Name\" is required (see --help)"
[ -f "$BOOTSTRAP" ] || die "bootstrap script not found: $BOOTSTRAP"

JIRA_KEY=$(echo "$JIRA_KEY" | tr '[:lower:]' '[:upper:]')
print_demo_notice

cd "$REPO_ROOT"
export TEAM_BRAIN_DIR="${TEAM_BRAIN_DIR:-$REPO_ROOT/.team-brain}"

bash "$BOOTSTRAP" \
--team "$TEAM_NAME" \
--admin "$ADMIN_NAME" \
--jira "$JIRA_KEY" \
--jira-title "$JIRA_TITLE" \
--jira-site "$JIRA_SITE" \
--write-env \
"${BOOTSTRAP_ARGS[@]}"

if [ "$DRY_RUN" -eq 1 ]; then
info "Dry run — would run pin set, doctor, start $JIRA_KEY"
exit 0
fi

info "Setting commit-safe pin"
bash "$API" pin set --jira "$JIRA_KEY" --team-name "$TEAM_NAME" | jq '{pinned, default_jira_key, team_name, path}' 2>/dev/null \
|| bash "$API" pin set --jira "$JIRA_KEY" --team-name "$TEAM_NAME"

info "Doctor check"
bash "$API" doctor || warn "doctor reported issues — fix migrations before going live"

if [ "$SKIP_START" -eq 0 ]; then
info "Starting sync mode ($JIRA_KEY)"
bash "$API" start "$JIRA_KEY"
bash "$API" sync-status "$JIRA_KEY" | jq '{jira_key, mode}' 2>/dev/null \
|| bash "$API" sync-status "$JIRA_KEY"
fi

invite=""
invite=$(bash "$API" whoami 2>/dev/null | jq -r '.invite_code // empty' || true)

invite_mask=""
if [ -n "$invite" ]; then
invite_mask=$(tb_mask_secret "$invite")
fi
bundle_path=$(tb_share_bundle_path)

echo ""
echo "═══════════════════════════════════════════════════════════════"
echo " Admin setup complete"
echo "═══════════════════════════════════════════════════════════════"
echo " Role: admin"
echo " Epic: $JIRA_KEY — $JIRA_TITLE"
if [ -n "$invite" ]; then
echo " Invite: $invite_mask"
echo " Full invite + anon: $bundle_path (gitignored — share via DM only)"
else
echo " Invite: (run: bash core/scripts/team-brain-api.sh whoami)"
echo " Full bundle: $bundle_path (if bootstrap wrote it)"
fi
echo ""
echo " Colleague (member) — use invite from $bundle_path or credentials.json:"
echo " bash core/scripts/team-brain-member-setup.sh --invite <INVITE> --name \"Bob\" --role member"
echo ""
echo " Do NOT paste invite/anon into git, PRs, or public Slack."
echo " (Demo used KAN-4 / Workshop Crew — pass --jira / --team for your epic.)"
echo "═══════════════════════════════════════════════════════════════"
tb_print_no_paste_warning
8 changes: 7 additions & 1 deletion core/scripts/team-brain-api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ CRED_FILE="${TEAM_BRAIN_CREDENTIALS:-$TEAM_DIR/credentials.json}"
CONFIG_YAML="${TEAM_BRAIN_CONFIG:-$TEAM_DIR/team.yaml}"
PIN_FILE="${TEAM_BRAIN_PIN:-$TEAM_DIR/project.json}"

# shellcheck source=team-brain-secret-utils.sh
source "$SCRIPT_DIR/team-brain-secret-utils.sh"

die() { echo "error: $*" >&2; exit 1; }

# Commit-safe pin (#39). Never put anon / api_key / invite here.
Expand Down Expand Up @@ -439,7 +442,10 @@ save_credentials() {
chmod 600 "$CRED_FILE" 2>/dev/null || true
echo "Wrote credentials → $CRED_FILE" >&2
if jq -e '.role == "admin" and (.invite_code // null) != null and .invite_code != ""' >/dev/null 2>&1 <<<"$json"; then
echo "Invite code (share with teammates; keep private from the public internet): $(jq -r .invite_code <<<"$json")" >&2
local invite_raw invite_mask
invite_raw=$(jq -r .invite_code <<<"$json")
invite_mask=$(tb_mask_secret "$invite_raw")
echo "Invite code (full value in $CRED_FILE and .team-brain/share-bundle.txt — share via DM only): $invite_mask" >&2
fi
echo "API key saved (keep private)." >&2
}
Expand Down
58 changes: 48 additions & 10 deletions core/scripts/team-brain-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ PUBLIC_ENV="${TEAM_BRAIN_PUBLIC_ENV:-$REPO_ROOT/supabase/project.public.env}"
MIGRATIONS_DIR="$REPO_ROOT/supabase/migrations"
SUPABASE_DIR="$REPO_ROOT/supabase"

# shellcheck source=team-brain-secret-utils.sh
source "$SCRIPT_DIR/team-brain-secret-utils.sh"

die() { echo "error: $*" >&2; exit 1; }
info() { echo "→ $*" >&2; }
ok() { echo "✓ $*" >&2; }
Expand Down Expand Up @@ -60,7 +63,7 @@ Migration strategy (first match wins):

Examples:
# Hosted project you already filled in project.public.env + linked CLI:
bash core/scripts/team-brain-bootstrap.sh --team "Spike Crew" --admin "Alice" --jira AAP-81423
bash core/scripts/team-brain-bootstrap.sh --team "Spike Crew" --admin "Alice" --jira YOU_JIRA_TICKET_HERE

# Pass URL/anon + DB URL for migrations:
bash core/scripts/team-brain-bootstrap.sh --team "Spike Crew" --admin "Alice" \\
Expand Down Expand Up @@ -314,6 +317,39 @@ print_share_bundle() {
local anon="$4"
local jira="$5"
local site="${6:-https://your-org.atlassian.net}"
local jira_key="${jira:-JIRA-KEY}"
local invite_mask anon_mask bundle_path full_bundle

invite_mask=$(tb_mask_secret "$invite")
anon_mask=$(tb_mask_secret "$anon")
export TEAM_BRAIN_DIR="${TEAM_BRAIN_DIR:-$REPO_ROOT/.team-brain}"

full_bundle=$(cat <<EOF
Team Brain — share bundle (admin → crew) — FULL SECRETS — gitignored local file
Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ") UTC

Team: ${team}
Invite: ${invite}
URL: ${url}
Anon: ${anon}
Jira key: ${jira:-"(give teammates the ticket key)"}
Jira: ${site}

Joiner (one shot):
bash core/scripts/team-brain-member-setup.sh \\
--invite ${invite} \\
--name "Their Name" \\
--url "${url}" \\
--anon "${anon}" \\
--role member

Or set supabase/project.public.env (gitignored) with URL + anon, then:
bash core/scripts/team-brain-member-setup.sh --invite ${invite} --name "Their Name" --role member

Do NOT commit this file or paste into public chat / PRs.
EOF
)
bundle_path=$(tb_write_share_bundle_file "$full_bundle")

cat <<EOF

Expand All @@ -322,31 +358,33 @@ print_share_bundle() {
╚══════════════════════════════════════════════════════════════════╝

Team: ${team}
Invite: ${invite}
Invite: ${invite_mask}
URL: ${url}
Anon: ${anon}
Anon: ${anon_mask}
Jira key: ${jira:-"(give teammates the ticket key)"}
Jira: ${site}

Full invite + anon (for DM to joiners only):
→ ${bundle_path}

Joiner checklist (no Supabase account needed):
1. Clone brainstack (or use installed skills)
2. Put URL + anon in local supabase/project.public.env (or .team-brain/team.yaml / env)
3. Admin assigns role (member = contributor, viewer = read-only), then run:
bash core/scripts/team-brain-api.sh onboard ${invite} "Their Name" ${jira:-JIRA-KEY} --role member
# or: ... --role viewer
4. Start sync:
bash core/scripts/team-brain-api.sh start ${jira:-JIRA-KEY}
2. Admin sends joiner the file above (or URL + anon + invite via DM — never public Slack/PR)
3. Joiner runs:
bash core/scripts/team-brain-member-setup.sh --invite <INVITE> --name "Their Name" --role member
(Use values from ${bundle_path})

Admin after bootstrap:
bash core/scripts/team-brain-api.sh list-members
bash core/scripts/team-brain-api.sh pending list ${jira:-JIRA-KEY} # review queued overrides (#67)
bash core/scripts/team-brain-api.sh pending list ${jira_key} # review queued overrides (#67)
bash core/scripts/team-brain-api.sh doctor

Do NOT commit live URL/anon/invite to a public fork.
Do NOT share service_role or member api_key in chat logs that get committed.

Admin credentials saved under .team-brain/credentials.json (gitignored).
EOF
tb_print_no_paste_warning
}

# ----- main -----
Expand Down
Loading
Loading