Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9024d46
release: merge develop into main for v0.26.0
DavidsonGomes Apr 22, 2026
566a534
release: merge develop into main for v0.27.0
DavidsonGomes Apr 22, 2026
13092e2
release: merge develop into main for v0.28.0
DavidsonGomes Apr 22, 2026
c84d615
refactor(telegram): centralize notifications in routines, remove from…
Apr 23, 2026
24af125
release: merge develop into main for v0.29.0
DavidsonGomes Apr 23, 2026
c6b635f
release: merge develop into main for v0.29.1
DavidsonGomes Apr 23, 2026
2d75828
release: merge develop into main for v0.29.2
DavidsonGomes Apr 23, 2026
23a2e44
release: merge develop into main for v0.29.3
DavidsonGomes Apr 23, 2026
86dbc3a
release: merge develop into main for v0.30.0
DavidsonGomes Apr 23, 2026
6b62b16
hotfix: merge activity parser fix into main for v0.30.0
DavidsonGomes Apr 23, 2026
e0ec879
release: merge develop into main for v0.30.1
DavidsonGomes Apr 23, 2026
cefae61
release: merge develop into main for v0.30.2
DavidsonGomes Apr 24, 2026
271f9e5
release: merge develop into main for v0.30.3
DavidsonGomes Apr 24, 2026
c6e80b3
release: merge develop into main for v0.30.4
DavidsonGomes Apr 24, 2026
56d78af
release: merge develop into main for v0.31.0
DavidsonGomes Apr 24, 2026
9d82da8
fix: merge brain-repo reconfigure-entry fix into main (v0.31.0)
DavidsonGomes Apr 24, 2026
16aae6c
release: merge develop into main for v0.32.0
DavidsonGomes Apr 24, 2026
60dc979
release: merge develop into main for v0.32.1
DavidsonGomes Apr 24, 2026
3066a50
release: merge develop into main for v0.32.2
DavidsonGomes Apr 24, 2026
277adb0
fix(telegram): remove reply() from prod-review-todoist skill
Apr 25, 2026
cab8966
fix(chat-bridge): resolve agent file from WORKSPACE_ROOT, not session…
Apr 25, 2026
fb9ebd2
release: merge develop into main for v0.32.3
DavidsonGomes Apr 25, 2026
cc27a4a
fix(telegram): eliminate duplicate notifications from inline reply() …
Apr 26, 2026
7400aa1
fix(telegram): remove all inline reply() calls from skill SKILL.md files
Apr 26, 2026
dc2f7ba
fix(telegram): move notification from Claude MCP to Python runner (de…
Apr 28, 2026
11629a4
Fix Brain Repo restoration flow: accept token in query string, valida…
dbadaniel Apr 30, 2026
31d4f7a
fix(dashboard): add websocket-client to fix terminal proxy WS error
dbadaniel Apr 30, 2026
8cf6afa
fix(dashboard): resolve DB migration crash (plugin_id vs slug)
dbadaniel Apr 30, 2026
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
8 changes: 3 additions & 5 deletions .claude/skills/fin-daily-pulse/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ workspace/finance/reports/daily/[C] YYYY-MM-DD-financial-pulse.html

Create the directory `workspace/finance/reports/daily/` if it does not exist.

## Step 8 — Confirm and notify (ONE Telegram message only)
## Step 8 — Confirm

Output the completion summary, then send **exactly one** Telegram message. Do NOT call `reply` more than once per run.
Output the completion summary in the terminal:

```
## Financial Pulse generated
Expand All @@ -171,6 +171,4 @@ Output the completion summary, then send **exactly one** Telegram message. Do NO
**Alerts:** {N} attention points
```

Call `reply` **once** with a short summary (do not send the full markdown above — send a compact version):

- Format: `[emoji] Financial Pulse [date] | MRR: R$ X,XXX | Receita: R$ X,XXX | Churn: X% | [N] alertas`
Do NOT send a Telegram message here — the caller handles notifications.
6 changes: 0 additions & 6 deletions .claude/skills/fin-monthly-close-kickoff/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,3 @@ Create the directory `workspace/finance/reports/monthly/` if it does not exist.
**Checklist:** X/10 completed
**Finance team pending items:** {N} items
```

### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + "Monthly Close" + month's result + pending items (2-3 lines)
6 changes: 0 additions & 6 deletions .claude/skills/fin-weekly-report/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,3 @@ Create the directory `workspace/finance/reports/weekly/` if it does not exist.
**MRR total:** R$ X,XXX (Stripe: R$ X,XXX | Evo Academy: R$ X,XXX) | **Projected 30d balance:** R$ XX,XXX
**Alerts:** {N} overdue accounts | {N} pending invoices
```

### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + "Financial Weekly" + revenue vs expenses + MRR + alerts (2-3 lines)
8 changes: 0 additions & 8 deletions .claude/skills/gog-email-triage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,3 @@ See `skills/gog/_shared/references/testing.md` for complete test plan.
- If email subject/sender contains obvious credentials or secrets, redact in output

- For recurring newsletters, suggest creating a filter/rule rather than manual archiving


### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
8 changes: 3 additions & 5 deletions .claude/skills/int-github-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,7 @@ Create directory if it does not exist.
- **Focus on action** — what needs the responsible person's attention, not just numbers


### Notify via Telegram
### Notification line

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
Write as the last line of your output:
TELEGRAM_MSG: 🐙 GitHub Review [date] | [main result in 1 line]
8 changes: 3 additions & 5 deletions .claude/skills/int-linear-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ Create the directory `workspace/projects/linear-reviews/` if it does not exist.
- **Be direct** — numbers, not narrative


### Notify via Telegram
### Notification line

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
Write as the last line of your output:
TELEGRAM_MSG: 📋 Linear Review [date] | [main result in 1 line]
11 changes: 5 additions & 6 deletions .claude/skills/int-sync-meetings/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,13 @@ When finished, present a short summary:

Without listing tasks one by one — just counts. If the user wants details, they ask.

### Step 9 — Notify via Telegram
### Step 9 — Notification line

Send the Step 8 summary via Telegram to the user using the `/int-telegram` skill:
- Chat ID: `YOUR_CHAT_ID`
- Use `reply(chat_id="YOUR_CHAT_ID", text="...")` via MCP
- Short format: emoji + title + meeting and task count
Only if at least one new meeting was processed, write this as the last line of your output:

If there are no new meetings (stopped at Step 2), do **NOT** send any Telegram message — stay silent. Only notify when at least one new meeting was processed.
TELEGRAM_MSG: 🎙️ Sync Fathom — N reunião(ões) processada(s) | N tarefas criadas

If no new meetings were processed (stopped at Step 2), do NOT write a TELEGRAM_MSG line.

## Notes

Expand Down
7 changes: 0 additions & 7 deletions .claude/skills/prod-dashboard/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,3 @@ Present a short summary:
**Health:** Product {status} | Community {status} | Financial {status} | Routines {status}
**Alerts:** {N} attention points
```

### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + health status of each area (1-3 lines)
- If there were no updates, send anyway with "no updates"
7 changes: 0 additions & 7 deletions .claude/skills/prod-review-todoist/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,3 @@ If the user wants to see details of what changed, they ask.
- **If unsure about the category**, use `[Operations]` as fallback
- **Execute first, report after** — no intermediate report


### Notify via Telegram

Upon completion, send **exactly ONE** Telegram message — do NOT call `reply()` more than once per run:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines, all combined in a single message)
- If the routine had no updates, send anyway with "no updates"
8 changes: 0 additions & 8 deletions .claude/skills/prod-trends/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,3 @@ Create `memory/trends/` if it does not exist.
- **If a source has no data, skip** — do not block due to a missing report
- **Focus on action** — each insight should lead to a concrete recommendation
- **Do not alarm without evidence** — red only when the metric truly indicates risk


### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
6 changes: 0 additions & 6 deletions .claude/skills/pulse-monthly/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,3 @@ Create the directory `workspace/community/reports/monthly/` if it does not exist
**Sentiment:** {trend} | **Resolution:** {X}%
**Highlights:** {N} features, {N} bugs, {N} docs gaps
```

### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + "Community Monthly" + MAM + sentiment + highlights (2-3 lines)
8 changes: 0 additions & 8 deletions .claude/skills/pulse-weekly/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,3 @@ Report saved to workspace/community/reports/weekly/
- **Docs gap is gold** — each question without docs becomes a backlog item
- **Comparison is fundamental** — always show trend vs previous week
- **Product insights** — the most valuable section, handle with care


### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
8 changes: 0 additions & 8 deletions .claude/skills/sage-strategy-digest/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,3 @@ Present a short and direct version.
- **Opinions flagged** — when it is opinion vs data, make it clear
- **One recommendation** — do not give 10 suggestions, give 1 clear one
- **Connect the dots** — the value of the digest is crossing areas, not repeating individual reports


### Notify via Telegram

Upon completion, send a short summary via Telegram to the user:
- Use the Telegram MCP: `reply(chat_id="YOUR_CHAT_ID", text="...")`
- Format: emoji + routine name + main result (1-3 lines)
- If the routine had no updates, send anyway with "no updates"
13 changes: 0 additions & 13 deletions .claude/skills/social-analytics-report/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,3 @@ workspace/social/reports/consolidated/[C] YYYY-MM-DD-social-analytics.html
```

Create directory if it does not exist.

### Step 9 — Telegram

Notify: `reply(chat_id="YOUR_CHAT_ID", text="...")`
Format:
```
📊 Social Analytics — {period}
👥 Total followers: {N} ({delta})
📹 YouTube: {subs} sub | {eng}% eng
📸 Instagram: {followers} fol | {eng}% eng
💼 LinkedIn: profile connected
🏆 Top: "{best content}" ({platform}, {eng}%)
```
5 changes: 0 additions & 5 deletions .claude/skills/social-youtube-report/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,3 @@ workspace/social/reports/youtube/[C] YYYY-MM-DD-youtube-{period}.html
```

Criar diretório if it does not exist.

### Step 7 — Telegram

Notify: `reply(chat_id="946857210", text="...")`
Format: emoji + canal + inscritos + delta + melhor vídeo
46 changes: 33 additions & 13 deletions ADWs/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,27 +303,47 @@ def run_skill(

Args:
notify_telegram: Controls post-skill Telegram notification.
False (default) — no notification (skill must NOT call reply() either).
True — appends notification instruction; reads chat_id from
TELEGRAM_CHAT_ID env var.
False (default) — no notification.
True — Python sends ONE Telegram after the skill; reads
chat_id from TELEGRAM_CHAT_ID env var.
"<chat_id>" — same as True but overrides the chat_id.

The agent is asked to output a line "TELEGRAM_MSG: <text>" in its stdout.
Python reads that line and calls send_telegram() exactly once.
The agent NEVER calls the Telegram MCP tool directly.
"""
prompt = f"Execute the skill /{skill_name} {args}".strip()
chat_id = None
if notify_telegram:
chat_id = (
notify_telegram
if isinstance(notify_telegram, str)
else os.environ.get("TELEGRAM_CHAT_ID", "")
) or None

prompt = f"Execute the skill /{skill_name} {args}".strip()
if chat_id:
prompt += (
f"\n\n---\n"
f"Ao finalizar, escreva na última linha do output:\n"
f"TELEGRAM_MSG: [emoji] [nome da rotina] [data] | [resultado 1] | [resultado 2]\n"
f"Apenas UMA linha TELEGRAM_MSG:. NÃO use a ferramenta Telegram/reply — "
f"o sistema Python lê essa linha e envia a notificação automaticamente.\n"
f"---"
)
if chat_id:
prompt += (
f"\n\nAo concluir TODOS os passos acima, envie UMA única mensagem Telegram via:"
f'\nreply(chat_id="{chat_id}", text="...")'
f"\nFormato: emoji + nome da rotina + principais resultados em 2-3 linhas."
f"\nCRÍTICO: chame reply() EXATAMENTE UMA VEZ, somente aqui no final."
f" Não envie mensagens intermediárias nem de progresso."
)
return run_claude(prompt, log_name or skill_name, timeout, agent=agent)

result = run_claude(prompt, log_name or skill_name, timeout, agent=agent)

if chat_id and result.get("returncode", -1) == 0:
stdout = result.get("stdout", "")
for line in reversed(stdout.splitlines()):
line = line.strip()
if line.startswith("TELEGRAM_MSG:"):
msg = line[len("TELEGRAM_MSG:"):].strip()
if msg:
send_telegram(msg, chat_id=chat_id)
break # only ever send one message

return result


def run_script(func, log_name: str = "unnamed", timeout: int = 120) -> dict:
Expand Down
5 changes: 3 additions & 2 deletions dashboard/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ def _cors_allowed_origins():
);
CREATE TABLE IF NOT EXISTS plugin_audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plugin_id TEXT NOT NULL,
slug TEXT NOT NULL,
action TEXT NOT NULL,
payload TEXT,
success INTEGER NOT NULL DEFAULT 1,
Expand All @@ -450,8 +450,9 @@ def _cors_allowed_origins():
PRIMARY KEY (plugin_slug, handler_path)
);
CREATE INDEX IF NOT EXISTS idx_plugins_status ON plugins_installed(status);
CREATE INDEX IF NOT EXISTS idx_plugin_audit_plugin ON plugin_audit_log(plugin_id, created_at);
CREATE INDEX IF NOT EXISTS idx_plugin_audit_slug ON plugin_audit_log(slug);
CREATE INDEX IF NOT EXISTS idx_hook_cb_disabled ON plugin_hook_circuit_state(disabled_until);

""")
_conn.commit()
# --- End plugins migration ---
Expand Down
24 changes: 20 additions & 4 deletions dashboard/backend/brain_repo/restore.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@


def _event(step: str, progress: int, message: str, error: bool = False) -> dict:
return {"step": step, "progress": progress, "message": message, "error": error}
event_type = "error" if error else "progress"
return {
"type": event_type,
"step": step,
"progress": progress,
"message": message,
"error": error
}


def _cleanup_staging(staging: Path) -> None:
Expand Down Expand Up @@ -47,6 +54,9 @@ def execute_restore(
"""
from brain_repo import git_ops, manifest as manifest_mod, migrations, secrets_scanner # type: ignore[import]

# ✅ Cria o diretório pai primeiro
STAGING_DIR.mkdir(parents=True, exist_ok=True)

staging = STAGING_DIR / f"restore-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}"
staging.mkdir(parents=True, exist_ok=True)

Expand All @@ -57,10 +67,16 @@ def execute_restore(
# If ref is not HEAD/default branch, checkout that specific ref
if ref and ref not in ("HEAD", "main", "master"):
ref_staging = staging / "_ref_extract"
ref_staging.mkdir(parents=True, exist_ok=True) # ✅ CREATE THE DIRECTORY FIRST
git_ops.checkout_ref(staging, ref, ref_staging)
# Use extracted ref content instead of full clone
shutil.rmtree(staging, ignore_errors=True)
ref_staging.rename(staging)
# ✅ Move o conteúdo de ref_staging para staging
for item in ref_staging.iterdir():
dest = staging / item.name
if dest.exists():
shutil.rmtree(dest, ignore_errors=True)
shutil.move(str(item), str(dest))
shutil.rmtree(ref_staging, ignore_errors=True)
except Exception as exc:
yield _event("clone", 5, f"Clone failed: {exc}", error=True)
_cleanup_staging(staging)
Expand Down Expand Up @@ -194,4 +210,4 @@ def execute_restore(

# ---------------------------------------------------------------- 10. complete
_cleanup_staging(staging)
yield _event("complete", 100, "Restore complete")
yield {"type": "complete", "step": "complete", "progress": 100, "message": "Restore complete", "error": False}
5 changes: 4 additions & 1 deletion dashboard/backend/routes/brain_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ def connect():
create_repo - Name of a new private repo to create (mutually exclusive with repo_url)
"""
data = request.get_json() or {}
token = data.get("token", "").strip()
token = data.get("token", "").strip() or request.args.get("token", "").strip()
repo_url = data.get("repo_url", "").strip()
create_repo = data.get("create_repo", "").strip()

Expand Down Expand Up @@ -527,6 +527,9 @@ def snapshots():
if not token:
abort(400, description="Could not decrypt stored token")

if not config.repo_owner or not config.repo_name:
abort(400, description="Repository info not fully configured — please reconnect the brain repo")

try:
from brain_repo.github_api import list_snapshots
result = list_snapshots(token, config.repo_owner, config.repo_name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ export default function RestoreFlow({ onComplete, onBack }: RestoreFlowProps) {
const [step, setStep] = useState<RestoreStep>('select-repo')
const [repoUrl, setRepoUrl] = useState('')
const [snapshot, setSnapshot] = useState<SelectedSnapshot | null>(null)
const [token, setToken] = useState('')

if (step === 'select-repo') {
return (
<RestoreSelectRepo
onNext={(url: string) => {
onNext={(url: string, pat: string) => {
setRepoUrl(url)
setToken(pat)
setStep('select-snapshot')
}}
onBack={onBack}
Expand All @@ -38,6 +40,7 @@ export default function RestoreFlow({ onComplete, onBack }: RestoreFlowProps) {
return (
<RestoreSelectSnapshot
repoUrl={repoUrl}
token={token}
onNext={(s: SelectedSnapshot) => {
setSnapshot(s)
setStep('confirm')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Repo {
}

interface RestoreSelectRepoProps {
onNext: (repoUrl: string) => void
onNext: (repoUrl: string, token: string) => void
onBack: () => void
}

Expand Down Expand Up @@ -65,7 +65,7 @@ export default function RestoreSelectRepo({ onNext, onBack }: RestoreSelectRepoP
setError(t('restore.selectRepo.selectRepo'))
return
}
onNext(selectedRepo.html_url)
onNext(selectedRepo.html_url, token.trim())
}

return (
Expand Down
Loading