@xmarts/genius-setup 1.10.0 → 1.11.0

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.
@@ -225,6 +225,32 @@ function runSetup(token) {
225
225
  log('WARN: could not write CLAUDE.md protocol (' + e.message + ')');
226
226
  }
227
227
 
228
+ // 4b) ~/.claude/skills — install the bundled Genius skills so `token+npm` REALLY
229
+ // delivers skills too (previously only hooks+MCP shipped, though the footer
230
+ // already claimed skills arrived — this closes that gap). plugin/skills is the
231
+ // git source-of-truth mirror; copy each skill dir into ~/.claude/skills/<name>/,
232
+ // refreshing ours without clobbering the consultant's own skills. Fails open.
233
+ let skillsInstalled = [];
234
+ try {
235
+ const SKILLS_SRC = path.join(__dirname, '..', 'plugin', 'skills');
236
+ const SKILLS_DST = path.join(CLAUDE_DIR, 'skills');
237
+ if (fs.existsSync(SKILLS_SRC)) {
238
+ ensureDir(SKILLS_DST);
239
+ for (const name of fs.readdirSync(SKILLS_SRC)) {
240
+ const src = path.join(SKILLS_SRC, name);
241
+ try {
242
+ if (!fs.statSync(src).isDirectory()) continue;
243
+ fs.cpSync(src, path.join(SKILLS_DST, name), { recursive: true, force: true });
244
+ skillsInstalled.push(name);
245
+ } catch (e) {
246
+ log('WARN: could not install skill ' + name + ' (' + e.message + ')');
247
+ }
248
+ }
249
+ }
250
+ } catch (e) {
251
+ log('WARN: could not install skills (' + e.message + ')');
252
+ }
253
+
228
254
  // 5) Fan out the consultant's OTHER MCP servers (asana, clickup, google, ...).
229
255
  // Their non-secret shape (manifest) + per-device secret VALUES come from the
230
256
  // Bearer-authed /xma/genius/v1/mcp/secrets endpoint. We write Claude Code
