Toate conturile Claude logate pe același PC văd aceleași chat-uri Code din aplicația desktop.

Acest repo e un plugin Claude Code care învață asistentul AI cum să lucreze cu platforma Symbai (sistem de management pentru restaurante și hoteluri). Conține:
expo-aplicatii-mobile.md: Symbai POS, Symbai Portal, Symbai Staff, branding, emulator Android, build, GP/Viva app2app, push). Plus referințele transversale de navigare și operare MCP — agent-operare-avansata.md (standardul „consultant + inginer + QA" pentru task-uri complexe), erp-manufacturing-benchmark.md (pattern-uri ERP/MES pentru producție: BoM, MPS, readiness, QC, capacitate), navigare-rapida.md (cheat-sheet compact intenție→URL), navigare.md (cum DUCI userul pe pagină), claude-code-mcp-operare.md (doctrina skill→knowledge→MCP live→dry-run→confirmare→verificare), harta-aplicatiei.md (indexul exhaustiv al paginilor) și tools-mcp.md (catalog generat din registry-ul live al tool-urilor MCP, cu modelul de permisiuni) — și folderul knowledge/onboarding/ (planul general + 12 faze pentru configurarea de la zero a unui client nou, folosit de skill-ul onboarding-symbai).Pluginul se folosește împreună cu conexiunea MCP la instanța ta Symbai (date live + acțiuni reale). Conexiunea o adaugi separat din portalul Hub. Pentru email marketing, skill-urile sunt optimizate pentru flow-uri complexe, deliverability/warm-up, trimitere predictivă pe ore individuale și atribuire conversii din POS/rezervări.
Metoda recomandată — prin fișier, cu AUTO-UPDATE pornit din start. Funcționează în orice mediu (inclusiv aplicația desktop, unde comanda /plugin poate lipsi) și e singura care pornește actualizarea automată. Editează settings.json din folderul Claude (Windows: C:\Users\<nume>\.claude\settings.json; macOS/Linux: ~/.claude/settings.json; creează-l dacă lipsește) și îmbină următoarele chei, păstrând restul fișierului:
{
"extraKnownMarketplaces": {
"symbai": {
"source": { "source": "git", "url": "https://github.com/sinesteziadrimo/symbaimemory.git" },
"autoUpdate": true
}
},
"enabledPlugins": { "symbai-core@symbai": true }
}
⚠️
"autoUpdate": trueeste obligatoriu — fără el rămâi blocat pe versiunea de la instalare și nu mai primești ghidurile noi. Comanda/plugin marketplace addnu pornește auto-update — de aceea folosim fișierul.
Începând cu symbai-core v0.27, pluginul pornește la fiecare sesiune un hook fail-safe de self-update: aduce clona locală de marketplace symbai la zi (fast-forward; re-sincronizare cu upstream dacă a divergat dar nu are modificări locale) și, dacă a apărut o versiune nouă, face singur upgrade-ul instalării — versiunea nouă se încarcă la următoarea pornire. E necesar pentru că auto-update-ul nativ al pluginurilor nu rulează în aplicația desktop (procesul e pornit cu DISABLE_AUTOUPDATER=1); hook-ul nu depinde de el și nu cere pași manuali clientului.
Apoi instalează pluginul o singură dată (activarea efectivă), pe metoda disponibilă la tine:
# caseta de chat Claude Code:
/plugin install symbai-core@symbai
# aplicația desktop fără /plugin: Customize / Settings → Plugins/Marketplaces → instalează „symbai-core" din marketplace-ul „symbai"
# terminal, doar dacă `claude --version` merge:
claude plugin install symbai-core@symbai
Repornește Claude Code. De aici încolo pluginul se actualizează singur la fiecare pornire — nu mai faci nimic.
Separat, din portalul tău Symbai Hub → Acces AI, ia instrucțiunile de conectare la datele instanței tale (server MCP symbai). Dacă ceva nu merge la conectare, spune-i asistentului „conectează-mă la Symbai" — skill-ul conecteaza-symbai îl ghidează (inclusiv la eroarea „Some MCP servers could not be loaded").
De la symbai-core v0.27 nu trebuie să faci nimic — hook-ul de self-update din plugin aduce marketplace-ul la zi și upgradează pluginul la fiecare pornire de sesiune (cu un throttle de 6h), independent de auto-update-ul nativ al Claude Code (care în aplicația desktop nici nu rulează). "autoUpdate": true din settings rămâne recomandat ca plasă suplimentară pe CLI.
Dacă ești blocat pe o versiune veche (≤0.26.x, dinainte de self-update) sau vrei ultima versiune imediat: scrie în chat „actualizează skill-urile Symbai" (skill-ul symbai-update conține pașii de recovery — un git merge --ff-only pe clona de marketplace + rularea scriptului self-heal-marketplace.mjs --force), sau, dacă ai comanda disponibilă, /plugin marketplace update symbai. După o singură astfel de trecere pe v0.27+, actualizarea rămâne automată pe veci.
Echipa Symbai urcă fișiere noi de skill/knowledge în acest repo (pe main) și bumpează versiunea în plugins/symbai-core/.claude-plugin/plugin.json, plugins/symbai-core/.codex-plugin/plugin.json și intrarea din .claude-plugin/marketplace.json. Clienții cu autoUpdate: true le primesc automat la următoarea pornire; ceilalți, manual cu /plugin marketplace update symbai.
Bumpul de versiune e obligatoriu la fiecare livrare. Claude Code servește pluginul dintr-un folder fixat pe versiune (
cache/symbai/symbai-core/<versiune>/); dacăversionnu se schimbă, clienții rămân pe copia veche din cache chiar dacă au tras commit-uri noi.
marketplace.jsonCâmpul category al unei intrări din .claude-plugin/marketplace.json îi spune lui Symbai Connect ce să facă cu pluginul pe PC-urile clienților. Ajunge la ei doar cu un pachet nou: numărul pachetului e versiunea symbai-core, deci se bumpează și ea, apoi build-package.ps1 -Publish.
category | Ce face Connect în Claude Code (Codex nu primește mod-uri) |
|---|---|
"mod" | Pornește modul (<nume>@symbai: true în ~/.claude/settings.json + claude plugin install), fără versiune nouă de Connect. Un false pus de om rămâne. |
"mod-retras" | Oprirea centrală: scrie false și rulează claude plugin uninstall <nume>@symbai, care scoate cheia și copia din cache. |
Scoaterea unui mod din pachet nu îl oprește: intrarea true și copia din cache rămân. Un mod care se poartă rău se retrage cu "mod-retras":
true de sub @symbai. Un false pus de om rămâne, iar un plugin cu același nume din alt marketplace nu se atinge.build-package.ps1 cere plugin.json pentru fiecare intrare. symbai-core nu se poate retrage."mod" repornește modul acolo unde dezinstalarea a reușit. Unde a eșuat, a rămas false, care nu se mai deosebește de alegerea omului, deci modul rămâne oprit.symbai-ui, pe care îl are într-o listă fixă), dar nici nu-l oprește. Oprirea propriu-zisă vine cu versiunea de Connect de după 1.2.113..claude-plugin/marketplace.json # marketplace (listează pluginul symbai-core)
plugins/symbai-core/
.claude-plugin/plugin.json # manifestul pluginului
skills/<nume>/SKILL.md # workflow-uri
knowledge/*.md # cunoștințe pe module
Skill-urile urmează standardul deschis Agent Skills (SKILL.md), deci funcționează în Claude Code. Pentru alte unelte (ex. Codex) suportul poate diferi — conexiunea MCP funcționează oricum cross-tool.
hooks/register.ts 133 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// What scripts/sync.ps1 prints: one line of JSON.
4type AccountReport = {
5 account: string
6 email: string | null
7 isActive: boolean
8 chats: number
9 added: number
10 updated: number
11}
12
13export type SyncReport = {
14 ok: boolean
15 busy?: boolean
16 added?: number
17 updated?: number
18 withoutConversation?: number
19 accounts?: AccountReport[]
20 errors?: string[]
21}
22
23const LAST_RUN = 'lastRunAt'
24// The store is shared by every session on this PC, so these gaps hold
25// across all of them: many open chats do not each start a sync.
26const GAP_AFTER_START_MS = 60_000
27const GAP_AFTER_TURN_MS = 5 * 60_000
28// The app reads an account's list only when it starts or the account logs in;
29// closing its window leaves it running beside the clock.
30const RESTART_HINT =
31 'Lista se reîncarcă doar la pornirea aplicației: închide Claude de tot (clic dreapta pe iconița Claude de lângă ceas → Quit) și deschide-l din nou. Celelalte conturi văd chat-urile la următoarea logare.'
32
33export const chats = (n: number): string => {
34 if (n === 1) return '1 chat'
35 const tail = n % 100
36 return n > 0 && (tail === 0 || tail >= 20) ? `${n} de chat-uri` : `${n} chat-uri`
37}
38
39async function sync($: EngineInterface, gapMs: number): Promise<SyncReport | undefined> {
40 // The app's Code tab keeps its lists under %APPDATA%: Windows only.
41 if ((await $.env.get('APPDATA')) === undefined) return undefined
42 const now = await $.clock.now()
43 const lastRun = Number((await $.store.get(LAST_RUN)) ?? 0)
44 if (now - lastRun < gapMs) return undefined
45 await $.store.set(LAST_RUN, now)
46
47 const run = await $.process.run(
48 [
49 'powershell.exe',
50 '-NoProfile',
51 '-NonInteractive',
52 '-ExecutionPolicy',
53 'Bypass',
54 '-File',
55 `${$.plugin.root}/scripts/sync.ps1`,
56 ],
57 { timeoutMs: 120_000 },
58 )
59 const line = run.stdout.trim().split(/\r?\n/).pop() ?? ''
60 try {
61 return JSON.parse(line) as SyncReport
62 } catch {
63 return { ok: false, errors: [run.stderr.trim() || `codul de ieșire ${run.exitCode}`] }
64 }
65}
66
67export function describe(report: SyncReport | undefined): string {
68 if (report === undefined) return 'Chat-uri comune: funcționează doar pe Windows, cu aplicația desktop Claude.'
69 if (report.busy) return 'Chat-uri comune: o altă sincronizare rulează chiar acum. Încearcă din nou peste un minut.'
70
71 const accounts = report.accounts ?? []
72 const lines = [`Chat-uri comune: am sincronizat lista pentru ${accounts.length} conturi de pe acest PC.`]
73 for (const account of accounts) {
74 const who = account.email ?? `cont ${account.account.slice(0, 8)} (emailul apare după prima lui sesiune)`
75 const changes = [
76 account.added > 0 ? `${account.added} noi` : '',
77 account.updated > 0 ? `${account.updated} actualizate` : '',
78 ].filter(Boolean)
79 lines.push(
80 `- ${who}${account.isActive ? ' (contul tău)' : ''}: ${chats(account.chats)}` +
81 (changes.length > 0 ? `, ${changes.join(', ')}` : ''),
82 )
83 }
84 lines.push(RESTART_HINT)
85 if ((report.withoutConversation ?? 0) > 0) {
86 lines.push(
87 `${chats(report.withoutConversation ?? 0)} nu mai au conversația salvată pe acest PC, așa că nu au fost copiate.`,
88 )
89 }
90 for (const error of report.errors ?? []) lines.push(`Eroare: ${error}`)
91
92 return lines.join('\n')
93}
94
95function syncSoon($: EngineInterface, gapMs: number): void {
96 $.clock.after(2_000, () => {
97 sync($, gapMs)
98 .then(report => {
99 const mine = report?.accounts?.find(account => account.isActive)
100 if (mine !== undefined && mine.added > 0) {
101 $.ui.toast(
102 `Chat-uri comune: ${chats(mine.added)} din alte conturi au intrat în lista ta. ${RESTART_HINT}`,
103 )
104 }
105 })
106 .catch(() => undefined)
107 })
108}
109
110export const register: Register = on => {
111 on('session.start', async ($, e, next) => {
112 await $.command.register({
113 name: 'chaturi-comune',
114 description: 'Copiază acum chat-urile tuturor conturilor Claude de pe acest PC în lista fiecărui cont.',
115 })
116 const started = await next(e)
117 syncSoon($, GAP_AFTER_START_MS)
118
119 return started
120 })
121
122 on('turn.complete', async ($, e, next) => {
123 const completed = await next(e)
124 syncSoon($, GAP_AFTER_TURN_MS)
125
126 return completed
127 })
128
129 on('command.run', { command: 'chaturi-comune' }, async $ => ({ text: describe(await sync($, 0)) })).catch(
130 () => ({ text: 'Chat-uri comune: sincronizarea nu a putut rula. Încearcă din nou.' }),
131 )
132}
133