@@ -325,6 +351,7 @@ function runSetup(token) {
325
351
  log(' - Presence : ' + presenceDst + ' (SessionStart, detached — honest time-tracking amarre)');
326
352
  log(' - Config : ' + path.join(GENIUS_DIR, 'config.json'));
327
353
  log(' - Protocol : ' + path.join(CLAUDE_DIR, 'CLAUDE.md') + ' (auto-managed)');
354
+ if (skillsInstalled.length) log(' - Skills : ' + path.join(CLAUDE_DIR, 'skills') + ' (' + skillsInstalled.join(', ') + ')');
328
355
  if (CLIENT) log(' - Default client : ' + CLIENT);
329
356
  log('');
330
357
  log(' Restart Claude Code. Per-turn Brain injection + automatic capture + the Genius');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmarts/genius-setup",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "One-command, self-updating onboarding for Xmarts Genius (Brain MCP + per-turn injection + automatic session/time capture + presence heartbeat for honest time-tracking 'amarre' + plan/committee→Brain enforcement + CLAUDE.md protocol + MCP fan-out: provisions the consultant's granted MCP servers into Claude Code & Claude Desktop). Secret-free onboarding via single-use setup-token exchange. Installs once; self-updates daily so new tools/skills/features arrive with zero action.",
5
5
  "bin": {
6
6
  "genius-setup": "bin/genius-setup.js"
@@ -12,7 +12,8 @@
12
12
  "hooks/plan_committee_guard.py",
13
13
  "hooks/genius_presence.py",
14
14
  "CLAUDE_PROTOCOL.md",
15
- "README.md"
15
+ "README.md",
16
+ "plugin/skills"
16
17
  ],
17
18
  "engines": {
18
19
  "node": ">=16"
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: genius-operator
3
+ description: >
4
+ Operate the Xmarts Genius platform from any session: read, write, deploy webapps,
5
+ deploy/manage modules, snapshot, restore and restart Odoo — on the BeeSmart central
6
+ OR on ANY registered client Odoo (GEA, Elevatrix, Deploy-O, etc.) through the
7
+ `beesmart_genius` MCP. The key unlock most sessions miss: every Genius tool accepts a
8
+ `target_client` parameter that routes the SAME call to a client's Odoo over the secure
9
+ HMAC gateway. Use this skill whenever a session says it "can't" reach a client, can't
10
+ deploy a webapp to a client, can't see/manage a client's modules, or whenever the work
11
+ touches xma_genius, xma_connect, beesmart_genius, the Genius Brain, GEA Ecuador, or
12
+ deploying/controlling anything on a client instance from the central — even if the user
13
+ doesn't name the tool. If you ever think "I don't have access to that client", read this
14
+ skill first: you almost certainly do.
15
+ ---
16
+
17
+ # Genius Operator
18
+
19
+ You are operating the **Xmarts Genius** platform. Genius is a central command server
20
+ (module `xma_genius`, living on `beesmart.digital`) that controls a fleet of client Odoo
21
+ instances. Each client runs a thin agent (`xma_connect`) that exposes a signed HMAC gateway.
22
+ You reach all of it through one MCP server: **`beesmart_genius`**.
23
+
24
+ This skill exists to kill one specific failure: sessions saying *"no puedo hacer eso / I
25
+ don't have access to that client"* when they actually can. They can. They just didn't set
26
+ one parameter.
27
+
28
+ ## The one thing to internalize: `target_client`
29
+
30
+ Almost every `mcp__beesmart_genius__*` tool takes an optional **`target_client`** argument.
31
+
32
+ - **Omit it** → the call runs on the BeeSmart central (`odoo_clean` DB).
33
+ - **Set it** to a client's name or UUID → the EXACT same call is signed (HMAC-SHA256) and
34
+ routed through that client's `xma_connect` gateway, executing on *their* Odoo and returning
35
+ the result to you.
36
+
37
+ ```
38
+ # Reads the central Brain on beesmart:
39
+ execute_orm(code="result = env['xma.learning'].search_count([])")
40
+
41
+ # Reads GEA Ecuador's live Odoo — same tool, one extra arg:
42
+ execute_orm(code="result = env['sale.order'].search_count([])", target_client="GEA Ecuador")
43
+ ```
44
+
45
+ That is the whole trick. A session is **not** sandboxed to beesmart. With `target_client`
46
+ it can read data, write records, deploy webapps, install/upgrade modules, snapshot, restore,
47
+ and restart Odoo on any *registered, connected* client. When you catch yourself about to say
48
+ "I can't do that on the client," stop and set `target_client` instead.
49
+
50
+ `target_client` accepts either the human name (`"GEA Ecuador"`) or the UUID
51
+ (`"74292458-44e8-406c-a476-4e89f86920c8"`). The name is matched on the central's `xma.client`
52
+ registry, so it must match an existing record.
53
+
54
+ ## "Can I actually do X?" — yes, here's the map
55
+
56
+ When in doubt, this table answers it. The full per-tool reference with quirks is in
57
+ [references/tool-catalog.md](references/tool-catalog.md) — read it when you need the exact
58
+ argument shape or hit an edge case.
59
+
60
+ | You want to… | Tool | Routable to a client? |
61
+ |---|---|---|
62
+ | Run arbitrary ORM/Python (read or write) | `execute_orm` | ✅ `target_client` |
63
+ | Search / read records | `search_read`, `read_record`, `read_group` | ✅ |
64
+ | Create / update / delete a record | `create_record`, `update_record`, `delete_record` | ✅ |
65
+ | Call a model method | `execute_method` | ✅ |
66
+ | Deploy / edit a **webapp** | `manage_webapp` | ✅ **only if the client has the webapp infra installed — probe first** (see below + [deploy-webapp-to-client.md](references/deploy-webapp-to-client.md)) |
67
+ | Deploy / install / upgrade a **module** | `manage_module`, `deploy_module` | ✅ — see [manage-client-modules.md](references/manage-client-modules.md) |
68
+ | Inspect a model's schema | `get_model_schema`, `list_models` | ✅ |
69
+ | Read / search a client's source code | `code_read`, `code_search`, `search_webapp_code` | ✅ |
70
+ | Snapshot / restore / export the DB | `create_snapshot`, `restore_snapshot`, `export_database` | ✅ |
71
+ | Restart Odoo | `trigger_restart` | ✅ |
72
+ | Build a chart/dashboard | `create_echart` | ✅ *(same infra caveat as webapps)* |
73
+ | Query the Genius **Brain** | `query_knowledge` | ❌ central only (the Brain lives on beesmart) |
74
+ | Capture a learning to the Brain | `capture_learning` | ❌ central only |
75
+ | Read your own consultant profile | `get_my_profile` | ❌ central only |
76
+
77
+ 33 tools are routable to clients in total. If a tool you need isn't listed, check
78
+ [references/tool-catalog.md](references/tool-catalog.md) before concluding it's impossible.
79
+
80
+ ## Always orient before you act
81
+
82
+ The Brain (`query_knowledge`) is the server-side source of truth and it remembers things this
83
+ session doesn't. It has saved you from real footguns (downgrade bricking, version desync,
84
+ auth gotchas). So:
85
+
86
+ 1. **`query_knowledge(<your task topic>)` before building or debugging.** Two minutes here
87
+ beats an hour of rediscovering a known trap.
88
+ 2. Check the client is actually reachable before routing to it. A quick
89
+ `execute_orm(code="result = True", target_client="<name>")` round-trips the gateway. If it
90
+ errors with `auth_invalid` / `Not configured`, the client has a master↔client api_key
91
+ desync — that's a known issue, query the Brain for "Not configured gateway api_key desync"
92
+ rather than guessing.
93
+ 3. After a notable build or fix, **`capture_learning(...)`** so the next session inherits it.
94
+ Required shape: `name` + `category` (one of `breaking_change | pattern | antipattern |
95
+ workaround | best_practice`) + `resolution`; `tags` is a comma-separated **string**.
96
+
97
+ ## Hard rules (these are non-negotiable and protect production)
98
+
99
+ These come straight from the Xmarts operating doctrine — follow them even when not asked:
100
+
101
+ - **Snapshot before any write to a client's production data.** Use `create_snapshot`
102
+ (`target_client=...`) first. The orchestrated `deploy_module` auto-snapshots; manual
103
+ `update_record`/`execute_orm` writes do not — you take the snapshot. No snapshot → don't run
104
+ the change.
105
+ - **Never destroy productive data.** Archive with `active=False`, never `unlink()`/delete.
106
+ It's reversible and keeps the audit trail.
107
+ - **Everything is Odoo 19.** Generate only v19-compatible code (e.g. `<list>` not `<tree>`,
108
+ `models.Constraint` not `_sql_constraints`, `group_ids` not `groups_id`). When unsure,
109
+ `code_search` the client's own source to confirm the API before you write it.
110
+ - **Webapps are mobile-first and theme-driven.** No hardcoded hex colors — colors flow from
111
+ the theme. Don't hand-roll a webapp from scratch here; use the dedicated skills (below) to
112
+ build it well, then this skill to *deploy* it to the right place.
113
+ - **Credentials never inline.** No api_keys/passwords in tool arguments or committed code.
114
+
115
+ ## Webapps: build with the specialists, deploy with this skill
116
+
117
+ This skill's job for webapps is the **routing and deployment** layer — getting the right app
118
+ onto the right Odoo (central vs a specific client) and verifying it serves. For the actual
119
+ *construction* quality (design, theming, mobile-first, audit), hand off to the specialists:
120
+
121
+ - `webapp-forge` — prompt → mockup → theme → React → deploy pipeline
122
+ - `webapp-theming-system` — OKLCH themes, dark mode, WCAG contrast
123
+ - `webapp-refine` — iterate on an existing webapp
124
+ - `webapp-audit` — 6-point quality gate before you call it done
125
+ - `xmarts-kanban-standard` — the mandatory kanban card look
126
+
127
+ **Prerequisite — probe the client's capability first, do NOT trust the version number.**
128
+ Webapp deploy to a client only works if that client's `xma_connect` actually ships *and loads*
129
+ the webapp infra (the `mcp.webapp*` models + the `/mcp/webapp` controller + the `manage_webapp`
130
+ gateway handler). The reported `xma_connect_version` is **not** a reliable gate — a stale-
131
+ constant bug and divergent deployed bundles mean a client labelled "5.0.0" may lack the infra
132
+ even though git added it at 4.0.0. So before deploying, route this probe:
133
+
134
+ ```
135
+ execute_orm(code="result = bool(env['ir.model'].search([('model','=','mcp.webapp')], limit=1))",
136
+ target_client="<client>")
137
+ ```
138
+
139
+ `false` → the client can't host a webapp yet; it needs an `xma_connect` upgrade that ships and
140
+ loads the webapp infra first (and on some clients that upgrade is itself gated). Probe every
141
+ time, because **state changes**: GEA Ecuador returned `false` at `xma_connect` 5.0.0, then was
142
+ upgraded to 8.0.0 (Deploy-O parity) and now returns `true`. Never hard-code "client X can/can't"
143
+ from memory — the version field lies and clients get upgraded; the live probe is the truth.
144
+
145
+ The deploy mechanics, the data-flow gotcha that bites everyone (page `data_code` vs webapp
146
+ `data_code`), the slug/URL model, and the client-vs-central decision are all in
147
+ [references/deploy-webapp-to-client.md](references/deploy-webapp-to-client.md). Read it before
148
+ your first deploy to a client.
149
+
150
+ ## Modules: controlling what Xmarts builds for a client (the GEA case)
151
+
152
+ When a client like GEA accumulates many Xmarts-built modules, this is how you keep control of
153
+ them from the central — list installed modules, read their source, deploy new ones, upgrade,
154
+ snapshot before each change, and roll back if needed. The full workflow, including the
155
+ orchestrated `deploy_module` (pre-checks + auto-snapshot + post-checks) and when a
156
+ Python-bearing module needs the SSH path instead of `manage_module`, is in
157
+ [references/manage-client-modules.md](references/manage-client-modules.md).
158
+
159
+ The short version: `module_list_installed` / `manage_module` (info) to see what's there,
160
+ `code_read` + `code_search` to inspect it, `deploy_module` to ship changes safely. Always
161
+ validate a non-trivial module on a clean Odoo 19 env first — green install ≠ correct.
162
+
163
+ ## When a session is stuck saying "I can't"
164
+
165
+ Diagnose in this order — it's almost always #1 or #2:
166
+
167
+ 1. **Did it set `target_client`?** Most "I can't reach the client" cases are just a missing
168
+ parameter. Add it.
169
+ 2. **Is the MCP authenticated?** The `beesmart_genius` tools must be loaded. If they aren't
170
+ visible, the session needs the MCP connected (and OAuth doesn't always persist between
171
+ sessions). Reconnect, don't give up.
172
+ 3. **Is the client connected?** Round-trip a trivial `execute_orm` to it. If `Not configured`,
173
+ it's the known api_key desync — query the Brain, don't reinvent.
174
+ 4. **Is the tool genuinely central-only?** Only Brain tools (`query_knowledge`,
175
+ `capture_learning`, `get_my_profile`) are. Everything operational is routable.
176
+
177
+ Only after all four genuinely fail is "I can't" the honest answer — and then say *why*,
178
+ specifically, not just "no puedo".
@@ -0,0 +1,183 @@
1
+ # Deploying a webapp to a client (or central) via Genius
2
+
3
+ This is the routing/deployment layer. Build the webapp's quality (design, theme, mobile-first,
4
+ audit) with the specialist skills — `webapp-forge`, `webapp-theming-system`, `webapp-refine`,
5
+ `webapp-audit`, `xmarts-kanban-standard`. This file is about getting the right app onto the
6
+ right Odoo and confirming it serves.
7
+
8
+ ## Table of contents
9
+ - [Prerequisite: the client must HAVE the webapp infra (probe it)](#prerequisite-probe-the-client)
10
+ - [Central vs client: decide first](#central-vs-client-decide-first)
11
+ - [The data model](#the-data-model)
12
+ - [The gotcha that bites everyone: data_code lives on the PAGE](#the-gotcha-data_code-lives-on-the-page)
13
+ - [Deploy recipe](#deploy-recipe)
14
+ - [Verify it serves](#verify-it-serves)
15
+ - [Editing an existing webapp](#editing-an-existing-webapp)
16
+ - [Quality gates before you call it done](#quality-gates)
17
+
18
+ ## Prerequisite: probe the client
19
+
20
+ A client can only host a webapp if its `xma_connect` actually ships **and loads** the webapp
21
+ infra: the models `mcp.webapp` / `mcp.webapp.page` / `mcp.webapp.page.file` /
22
+ `mcp.webapp.endpoint` / `mcp.webapp.theme`, the `/mcp/webapp/...` controller, and the
23
+ `manage_webapp` gateway handler. The webapp infra was added to `xma_connect` at v19.0.4.0.0,
24
+ but **do not gate on the version number** — it lies. A stale-constant version bug plus divergent
25
+ deployed bundles (an older "5.0.0" served as newest) mean a client can report a version that
26
+ *should* have the infra and still not have it. Probe the ground truth instead:
27
+
28
+ ```
29
+ execute_orm(code="result = bool(env['ir.model'].search([('model','=','mcp.webapp')], limit=1))",
30
+ target_client="<client>")
31
+ ```
32
+
33
+ - `true` → infra present, proceed.
34
+ - `false` → the client cannot host a webapp yet. It needs an `xma_connect` upgrade that ships
35
+ and loads the webapp models first. Don't attempt `manage_webapp` against it — it will fail
36
+ on a missing model. Say so explicitly and, if asked, scope the upgrade (which on some clients
37
+ is itself a gated rollout).
38
+
39
+ **Why this is probe-not-version:** GEA Ecuador once returned `false` at `xma_connect` 5.0.0
40
+ (webapp models absent), and was later upgraded to 8.0.0 — it now returns `true` (full infra,
41
+ Deploy-O parity). The version field is unreliable AND clients get upgraded out from under you,
42
+ so the only safe gate is the live probe. If a client's probe is `false`, it needs an
43
+ `xma_connect` upgrade that ships + loads the webapp infra (on big/gated clients that upgrade
44
+ has its own rollout window); until then a "client webapp" can only live on the central reading
45
+ the client's data over the gateway (slower, fragile).
46
+
47
+ ## Central vs client: decide first
48
+
49
+ `manage_webapp` writes `mcp.webapp` records wherever the call lands:
50
+
51
+ - **No `target_client`** → webapp lives on BeeSmart central, served at
52
+ `https://beesmart.digital/mcp/webapp/<slug>`. Use for Genius's own admin/consultant tools.
53
+ - **`target_client="<client>"`** (one whose probe returned `true`) → webapp lives on that
54
+ client's Odoo, served at `https://<client-domain>/mcp/webapp/<slug>`. Use for anything the
55
+ client's own users see, or any app that needs the client's live data.
56
+
57
+ Rule of thumb: if the app reads/writes the client's business data (their sales, their
58
+ inventory), deploy it ON the client with `target_client`. Routing a central webapp to read
59
+ client data over the gateway per request is slower and fragile — put the app where the data is.
60
+
61
+ The client must have `xma_connect` installed and connected **with the webapp infra present**
62
+ for client deploys (see the prerequisite probe above). Don't assume a given client's state from
63
+ memory — it changes (GEA went from no-infra to full infra after an upgrade). Probe, then decide.
64
+
65
+ ## Visibility & RBAC (xma_connect ≥ 19.0.9.0.0) — PROBE → CHOOSE → DEPLOY → VERIFY
66
+
67
+ Newer `xma_connect` gates every webapp natively by a `visibility` field
68
+ (`public` / `internal` / `groups` / `users`, default **internal**), enforced by `ir.rule` +
69
+ route auth — no self-gating inside `data_code`. The contract for every deploy:
70
+
71
+ 1. **PROBE** the client supports it: `search_read(model="ir.model", domain=[["model","=","mcp.webapp"]], ...)` exists, and check `mcp.webapp._fields` has `visibility` (xma_connect ≥ 9.0.0). Older clients have no visibility model → upgrade first.
72
+ 2. **CHOOSE** visibility explicitly on EVERY `manage_webapp` create — never omit (omitting defaults to `internal`, which is safe but be deliberate):
73
+ - `internal` (default): any logged-in internal user.
74
+ - `groups`: pass `shared_group_ids` (native `res.groups`); add `base.group_portal` for portal users.
75
+ - `users`: pass `shared_user_ids`.
76
+ - `public`: anyone, no login — requires BOTH `visibility:"public"` **and** `public_confirm:true` in the SAME call, plus `public_model_allowlist` for any model the data reads. The tool rejects public without confirm.
77
+ - **Do NOT** write `env.user.has_group(...)`/`res.users` self-gates in `data_code` — the native rule gates; a self-gate just double-gates and 403s anonymous.
78
+ 3. **DEPLOY** with the explicit visibility; the tool result echoes `visibility` + `access_summary` ("who can see this") — read it back.
79
+ 4. **VERIFY** by fetching `/mcp/webapp/<slug>/page/<id>/data` (or the app URL) **as anonymous AND as an internal user**: an `internal` app must 401/redirect anonymous (no traceback); a `public` app must render for anonymous with its blast radius == the allowlist.
80
+
81
+ **Public forms / notice boards:** public `data_code` runs in a locked sandbox (no `.sudo()`, only allowlisted models). To accept submissions, set the endpoint `allow_public:true` + `submit_fields` and call `submit({...})` in `handler_code` — it writes one `mcp.webapp.submission` (staff-only) and returns an opaque ref. Anonymous calls are rate-limited per IP/min.
82
+
83
+ ## The data model
84
+
85
+ ```
86
+ mcp.webapp # the app: name, slug, router_mode, theme, PWA, shared styles/components
87
+ └── mcp.webapp.page # a route: route_path, component_code (React), data_code (Python)
88
+ └── mcp.webapp.page.file # extra JS/JSX files injected before the page component
89
+ mcp.webapp.endpoint # api.get/post/... handlers (handler_code, safe_eval Python)
90
+ mcp.webapp.theme # theme tokens (OKLCH); colors flow from here — never hardcode hex
91
+ ```
92
+
93
+ `component_code` is React (props: `{data, routeParams, globalState, setGlobalState,
94
+ initialData, api, storage}`). `data_code` / `handler_code` are safe_eval Python with the SAME
95
+ rules and pre-imports as `execute_orm` — assign to `result`, no `import`, use
96
+ `env['mcp.webapp'].browse(mcp_webapp_id)`, never hardcode IDs or slugs.
97
+
98
+ ## The gotcha: `data_code` lives on the PAGE
99
+
100
+ The single most common reason a deployed page shows no data: the component reads its data from
101
+ the **page's** `data_code`, not the webapp's. The webapp-level `data_code` only feeds
102
+ `initialData` on app load. If your page needs records, put the query in that **page's**
103
+ `data_code` and read it from the `data` prop.
104
+
105
+ When you edit data logic, update the PAGE's `data_code` (and if you also keep a copy at webapp
106
+ level, update both — they drift otherwise). This has burned multiple sessions.
107
+
108
+ ## Deploy recipe
109
+
110
+ 1. **Pick the target.** Central or `target_client`? (see above)
111
+ 2. **If targeting a client, run the capability probe** (`mcp.webapp` exists?). `false` → stop,
112
+ the client needs an agent upgrade first.
113
+ 4. **`query_knowledge("webapp deploy <client> <topic>")`** — pick up known gotchas first.
114
+ 5. **Snapshot if it's a client with anything to lose.** `create_snapshot(target_client=...)`.
115
+ Creating a brand-new webapp is additive and low-risk; still cheap insurance on production.
116
+ 6. **Create the webapp shell**, then pages/endpoints. You can do it in one `manage_webapp`
117
+ call combining `create_webapp` + `create_pages` + `create_endpoints`:
118
+
119
+ ```
120
+ manage_webapp(
121
+ target_client="<client-with-webapp-infra>",
122
+ create_webapp={
123
+ "name": "GEA Ops Dashboard",
124
+ "slug": "gea-ops",
125
+ "router_mode": "hash", # hash works everywhere; "browser" needs a slug + clean URLs
126
+ "pwa_enabled": True, # mobile-first PWA is the Xmarts standard
127
+ "tailwind_enabled": True
128
+ },
129
+ create_pages=[{
130
+ "name": "Home",
131
+ "route_path": "/",
132
+ "component_code": "function Home({data}){ return <div className='p-4'>...</div> }",
133
+ "data_code": "result = {'orders': env['sale.order'].search_count([])}" # PAGE data_code!
134
+ }]
135
+ )
136
+ ```
137
+
138
+ 7. **Theme it** — don't hardcode colors. Hand off to `webapp-theming-system` or set the
139
+ `mcp.webapp.theme`. Colors must flow from theme tokens so dark/light both work.
140
+ 8. **Verify** (next section).
141
+ 9. **`capture_learning(...)`** if you hit and solved anything non-obvious.
142
+
143
+ For multi-step builds, create the shell first, then add pages/files in follow-up
144
+ `manage_webapp` calls (each can derive the webapp from a page/file id, so you don't have to
145
+ repeat `update_webapp`).
146
+
147
+ ## Verify it serves
148
+
149
+ Don't declare done from the tool's success message alone — confirm the route actually renders:
150
+
151
+ ```
152
+ # Confirm the record exists on the right instance:
153
+ search_read(model="mcp.webapp", domain=[["slug","=","my-app"]],
154
+ fields=["id","name","slug"], target_client="<client>", limit=5)
155
+ ```
156
+ Then load `https://<client-domain>/mcp/webapp/my-app` (or have the user open it). A 200
157
+ with the rendered page = deployed. If the page is blank, re-check the **page** `data_code` and
158
+ the browser console for a component error.
159
+
160
+ ## Editing an existing webapp
161
+
162
+ Read before you write — use `search_read`/`read_record` (with `target_client`) to get current
163
+ code, then patch surgically:
164
+
165
+ ```
166
+ # Discover pages:
167
+ search_read(model="mcp.webapp.page", domain=[["webapp_id","=",ID]],
168
+ fields=["id","name","route_path"], target_client="<client>", limit=50)
169
+ # Read full code:
170
+ read_record(model="mcp.webapp.page", id=PAGE_ID,
171
+ fields=["component_code","data_code"], target_client="<client>")
172
+ # Surgical edit (preferred over full rewrite):
173
+ manage_webapp(target_client="<client>",
174
+ update_pages=[{"page_id": PAGE_ID,
175
+ "component_code_patches": [{"find": "old snippet", "replace": "new snippet"}]}])
176
+ ```
177
+ Use `search_webapp_code` to locate exact strings before patching.
178
+
179
+ ## Quality gates
180
+
181
+ Before calling a webapp done, run the `webapp-audit` skill (PWA, WCAG contrast, no hardcoded
182
+ hex, CSS/JS budget, accessibility, dark mode). The Xmarts non-negotiables: mobile-first PWA,
183
+ 44px touch targets, theme-driven colors, dark mode that actually flips, skeleton loading.
@@ -0,0 +1,106 @@
1
+ # Controlling a client's Xmarts-built modules (the GEA case)
2
+
3
+ GEA Ecuador will accumulate many modules built by Xmarts. This is how you keep central control
4
+ of them: see what's installed, read the source, ship changes safely, upgrade, and roll back —
5
+ all from the `beesmart_genius` MCP with `target_client="GEA Ecuador"`.
6
+
7
+ GEA facts to keep in mind: Odoo 19 Enterprise, ~474 modules installed, DB ≈ 14.8 GB (large —
8
+ exports/restores need a dedicated window), hosted on `geaecuador.xmarts.net`. Its `xma_connect`
9
+ was upgraded to 8.0.0 (Deploy-O parity) and now has the full webapp/dashboard infra — but
10
+ always probe `mcp.webapp` rather than trusting that statement, since state changes.
11
+ Files in `xma_connect`/custom addons on `xmarts.net` infra are often owned by root, so Odoo's
12
+ uid can't write — a true filesystem deploy may need an SSH `chown`. The gateway path below
13
+ avoids that for ZIP-importable modules.
14
+
15
+ ## Table of contents
16
+ - [See what Xmarts has shipped](#1-see-whats-installed)
17
+ - [Inspect a module's source](#2-inspect-the-source)
18
+ - [Ship or upgrade a module](#3-ship-or-upgrade-a-module-safely)
19
+ - [manage_module vs deploy_module vs SSH](#which-path-data-vs-python)
20
+ - [Snapshot & rollback](#4-snapshot--rollback)
21
+ - [Restart when needed](#5-restart-when-the-registry-is-stale)
22
+
23
+ ## 1. See what's installed
24
+
25
+ ```
26
+ module_list_installed(target_client="GEA Ecuador")
27
+ ```
28
+ Or filter to the Xmarts-built ones:
29
+ ```
30
+ search_read(model="ir.module.module",
31
+ domain=[["state","=","installed"],["name","=like","xma%"]],
32
+ fields=["name","installed_version","summary","state"],
33
+ target_client="GEA Ecuador", order="name", limit=200)
34
+ ```
35
+ This is your inventory of "what Xmarts built and shipped to GEA" and the live version of each.
36
+ (Remember the central's `xma.client.xma_connect_version` field is unreliable — read the real
37
+ versions from GEA like this instead.)
38
+
39
+ ## 2. Inspect the source
40
+
41
+ ```
42
+ # Find where something is defined across GEA's addons:
43
+ code_search(pattern="class .*Model|def action_", file_pattern="**/*.py",
44
+ output_mode="files_with_matches", limit=50, target_client="GEA Ecuador",
45
+ module="xma_<module>")
46
+ # Read a specific file (path from the addon root):
47
+ code_read(path="xma_<module>/models/<file>.py", target_client="GEA Ecuador")
48
+ ```
49
+ For webapp/echart code shipped to GEA, use `search_webapp_code`.
50
+
51
+ ## 3. Ship or upgrade a module safely
52
+
53
+ Prefer the orchestrated path — it snapshots automatically and runs pre/post checks:
54
+
55
+ ```
56
+ deploy_module(target_client="GEA Ecuador", arguments={...module + options...})
57
+ ```
58
+
59
+ `deploy_module` = pre-checks → auto-snapshot → install/upgrade → post-checks. This is the
60
+ right default for production because the snapshot and verification are built in.
61
+
62
+ For authoring/iterating a data/XML/CSV/webapp module, `manage_module` gives finer control:
63
+ - `create_module` + `create_files` (include `__manifest__.py` as a normal file).
64
+ - `skip_install=true` while adding files across several calls; `false` on the last call to
65
+ install.
66
+ - If an install fails, **files are saved but the install rolls back** — fix the broken file
67
+ with `update_files` and call again; you don't resubmit everything.
68
+ - `force=true` re-inits `noupdate=1` records. `uninstall_first=true` **drops all module data**
69
+ — destructive, ask the user before using it.
70
+ - Manifest is validated pre-install: every file in `data` must exist.
71
+
72
+ ### Which path: data vs Python
73
+
74
+ | Module contents | Path | Why |
75
+ |---|---|---|
76
+ | XML / CSV / views / webapp / data only | `manage_module` or `deploy_module` (ZIP via `base_import_module`) | Installs cleanly over the gateway, no SSH. |
77
+ | Python that must execute at load (models, compute, controllers) | Validate on a **clean Odoo 19 EE env first**, then deploy. `base_import_module` does not reliably run arbitrary Python — a real filesystem deploy may be needed (SSH `chown` on `xmarts.net`). | Historically `manage_module` is data-only for code execution (Brain: "manage_module data-only confirmed"). Don't assume a Python module "installed" just because the call returned success — verify the model/fields actually exist. |
78
+
79
+ Always: a green install ≠ a correct module. After install, verify behavior — e.g.
80
+ `list_models` / `get_model_schema` for new models, or `execute_orm` exercising the new logic,
81
+ routed to GEA. For anything non-trivial, validate on the disposable clean env before GEA.
82
+
83
+ When generating module code, it's **Odoo 19** — `<list>` not `<tree>`, `models.Constraint` not
84
+ `_sql_constraints`, `group_ids` not `groups_id`, `hr.version` not `hr.contract`, fiscal tax
85
+ mappings on taxes not fiscal positions. `code_search` GEA's own source to confirm an API before
86
+ relying on it.
87
+
88
+ ## 4. Snapshot & rollback
89
+
90
+ ```
91
+ create_snapshot(target_client="GEA Ecuador") # before any change deploy_module didn't auto-snapshot
92
+ list_snapshots(target_client="GEA Ecuador") # confirm it exists
93
+ restore_snapshot(target_client="GEA Ecuador", ...) # the rollback — confirm with the user first
94
+ ```
95
+ Register every snapshot per the snapshot protocol (name, UUID, type, size, trigger, storage).
96
+ On a 14.8 GB DB, snapshot/restore is heavy — plan the window, don't fire it mid-business-hours
97
+ without telling the user.
98
+
99
+ ## 5. Restart when the registry is stale
100
+
101
+ Some upgrades change the in-memory registry; the running server keeps the old one until
102
+ restarted:
103
+ ```
104
+ trigger_restart(target_client="GEA Ecuador")
105
+ ```
106
+ Always restart after a `--stop-after-init` style upgrade so the new code is actually live.
@@ -0,0 +1,129 @@
1
+ # Genius Tool Catalog (`beesmart_genius` MCP)
2
+
3
+ The authoritative list of what you can do through Genius, what routes to a client, and the
4
+ quirks that trip people up. All tools are namespaced `mcp__beesmart_genius__<tool>`.
5
+
6
+ ## Table of contents
7
+ - [Routing model](#routing-model)
8
+ - [Routable operational tools (run on central OR a client)](#routable-operational-tools)
9
+ - [Central-only tools (Brain & self)](#central-only-tools)
10
+ - [Cross-cutting quirks](#cross-cutting-quirks)
11
+
12
+ ## Routing model
13
+
14
+ Every routable tool takes `target_client` (string: client name or UUID).
15
+ - Omitted → executes on BeeSmart central (`odoo_clean`).
16
+ - Set → signed HMAC-SHA256 request through the client's `xma_connect` gateway
17
+ (`/xma/gateway/v1`), executed under the client's non-admin **service user** (not superuser),
18
+ inside a savepoint that rolls back on error.
19
+
20
+ The client side dispatches through `ToolDispatcher` (33 registered handlers). If a tool isn't
21
+ in that set, it can't be routed — but every operational tool you'd want is.
22
+
23
+ Reachability self-test (cheap, safe, read-only):
24
+ ```
25
+ execute_orm(code="result = {'ok': True, 'db': env.cr.dbname}", target_client="GEA Ecuador")
26
+ ```
27
+ A clean `{success: true, ...}` means the gateway, HMAC auth and service user are all healthy.
28
+ `auth_invalid` / `Not configured` = master↔client api_key desync (known issue — query the
29
+ Brain: "Deploy-O gateway Not configured api_key desync", learning #1065).
30
+
31
+ ## Routable operational tools
32
+
33
+ ### Data read
34
+ | Tool | Use | Quirks |
35
+ |---|---|---|
36
+ | `search_read` | Search + read fields | Paginated — compare `count` vs `total`, use `offset`. Binary/image fields auto-base64'd as separate content. |
37
+ | `read_record` | One record by ID | Cheaper than `search_read` if you have the ID. |
38
+ | `read_group` | GROUP BY aggregation | v19: this is the supported aggregator (raw `read_group` ORM method is deprecated in favor of `_read_group`/`formatted_read_group`, but the MCP tool wraps it correctly). |
39
+ | `validate_domain` | Sanity-check a domain before a big query | Use on untrusted/long domains. |
40
+
41
+ ### Data write
42
+ | Tool | Use | Quirks |
43
+ |---|---|---|
44
+ | `create_record` | Create one record | Simple creates only. |
45
+ | `update_record` | Write fields | **Snapshot first if production.** Archive via `active=False`, never delete. |
46
+ | `delete_record` | Delete | Avoid on productive data — prefer `active=False`. Ask the user before destructive deletes. |
47
+ | `execute_method` | Call any model method | For `action_confirm`, recompute, custom methods. Powerful — same write-safety rules apply. |
48
+ | `execute_orm` | Arbitrary safe_eval Python | The workhorse. NO `import`/`from` — utilities are pre-imported (`env`, `Command`, `datetime`, `requests`, `re`, `json`, `defaultdict`, `Counter`, etc.). Assign to `result`. Output cap ~100KB → paginate. `hasattr`/`getattr`/`__class__`/`dir` are blocked by safe_eval. |
49
+ | `bulk_execute` | Batch several operations | For multi-op efficiency. |
50
+
51
+ ### Schema & code introspection
52
+ | Tool | Use |
53
+ |---|---|
54
+ | `get_model_schema` / `get_model_schema_progressive` | Field definitions. Filter by type / required to shrink output. |
55
+ | `list_models` | Enumerate models on the instance. |
56
+ | `code_read` | Read a source file by path (from addon root). |
57
+ | `code_search` | Regex search across addon source. `files_with_matches` to discover, then `code_read`. Paginate via `offset` (compare `returned` vs `total_*`). |
58
+ | `search_webapp_code` | Search inside `mcp.webapp` component/data code. |
59
+ | `get_system_info` | Odoo version, db, module counts, etc. |
60
+
61
+ ### Webapps → see [deploy-webapp-to-client.md](deploy-webapp-to-client.md)
62
+ **Client-routed webapp/echart/design tools require the client's `xma_connect` to actually have
63
+ the `mcp.webapp*` / `mcp.echart` / `mcp.design.asset` infra installed.** This is NOT guaranteed
64
+ by the version number (see the version-truth quirk below) — probe first:
65
+ `execute_orm(code="result = bool(env['ir.model'].search([('model','=','mcp.webapp')], limit=1))", target_client="<client>")`.
66
+ A `false` means these three tools fail there until the agent is upgraded. State changes — GEA
67
+ returned `false` at 5.0.0 and `true` after its 8.0.0 upgrade — so probe each time, don't recall.
68
+
69
+ | Tool | Use |
70
+ |---|---|
71
+ | `manage_webapp` | Create/update webapps, pages, endpoints, page-files, assets. Surgical `*_patches` edits. Needs `mcp.webapp` infra on the client. |
72
+ | `create_echart` | Persistent ECharts 6.x dashboards. Needs `mcp.echart` on the client. |
73
+ | `manage_design_asset` | Manage `mcp.design.asset` (icons/SVGs). Needs `mcp.design.asset` on the client. |
74
+
75
+ ### Modules → see [manage-client-modules.md](manage-client-modules.md)
76
+ | Tool | Use | Quirks |
77
+ |---|---|---|
78
+ | `module_list_installed` | List installed modules on the instance | Start here to see what Xmarts has shipped to a client. |
79
+ | `manage_module` | Create/install/upgrade/uninstall an importable module (ZIP via `base_import_module`) | Best for data/XML/CSV/webapp modules. `skip_install=true` for multi-step builds. `uninstall_first=true` is **destructive** — ask the user first. |
80
+ | `deploy_module` | Orchestrated deploy: pre-checks → **auto-snapshot** → install → post-checks | Preferred path for shipping a module to a client safely. |
81
+ | `module_manage_safe` | Guarded module ops | Safety wrapper. |
82
+ | `module_job_status` | Poll an async module job | For long installs. |
83
+
84
+ ### DB lifecycle & ops
85
+ | Tool | Use | Quirks |
86
+ |---|---|---|
87
+ | `create_snapshot` | Snapshot the DB (and register it) | **Do this before any production write.** Register per the snapshot protocol (name, UUID, type, size, trigger). |
88
+ | `list_snapshots` | List snapshots | Verify a snapshot exists before a risky change. |
89
+ | `restore_snapshot` | Restore from a snapshot | The rollback. Confirm with the user. |
90
+ | `export_database` / `export_data` | Pull a full DB dump / dataset out | GEA is ~14.8GB — large-dump exports need a dedicated window. |
91
+ | `upload_attachment` | Push a binary into the instance | Base64 or URL. |
92
+ | `trigger_restart` / `trigger_restart` | Restart Odoo on the client | Needed after some module upgrades (registry lives in memory until restart). |
93
+ | `analyze_data` | Server-side analysis helper | — |
94
+
95
+ ## Central-only tools
96
+
97
+ These have **no** `target_client` because they operate on Genius's own brain/registry:
98
+
99
+ | Tool | Use |
100
+ |---|---|
101
+ | `query_knowledge` | Vector search the Genius Brain (Gemini embeddings). Run BEFORE debugging/building. Filters: `category`, `odoo_version`. |
102
+ | `capture_learning` | Write a learning to the Brain. Required: `name` + `category` (`breaking_change\|pattern\|antipattern\|workaround\|best_practice`) + `resolution`. `tags` = comma-separated string. |
103
+ | `bulk_manage_learnings` / `update_learning` | Maintain learnings. |
104
+ | `get_my_profile` | Your consultant profile (specialty, leverage stats). Self-orient at session start. |
105
+ | `upload_code_graph` | Push a code-graph for a repo. |
106
+
107
+ Note: the central `xma.client` registry itself is just data on beesmart — read/manage it with a
108
+ plain (no-`target_client`) `execute_orm` / `search_read` on model `xma.client`.
109
+
110
+ ## Cross-cutting quirks
111
+
112
+ - **Service user, not admin.** Client-routed calls run as the client's `service_user_id`
113
+ (a non-admin). Record rules apply. If a write is silently empty, it may be an ACL issue, not
114
+ a tool failure.
115
+ - **`SUPERUSER_ID` is rejected** for routed tool execution (security). Don't pass `user_id=1`.
116
+ - **Savepoint semantics.** A routed call that raises rolls back its savepoint — partial writes
117
+ won't persist, which is good, but means you must fix and re-run cleanly.
118
+ - **safe_eval limits** (in `execute_orm`, page `data_code`, endpoint `handler_code`): no
119
+ `import`, no `hasattr`/`getattr`/`__class__`/`dir`/`del`. Use the pre-imported names. Use
120
+ `datetime.datetime.now()` (datetime is the module). Test field presence with
121
+ `'field' in record._fields`, not `hasattr`.
122
+ - **Output ~100KB cap** on `execute_orm` — page large reads.
123
+ - **Version truth is unreliable on the central** for `xma_connect_version` (a known stale-
124
+ constant bug, plus divergent deployed bundles). To learn a client's REAL `xma_connect`
125
+ version, route an `execute_orm` reading
126
+ `env['ir.module.module'].search([('name','=','xma_connect')]).installed_version` to the
127
+ client — don't trust the central `xma.client.xma_connect_version` field. And because even the
128
+ real version can mislead (a "5.0.0" without the webapp infra that landed at 4.0.0), gate
129
+ feature availability on **probing the actual model/capability**, not on the version string.