@fleox-agent-crm/agentcrm 0.0.0-stage → 0.3.1
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.
- package/LICENSE +143 -0
- package/README.md +223 -2
- package/changelog.json +76 -0
- package/dist/admin/admin-files.json +26 -0
- package/dist/admin/assets/chartsImpl-B45AAqnr.js +91 -0
- package/dist/admin/assets/firebase-C1x_l9P9.js +2 -0
- package/dist/admin/assets/gitJson-CAUSxl8d.js +3 -0
- package/dist/admin/assets/index-ChFauO8y.js +1181 -0
- package/dist/admin/assets/index-TcjySLVg.css +1 -0
- package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
- package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
- package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
- package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
- package/dist/admin/assets/lucide-react.min--rznIkmQ.js +6 -0
- package/dist/admin/assets/sdk-DgzLACmL.js +1717 -0
- package/dist/admin/config.js +3 -0
- package/dist/admin/favicon.svg +1 -0
- package/dist/admin/index.html +139 -0
- package/dist/admin/runtime/agentcrm-runtime.js +104 -0
- package/dist/admin/runtime/lucide-react.js +791 -0
- package/dist/admin/runtime/react-dom-client.js +17 -0
- package/dist/admin/runtime/react-dom.js +20 -0
- package/dist/admin/runtime/react-i18next.js +19 -0
- package/dist/admin/runtime/react-jsx-runtime.js +17 -0
- package/dist/admin/runtime/react.js +22 -0
- package/dist/agent/agentcrm.mjs +4057 -0
- package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
- package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
- package/dist/agent/chunks/api-Cz0Ubj-B.mjs +98 -0
- package/dist/agent/chunks/appearance-BloHbiLA.mjs +174 -0
- package/dist/agent/chunks/automations-DwWruxmu.mjs +698 -0
- package/dist/agent/chunks/backup-W3CjgkAJ.mjs +116 -0
- package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
- package/dist/agent/chunks/compact-BGHI92tm.mjs +69 -0
- package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
- package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
- package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
- package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
- package/dist/agent/chunks/exportCsv-C6e6XEmB.mjs +130 -0
- package/dist/agent/chunks/extCheck-GgHG1dYB.mjs +200 -0
- package/dist/agent/chunks/extPackage-DuZ6GZhp.mjs +115 -0
- package/dist/agent/chunks/extScaffold-Aqyjf1nr.mjs +320 -0
- package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
- package/dist/agent/chunks/extensions-Bp72uZ9a.mjs +164 -0
- package/dist/agent/chunks/featureRegistry-DSsZbhrh.mjs +149 -0
- package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
- package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
- package/dist/agent/chunks/files-D1daDgY0.mjs +367 -0
- package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
- package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
- package/dist/agent/chunks/forms-BfuWaQjA.mjs +847 -0
- package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
- package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
- package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
- package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
- package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
- package/dist/agent/chunks/importCsv-LVmMTaWU.mjs +334 -0
- package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
- package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
- package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
- package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
- package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
- package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
- package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
- package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
- package/dist/agent/chunks/install-CbpD_7gd.mjs +188 -0
- package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
- package/dist/agent/chunks/loader-MPD9iAvh.mjs +184 -0
- package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
- package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
- package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
- package/dist/agent/chunks/modules-clWdE-K2.mjs +202 -0
- package/dist/agent/chunks/nodeImport-ylfRDvqt.mjs +38 -0
- package/dist/agent/chunks/package-B7mI6HGi.mjs +194 -0
- package/dist/agent/chunks/papaparse-Df7i1dHr.mjs +1309 -0
- package/dist/agent/chunks/pipelines-BAKpPk1w.mjs +234 -0
- package/dist/agent/chunks/plan-BmTat6-T.mjs +254 -0
- package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
- package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
- package/dist/agent/chunks/records-DcHFelq5.mjs +2195 -0
- package/dist/agent/chunks/registry-COfJrSmg.mjs +182 -0
- package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
- package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
- package/dist/agent/chunks/reports-BACHGccj.mjs +189 -0
- package/dist/agent/chunks/runtime-C0Xf0Axi.mjs +14873 -0
- package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
- package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
- package/dist/agent/chunks/schema-C4lSslXv.mjs +373 -0
- package/dist/agent/chunks/schema-DnLj8dUO.mjs +213 -0
- package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
- package/dist/agent/chunks/seed-6MRmQQBR.mjs +541 -0
- package/dist/agent/chunks/server-DC3t8r30.mjs +242 -0
- package/dist/agent/chunks/settings-Di8nzNEL.mjs +111 -0
- package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
- package/dist/agent/chunks/stats-BfuLWhVV.mjs +51 -0
- package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
- package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
- package/dist/agent/chunks/tools-iSH-yMpl.mjs +884 -0
- package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
- package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
- package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
- package/dist/agent/chunks/views-DH1n9Lrt.mjs +109 -0
- package/dist/version.json +7 -0
- package/package.json +97 -4
- package/plugin/.claude-plugin/plugin.json +17 -0
- package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
- package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
- package/plugin/skills/agentcrm-admin/references/import.md +16 -0
- package/plugin/skills/agentcrm-admin/references/records.md +30 -0
- package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
- package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
- package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
- package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
- package/plugin/skills/agentcrm-extensions/references/testing.md +78 -0
- package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
- package/plugin/skills/agentcrm-install/SKILL.md +116 -0
- package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
- package/plugin/skills/agentcrm-install/references/github.md +20 -0
- package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
- package/plugin/skills/agentcrm-install/references/plans.md +45 -0
- package/plugin/skills/agentcrm-install/references/s3.md +24 -0
- package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
- package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
- package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
- package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
- package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
- package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
- package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
- package/types/agentcrm-runtime.d.ts +894 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentcrm-admin
|
|
3
|
+
description: Run an AgentCRM like its admin does - find, list, create, update, delete, restore and merge records (companies, people, deals, custom objects), log notes, calls, emails and meetings, manage tasks, set contact photos / company logos and attach files (images, PDFs, documents) to records, notes and tasks, move deals through pipeline stages, edit the data model (custom objects and fields), saved views, users and roles, settings, turn modules on and off, change the appearance (brand color, logo, favicon, presets, themes, custom CSS), enable / disable installed extensions, import and export CSV / JSON, reports, automations and lead forms, and call the AI tool catalog. Use when the user wants to read or change data, features or the look of one of their AgentCRM CRMs.
|
|
4
|
+
license: PolyForm-Noncommercial-1.0.0
|
|
5
|
+
compatibility: Requires Node.js 20+.
|
|
6
|
+
metadata:
|
|
7
|
+
version: "0.3.1"
|
|
8
|
+
repository: "https://github.com/fleox/agentcrm"
|
|
9
|
+
allowed-tools: Bash Read Write
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Run an AgentCRM
|
|
13
|
+
|
|
14
|
+
The CLI runs the admin's own engine headless: the same validation, roles and automations apply. It acts as the site's CLI user (an admin by default; `--as <email>` acts as another user).
|
|
15
|
+
|
|
16
|
+
**Language:** talk to the user in their language; keep commands and files in English.
|
|
17
|
+
|
|
18
|
+
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.1 <command> --json`; always pass `--site <alias>` (see `agentcrm-manage` to pick one). Read `error.hint` when `ok` is false. `npx -y @fleox-agent-crm/agentcrm@0.3.1 help records --json` lists any command's actions and options.
|
|
19
|
+
|
|
20
|
+
## Rules
|
|
21
|
+
|
|
22
|
+
- **Learn the model first:** `objects list`, then `objects get <object>` for field ids, types, select options and relations. Never guess field ids.
|
|
23
|
+
- **Search before creating** (`records search`) to avoid duplicates.
|
|
24
|
+
- **Destructive commands print a plan** (`needsConfirmation: true`) unless `--yes`: show the plan, ask, then re-run with `--yes`. This covers `records delete`, `records merge`, `files delete`, `objects delete`, `fields delete`, `views delete`, `import csv` (dry run by default), `compact`, `automations run`, `forms publish`, `forms ingest`, and every workspace-wide change: `modules enable|disable|config set`, `appearance set|preset|logo|favicon|css|reset`, `ext install|enable|disable|uninstall`.
|
|
25
|
+
- Personal data: don't paste whole exports into the chat; summarise.
|
|
26
|
+
|
|
27
|
+
## Records
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records list --site acme --object deals --filter stage:in:proposal,negotiation --filter amount:gte:10000 --sort amount:desc --limit 20 --json
|
|
31
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records get <id> --site acme --json
|
|
32
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records search "globex" --site acme --json
|
|
33
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records create --site acme --object people --set firstName=Grace --set lastName=Hopper --set email=grace@globex.com --set company=<companyId> --json
|
|
34
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records update <id> --site acme --set jobTitle=CTO --json
|
|
35
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records delete <id> --site acme --json
|
|
36
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records restore <id> --site acme --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Filters: `--filter field:op:value`, repeatable. Ops: eq, neq, contains, notContains, empty, notEmpty, gt, gte, lt, lte, in, notIn, before, after, withinDays. Dates: `YYYY-MM-DD`, `today`, `+30d`. Also `id`, `ownerId`, `createdAt`, `updatedAt`.
|
|
40
|
+
- Values: `--set key=value` (numbers, true / false, null and JSON arrays are parsed; phone numbers and zip codes stay text) or `--fields '{"...": ...}'` / `--fields @file.json`. Select fields take the option id or label; relations take record ids (arrays for "many"); `null` clears.
|
|
41
|
+
- `--columns name,email` trims the output; `--view <id>` applies a saved view; `--deleted` lists the trash.
|
|
42
|
+
- Duplicates: `records duplicates --object people --by email,name`, then `records merge <primaryId> <dupId>...` (plan, then `--yes`). Merging fills empty fields and moves relations and activities.
|
|
43
|
+
|
|
44
|
+
More examples per object: references/records.md.
|
|
45
|
+
|
|
46
|
+
## Timeline and tasks
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 notes add <recordId> --site acme --body "Met at the expo; wants a demo in June." --json
|
|
50
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 activities add --site acme --type call --title "Discovery call" --record <personId> --record <dealId> --meta '{"durationMin":25}' --json
|
|
51
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 activities list --site acme --record <companyId> --related --json
|
|
52
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tasks add --site acme --title "Send the proposal" --due 2026-11-10 --assignee me --record <dealId> --json
|
|
53
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tasks list --site acme --assignee me --overdue --json
|
|
54
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tasks done <taskId> --site acme --json
|
|
55
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tasks reassign <taskId> --site acme --assignee sam@acme.com --json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Activity types: note, task, call, email, meeting. `--body @notes.md` reads a file.
|
|
59
|
+
|
|
60
|
+
## Images and attachments
|
|
61
|
+
|
|
62
|
+
People have a **photo** (`avatar` field) and companies a **logo** (`logo`), shown next to the name everywhere; custom objects get one by adding an `image` field (the first becomes the object's image). Files (any type: images, PDFs, Office documents…) attach to a record or to an activity (note, task, call, email, meeting).
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records image <personId> ./ada.jpg --site acme --json
|
|
66
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records image <companyId> ./logo.svg --site acme --json
|
|
67
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records image <personId> --remove --site acme --json
|
|
68
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 notes add <recordId> --body "Signed contract attached." --attach ./contract.pdf --site acme --json
|
|
69
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tasks add --title "Review the quote" --record <dealId> --attach ./quote.xlsx --attach ./scope.pdf --site acme --json
|
|
70
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 activities add --type meeting --title "Workshop" --record <companyId> --attach ./whiteboard.png --site acme --json
|
|
71
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files upload ./brochure.pdf ./photo.png --record <companyId> --site acme --json
|
|
72
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files upload ./minutes.docx --activity <noteId> --site acme --json
|
|
73
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files list --record <companyId> --related --site acme --json
|
|
74
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files get <fileId> --out ./contract.pdf --site acme --json
|
|
75
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files delete <fileId> --site acme --json
|
|
76
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 files usage --site acme --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Files live next to the data and are **encrypted with the same passphrase** on the journal backend; IndexedDB in local mode; the data repo (`files/`) for git-json (warn: history keeps them); Firebase Storage for Firebase (Storage must be enabled, see docs/files.md).
|
|
80
|
+
- Limit: 25 MB per file by default (`settings set --set maxUploadMb=50`). The admin resizes photos (2560 px) and makes thumbnails; the CLI uploads files as they are — resize big photos before uploading when you can.
|
|
81
|
+
- `files list` returns metadata only (name, type, size, what it is attached to); `files get` writes the decrypted bytes to a local file — never paste file contents into the chat.
|
|
82
|
+
- `files delete` is a soft delete (plan, then `--yes`): the file disappears from records and activities; `compact --yes` physically erases the bytes (also of files whose record / activity was purged) — GDPR erasure.
|
|
83
|
+
- In Markdown bodies an image can be shown inline with `` (upload it with `files upload --activity <id>` or `--attach` first).
|
|
84
|
+
- AI tools: `list_files`, `describe_attachments` (metadata only; nothing fetches a URL into the CRM). Backups with files: `export zip --out backup.zip` / `import zip backup.zip` (plan, then `--yes`).
|
|
85
|
+
|
|
86
|
+
## Pipelines
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 pipelines list --site acme --json
|
|
90
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 pipelines move <dealId> Negotiation --site acme --json
|
|
91
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 pipelines move <dealId> Lost --lost-reason "Budget" --site acme --json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Moving sets the deal's probability to the stage's. Adding / renaming stages is done in the admin (Settings → Data model).
|
|
95
|
+
|
|
96
|
+
## Data model, views, users, settings (admin role)
|
|
97
|
+
|
|
98
|
+
See references/schema.md for objects, fields (types, options, relations), views, users and roles, and settings.
|
|
99
|
+
|
|
100
|
+
## Modules: disable what you don't need
|
|
101
|
+
|
|
102
|
+
Built-in modules: dashboard, deals (deals object + pipelines), reports, forms, tasks, automations, integrations (email and calendar), assistant (in-admin AI); records, search, settings, setup and customize are required. Installed extension modules are listed with `source: "extension"`. Disabling hides pages, navigation, widgets and owned objects for every user; it never deletes data, and enabling again brings everything back.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules list --site acme --json
|
|
106
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules disable forms --site acme --json
|
|
107
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules disable forms --site acme --yes --json
|
|
108
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules enable deals --site acme --cascade --yes --json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- When the user says "we only manage contacts" or "hide what we don't use": list the modules, propose which to turn off (with what each hides), and apply only after they agree.
|
|
112
|
+
- A `conflict` error lists the modules that depend on it (`error.message`); `--cascade` also disables them (enable: also enables the dependencies). Show the plan's `changed` list first.
|
|
113
|
+
- Module configuration (JSON, used by extensions such as sales goals): `modules config get <id>` / `modules config set <id> --value '<json>'` (plan, then `--yes`). Never store secrets there.
|
|
114
|
+
|
|
115
|
+
## Appearance: make it look like our brand
|
|
116
|
+
|
|
117
|
+
One accent color drives the whole palette (a 50 to 950 scale is generated). Applies live to every user.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance get --site acme --json
|
|
121
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance presets --site acme --json
|
|
122
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance set --site acme --accent "#0f766e" --neutral stone --radius lg --json
|
|
123
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance preset ocean --site acme --yes --json
|
|
124
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance logo ./logo.svg --site acme --yes --json
|
|
125
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance favicon ./favicon.png --site acme --yes --json
|
|
126
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance css ./brand.css --site acme --yes --json
|
|
127
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 appearance reset --site acme --json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- **Brand from a hex:** `appearance set --accent <hex>`, then pick `--neutral` by temperature (blue / teal brands: slate; warm brands: stone; otherwise zinc), `--buttons accent` for a colorful brand. **From a logo:** read the dominant non-gray color of the SVG (its `fill`) or the image, propose it as the accent, and upload the logo itself with `appearance logo`.
|
|
131
|
+
- Options and values: references/appearance.md. Each change prints the before / after plan; apply with `--yes`.
|
|
132
|
+
- Presets: default, ocean, forest, sunset, graphite, rose, high-contrast, plus installed theme extensions. A preset keeps the logo, favicon and custom CSS.
|
|
133
|
+
|
|
134
|
+
## Extensions (installed modules and themes)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext list --site acme --json
|
|
138
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext disable sales-goals --site acme --yes --json
|
|
139
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext enable sales-goals --site acme --yes --json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Installing, building or debugging extensions: the `agentcrm-extensions` skill. Installing runs third-party code with admin rights: always show the plan and ask. If the admin misbehaves after an extension change, `ext disable <id> --yes`, or open the admin with `?safe=1`; CLI commands accept `--safe` to skip extensions.
|
|
143
|
+
|
|
144
|
+
## Import and export
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 import csv contacts.csv --site acme --object people --json
|
|
148
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 import csv contacts.csv --site acme --object people --yes --json
|
|
149
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 export csv --site acme --object companies --out companies.csv --json
|
|
150
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 export json --site acme --out backup.json --json
|
|
151
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 export zip --site acme --out backup.zip --json
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The import is a DRY RUN until `--yes`: read the report (mapping, per-row action, errors, new select options, companies to create) with the user before applying. Mapping, dedupe and coercion rules: references/import.md.
|
|
155
|
+
|
|
156
|
+
## Reports and stats
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report pipeline --site acme --json
|
|
160
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report forecast --site acme --json
|
|
161
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report won-lost --site acme --months 6 --json
|
|
162
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report activity --site acme --days 30 --json
|
|
163
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report lead-sources --site acme --json
|
|
164
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report stale --site acme --days 21 --json
|
|
165
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 report conversion --site acme --json
|
|
166
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 stats --site acme --json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Automations and lead forms
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 automations list --site acme --json
|
|
173
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 automations disable <id> --site acme --json
|
|
174
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 automations run --site acme --json
|
|
175
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 forms list --site acme --json
|
|
176
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 forms publish <formId> --site acme --json
|
|
177
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 forms ingest --site acme --json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Event automations (record created, field changed, stage changed) fire on every write, from the CLI too. Building automations and forms is done in the admin; the CLI enables / disables, runs the scheduled ones and ingests form submissions. For a cron, see the `agentcrm-manage` skill.
|
|
181
|
+
|
|
182
|
+
## AI tool catalog
|
|
183
|
+
|
|
184
|
+
The same tools the MCP server and the in-admin assistant expose, including the tools of enabled extension modules (`source: "extension"`) the customisation tools (`list_modules`, `set_module_enabled`, `get_appearance`, `set_appearance`, `apply_appearance_preset`, `list_extensions`, `set_extension_enabled`) and the file tools (`list_files`, `describe_attachments`):
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tools list --site acme --json
|
|
188
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tools call pipeline_summary --site acme --input '{}' --json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Useful when a tool returns exactly what you need in one call (`get_record` with relations and latest activities, `pipeline_summary`).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Appearance options
|
|
2
|
+
|
|
3
|
+
`appearance set` takes any subset; `"null"` resets one value to its default. Every change is workspace-wide (all users, applied live in open admins) and prints a plan (`data.plan.changes`: key → from / to) until `--yes`.
|
|
4
|
+
|
|
5
|
+
| Option | Key | Values | Default |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| `--accent` | accent | any `#rrggbb` (a 50 to 950 scale is generated, 500 = this color) | `#3366ff` |
|
|
8
|
+
| `--neutral` | neutral | zinc, slate, stone, gray, neutral | zinc |
|
|
9
|
+
| `--radius` | radius | none, sm, md, lg, xl | md |
|
|
10
|
+
| `--density` | density | compact (shorter rows and paddings), comfortable | comfortable |
|
|
11
|
+
| `--font` | font | system (no network) or a Google font: inter, geist, roboto, open-sans, lato, source-sans-3, ibm-plex-sans, dm-sans, manrope, work-sans, plus-jakarta-sans, figtree, outfit, nunito-sans, poppins, space-grotesk | system |
|
|
12
|
+
| `--sidebar` | sidebar | default, dark, accent | default |
|
|
13
|
+
| `--mode` | defaultMode | system, light, dark (only for users who never toggled the mode themselves) | system |
|
|
14
|
+
| `--buttons` | buttons | neutral (near-black), accent | neutral |
|
|
15
|
+
| `--contrast` | contrast | normal, high | normal |
|
|
16
|
+
| `--theme` | themeId | an installed, enabled theme extension id | none |
|
|
17
|
+
|
|
18
|
+
Changing a visual value marks the preset `custom`. Other actions:
|
|
19
|
+
|
|
20
|
+
- `appearance preset <id>`: default, ocean, forest, sunset, graphite, rose, high-contrast, or a theme extension id. Keeps logo, favicon, custom CSS and the default mode.
|
|
21
|
+
- `appearance logo <file>` / `appearance favicon <file>`: PNG, JPEG, GIF, WebP, SVG or ICO, at most 200 KB (SVGs with scripts or event handlers are refused); `--remove` deletes it.
|
|
22
|
+
- `appearance css <file|->`: plain CSS (no `<style>` / `<script>`), at most 50 KB, loaded last; `--remove` deletes it. Style through the CSS variables (`--acc-50` to `--acc-950`, `--srf-50` to `--srf-950`, `--primary`, `--radius-scale`, `--font-sans`, `--row-h`…) as `rgb(var(--acc-600))`.
|
|
23
|
+
- `appearance reset`: back to defaults, keeping logo and favicon (`--branding` removes them too).
|
|
24
|
+
- `appearance get --full` includes the logo / favicon data URLs and the custom CSS.
|
|
25
|
+
|
|
26
|
+
Choosing a palette for a brand: keep the accent a mid-tone (very light accents make buttons hard to read); slate for cool brands, stone for warm ones; `--sidebar accent` only for strong brands; offer `--contrast high` when the user mentions accessibility.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# CSV import
|
|
2
|
+
|
|
3
|
+
1. Dry run (default): `import csv <file> --object <id> --site <alias> --json`.
|
|
4
|
+
2. Read `data.mapping` (header to field id; `""` = skipped), `unmappedColumns`, `created` / `updated` / `skipped` / `failed`, `rows[]` (per row: action, id, messages), `newOptions` (select values that will be added), `createdRelated` (companies created from a company column).
|
|
5
|
+
3. Fix the mapping if needed: `--map '{"E-mail":"email","Societe":"company","Source":"leadSource"}'` or `--map "E-mail=email,Societe=company"`. Special targets: `$id` (update by record id), `$owner` (owner by email or name), `""` (skip).
|
|
6
|
+
4. Apply with `--yes`.
|
|
7
|
+
|
|
8
|
+
## Rules
|
|
9
|
+
|
|
10
|
+
- **Dedupe**: people by email, companies by domain, other objects by their label field. `--dedupe update` (default) fills the existing record, `skip` leaves it, `create` makes a new one anyway.
|
|
11
|
+
- **Coercion**: numbers accept `1 200,50` and `$1,200.50`; dates accept ISO, `DD/MM/YYYY` with `--day-first`; booleans yes / no / true / false / 1 / 0; multi values split on `,` `;` `|`.
|
|
12
|
+
- **Relations**: a `company` column with a name or domain links the person to that company, creating it when missing (`createdRelated`).
|
|
13
|
+
- **Delimiter** is detected (`,` `;` tab); force it with `--delimiter ";"`.
|
|
14
|
+
- Re-importing an `export csv` file updates the same records (its first column is the id).
|
|
15
|
+
|
|
16
|
+
Show the user the counts and the errors before applying; never apply an import with unexplained errors.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Records by object
|
|
2
|
+
|
|
3
|
+
Standard objects (always present; custom objects come on top):
|
|
4
|
+
|
|
5
|
+
- `companies`: name, domain, industry (select), employees, annualRevenue (currency), address, linkedin, phone, tags.
|
|
6
|
+
- `people`: firstName, lastName, name (computed), email (unique), phone, jobTitle, company (relation to companies), city, linkedin, lifecycleStage (subscriber, lead, mql, sql, opportunity, customer, evangelist), leadSource, tags.
|
|
7
|
+
- `deals`: name, amount (currency), pipeline, stage, closeDate, probability, company (relation), contacts (relation to people, many), lostReason.
|
|
8
|
+
|
|
9
|
+
Always confirm with `objects get <object>`: admins add fields and options.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# A company and its contact
|
|
15
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records create --site acme --object companies --set name="Globex" --set domain=globex.com --set industry=software --json
|
|
16
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records create --site acme --object people --fields @grace.json --json
|
|
17
|
+
|
|
18
|
+
# A deal linked to both
|
|
19
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records create --site acme --object deals --fields '{"name":"Globex pilot","amount":12000,"closeDate":"2026-12-15","company":"<companyId>","contacts":["<personId>"]}' --json
|
|
20
|
+
|
|
21
|
+
# Leads created this week, without an owner
|
|
22
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records list --site acme --object people --filter lifecycleStage:eq:lead --filter createdAt:withinDays:-7 --filter ownerId:empty --json
|
|
23
|
+
|
|
24
|
+
# Assign an owner
|
|
25
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 records update <id> --site acme --owner <userId> --json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`records get` returns `relations` (records this one points to) and `related` (records pointing to it, e.g. a company's people and deals) plus `activityCount`.
|
|
29
|
+
|
|
30
|
+
Soft delete keeps the record restorable (`records restore`) until `compact`; `--hard` removes at once (also asks for `--yes`).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Data model, views, users, settings
|
|
2
|
+
|
|
3
|
+
All schema commands need the admin role.
|
|
4
|
+
|
|
5
|
+
## Objects
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 objects list --site acme --json
|
|
9
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 objects get deals --site acme --json
|
|
10
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 objects create --site acme --singular Project --plural Projects --icon folder --json
|
|
11
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 objects update projects --site acme --plural "Client projects" --json
|
|
12
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 objects delete projects --site acme --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Deleting an object deletes all its records (plan first).
|
|
16
|
+
|
|
17
|
+
## Fields
|
|
18
|
+
|
|
19
|
+
Types: text, longText, number, currency, percent, date, datetime, boolean, select, multiSelect, email, phone, url, relation, user, rating, address, tags, image (one picture; the first one becomes the record avatar), files (attachments).
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 fields add --site acme --object deals --label "Contract type" --type select --options "New,Renewal,Upsell" --json
|
|
23
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 fields add --site acme --object projects --label Client --type relation --relation-object companies --json
|
|
24
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 fields update contractType --site acme --object deals --options "New,Renewal,Upsell,Cross-sell" --json
|
|
25
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 fields delete contractType --site acme --object deals --json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`fields update --type <t>` converts existing values (the result reports what could not be converted). Standard fields can't be deleted.
|
|
29
|
+
|
|
30
|
+
## Views
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 views list --site acme --object deals --json
|
|
34
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 views create --site acme --object deals --name "Closing this month" --filter closeDate:withinDays:30 --sort closeDate --columns name,amount,stage,closeDate --shared --json
|
|
35
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 views create --site acme --object deals --name Board --type kanban --group-by stage --json
|
|
36
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 views delete <viewId> --site acme --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Users and roles
|
|
40
|
+
|
|
41
|
+
Roles: `admin` (everything), `member` (records, activities, own views, import / export), `viewer` (read only).
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 users list --site acme --all --json
|
|
45
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 users add --site acme --email sam@acme.com --name "Sam Rivera" --role member --json
|
|
46
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 users role sam@acme.com admin --site acme --json
|
|
47
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 users disable sam@acme.com --site acme --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Firebase / git-json sites: the login itself is created on that service (Firebase Authentication, a repository collaborator).
|
|
51
|
+
|
|
52
|
+
## Settings
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 settings get --site acme --json
|
|
56
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 settings set --site acme --set name="Acme CRM" --set defaultCurrency=EUR --set locale=fr-FR --set staleDealDays=21 --json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Secrets are refused in settings.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentcrm-extensions
|
|
3
|
+
description: Create, test and ship AgentCRM extensions - scaffold a module or theme project, write pages, record tabs, dashboard widgets, settings pages with module config, AI tools, automation actions and hooks against the @agent-crm/runtime API, validate the bundle exactly as the admin loads it, pack the ZIP and install / enable / disable / uninstall / export it on a CRM. Use when the user wants a new module or theme for AgentCRM, wants to change or debug one, or asks how AgentCRM extensions work.
|
|
4
|
+
license: PolyForm-Noncommercial-1.0.0
|
|
5
|
+
compatibility: Requires Node.js 20+ (npm for the extension project's own build).
|
|
6
|
+
metadata:
|
|
7
|
+
version: "0.3.1"
|
|
8
|
+
repository: "https://github.com/fleox/agentcrm"
|
|
9
|
+
allowed-tools: Bash Read Write Edit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Build AgentCRM modules and themes
|
|
13
|
+
|
|
14
|
+
**Language:** talk to the user in their language; code, comments, files and these instructions stay in English. Ship UI strings in English and French (`i18n: { en, fr }`).
|
|
15
|
+
|
|
16
|
+
## How AgentCRM runs extensions (read this first)
|
|
17
|
+
|
|
18
|
+
AgentCRM is a static CRM: the admin is a single-page app that runs in the browser, the CLI / MCP server run the same engine in Node. There is no server. An extension is **one ES module** (`bundle.js`) plus `manifest.json` (and an optional `style.css`), packed in a ZIP:
|
|
19
|
+
|
|
20
|
+
- Installing stores the package **in the CRM's own data** (collection `extensions`, encrypted with the rest, shared by every user). No hosting, no rebuild.
|
|
21
|
+
- When the admin starts (and when an extension is installed / enabled / upgraded, live), it `import()`s every enabled `bundle.js` and reads its **default export**: `defineModule({...})` or `defineTheme({...})`.
|
|
22
|
+
- The bundle gets React, react-dom, react-i18next, lucide-react and the CRM API (`@agent-crm/runtime`) from the admin. These must stay **external** at build time (the scaffold's `vite.config.ts` does it). Any other import must be bundled in.
|
|
23
|
+
- **Modules** add a Feature (routes + navigation, slots: record tabs / panels / actions, dashboard widgets, settings sections, overlays), a settings page (Settings, Modules, the module), AI tools (MCP, in-admin assistant, `agentcrm tools`), automation actions (automation editor) and hooks on CRM actions (`register(api)`). The module id is also its i18n namespace and its module-config key.
|
|
24
|
+
- **Themes** are a set of appearance values (accent, neutral, radius, density, font, sidebar, buttons, contrast, default mode) plus optional CSS. They show up as presets in Settings, Appearance.
|
|
25
|
+
- The CLI loads the same enabled modules in every session: their AI tools appear in `tools list` and the MCP server, their hooks see CLI writes, their automation actions run in `automations run`. `--safe` skips them.
|
|
26
|
+
|
|
27
|
+
## Safety rules (always)
|
|
28
|
+
|
|
29
|
+
- **Installing runs code with admin rights.** The bundle runs in the admin of every user who opens the CRM (admins included) and in CLI / MCP sessions. Before `ext install … --yes`, show the user the plan (id, version, author, upgrade or not, size, dependencies) and get an explicit yes. Never install a ZIP from an unknown source without saying so.
|
|
30
|
+
- Every write in this skill prints a plan unless `--yes`: `ext install`, `ext enable`, `ext disable`, `ext uninstall`, `modules enable|disable|config set`, `appearance …`. Show the plan, ask, then re-run with `--yes`.
|
|
31
|
+
- Never put secrets (API keys, tokens, passwords) in module config or in the bundle: they would be readable by every CRM user. `modules config set` refuses well-known secret keys.
|
|
32
|
+
- If an extension breaks the admin: `ext disable <id> --yes` from the CLI, or open the admin with `?safe=1` (or hold Shift while it loads) to start without extensions.
|
|
33
|
+
|
|
34
|
+
## The toolchain: `agentcrm ext`
|
|
35
|
+
|
|
36
|
+
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.1 <command> … --json`. Read `error.hint` when `ok` is false.
|
|
37
|
+
|
|
38
|
+
| Action | What it does |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `ext new module\|theme <id> [--dir <path>] [--name "…"] [--description "…"] [--author "…"] [--force]` | Scaffolds a standalone project: `package.json` (scripts build / typecheck / check / pack), `vite.config.ts` (runtime specifiers external, one `bundle.js`), `tsconfig.json` mapped to `types/agentcrm-runtime.d.ts` (copied from the CLI), `manifest.json`, `src/index.tsx` (module: a page with a nav entry, a record tab, an AI tool, en + fr strings) or `src/index.ts` + `src/style.css` (theme), README. |
|
|
41
|
+
| `ext check [<dir\|zip>] [--kind module\|theme] [--no-call]` | Validates manifest (id, kind, semver, apiVersion range, unknown keys), sizes, bundle imports (runtime specifiers only), default export, production JSX; then installs it in a throw-away demo CRM, loads it through the real loader and reports what it registers: routes, navigation, slots, settings page, i18n, AI tools (each one without required inputs is called once with `{}` on demo data), automation actions, hooks, theme values. Exit code 1 when `data.valid` is false. |
|
|
42
|
+
| `ext pack [<dir>] [--out <zip>]` | Writes `<id>-<version>.zip` (manifest.json + bundle.js + style.css) for Settings, Modules, Install, or `ext install`. |
|
|
43
|
+
| `ext install <zip\|dir> --site <alias> [--disabled] [--force] [--yes]` | First loads the bundle in a throw-away CRM like `ext check` (a package that fails is refused with `invalid-package` and nothing changes; `--force` skips this). Plan (install or upgrade, author, size, dependencies, warnings: downgrade, same version), then installs and loads it in the session (`load.status` must be `loaded`). Upgrades keep the enabled state and the module config. |
|
|
44
|
+
| `ext list --site <alias>` | Installed extensions with version, enabled state and load status. |
|
|
45
|
+
| `ext enable\|disable <id> --site <alias> [--cascade] [--yes]` | Switch it on or off without uninstalling. |
|
|
46
|
+
| `ext uninstall <id> --site <alias> [--force] [--yes]` | Remove it (its module config is kept for a later re-install). |
|
|
47
|
+
| `ext export <id> --site <alias> [--out <zip>]` | Download the installed package as a ZIP. |
|
|
48
|
+
|
|
49
|
+
## Workflow
|
|
50
|
+
|
|
51
|
+
1. **Understand the request.** A new look = a **theme**. New pages, widgets, record tabs, AI tools, automation steps or reactions to CRM events = a **module**. Pick an id: lowercase letters, digits and dashes, 3 to 50 characters, not a built-in module id (records, deals, dashboard, reports, forms, tasks, automations, integrations, assistant, search, settings, setup, customize) nor agentcrm, core, runtime, admin, app, custom, default. Prefix it with the company or product (`acme-quotes`).
|
|
52
|
+
2. **Scaffold and install the toolchain:**
|
|
53
|
+
```bash
|
|
54
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext new module acme-quotes --author "Acme" --json
|
|
55
|
+
cd acme-quotes && npm install
|
|
56
|
+
```
|
|
57
|
+
3. **Read the scaffold**, then the reference for what you are building:
|
|
58
|
+
- [references/module-recipes.md](references/module-recipes.md): custom object + page, record tab, dashboard widget, AI tool, automation action, settings page with module config, hooks.
|
|
59
|
+
- [references/theme-recipes.md](references/theme-recipes.md): appearance values, CSS variables, brand themes.
|
|
60
|
+
- [references/runtime-api.md](references/runtime-api.md): everything `@agent-crm/runtime` exports. The authoritative signatures are in the project's `types/agentcrm-runtime.d.ts`.
|
|
61
|
+
- [references/testing.md](references/testing.md): build, check, pack, install, upgrade, debug, pitfalls.
|
|
62
|
+
4. **Implement**, keeping the scaffold's build settings.
|
|
63
|
+
5. **Verify after every meaningful change:**
|
|
64
|
+
```bash
|
|
65
|
+
npm run typecheck
|
|
66
|
+
npm run build
|
|
67
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext check . --json
|
|
68
|
+
```
|
|
69
|
+
Fix every error; read the warnings (missing French strings, tool names without the module prefix, nav entries without a route).
|
|
70
|
+
6. **Pack and install** (plan first, then `--yes` once the user agreed):
|
|
71
|
+
```bash
|
|
72
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext pack . --json
|
|
73
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext install . --site acme --json
|
|
74
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 ext install . --site acme --yes --json
|
|
75
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 tools list --site acme --json
|
|
76
|
+
```
|
|
77
|
+
For a new release, bump `version` in `manifest.json` (semver) and install again: it upgrades in place.
|
|
78
|
+
|
|
79
|
+
## Golden rules
|
|
80
|
+
|
|
81
|
+
- Import UI, data and helpers from `@agent-crm/runtime`, React from `react`, translations from `react-i18next`, icons from `lucide-react`. Never bundle a second React.
|
|
82
|
+
- The default export's `id` and `apiVersion` must equal `manifest.json`'s.
|
|
83
|
+
- All writes go through `ops.*` with the right context: `useOps()` in components, the `ctx` argument in AI tools, automation actions and hooks. Roles are enforced there (viewers can't write, only admins change modules / schema).
|
|
84
|
+
- Prefix AI tool names with the module id in snake_case (`acme_quotes_list`), describe them so a model knows when to call them, set `mutating` honestly.
|
|
85
|
+
- Automation action ids are `<moduleId>.<action>`.
|
|
86
|
+
- `register(api)` must only register things (hooks, tools); never write data there. Hooks run in the process that made the change (the user's admin tab, or the CLI): keep them fast and idempotent.
|
|
87
|
+
- Strings through `t("<moduleId>:key")` with `useTranslation("<moduleId>")`; ship `en` and `fr`.
|
|
88
|
+
- Every `<form>` has `data-form-type="other"`.
|
|
89
|
+
- Keep the bundle small (limit 1.5 MB, style.css 256 KB, ZIP 4 MB).
|
|
90
|
+
|
|
91
|
+
## Without writing code
|
|
92
|
+
|
|
93
|
+
Many requests need no extension at all: turning built-in modules off, changing the look, logo and colors, custom objects and fields. Use the `agentcrm-admin` skill (`modules`, `appearance`, `objects`, `fields`) first, and only build an extension when the CRM can't do it out of the box.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Module recipes
|
|
2
|
+
|
|
3
|
+
Every recipe is a fragment of `src/index.tsx` in a project made by `ext new module <id>`. They combine freely in one `defineModule({...})`. The module id (`ID` below) is also the i18n namespace (`t("<id>:key")`) and the module-config key.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { FolderKanban } from "lucide-react";
|
|
8
|
+
import { defineModule, ops, type OpsContext, type ObjectDef } from "@agent-crm/runtime";
|
|
9
|
+
|
|
10
|
+
const ID = "acme-projects";
|
|
11
|
+
export default defineModule({
|
|
12
|
+
id: ID,
|
|
13
|
+
apiVersion: "1.0.0", // = manifest.json apiVersion
|
|
14
|
+
feature: { routes: [], navItems: [], i18n: { en, fr }, slots: {} },
|
|
15
|
+
settingsPage: SettingsPage,
|
|
16
|
+
aiTools: [],
|
|
17
|
+
automationActions: [],
|
|
18
|
+
register(api) { /* hooks */ },
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 1. Custom object + its own page
|
|
23
|
+
|
|
24
|
+
Objects are data, not code: create them once (an AI tool or a settings-page button), never in `register()`. The admin then lists them in the navigation with table / kanban views like any object. Add a page of your own for a dedicated view.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
// Typed views of ops that are not in RuntimeOps' typed subset.
|
|
28
|
+
const createObject = ops.createObject as (ctx: OpsContext, input: {
|
|
29
|
+
id?: string; labelSingular: string; labelPlural: string; icon?: string; color?: string;
|
|
30
|
+
fields?: { id?: string; label: string; type: string; options?: string[]; relation?: { object: string; cardinality: "one" | "many" } }[];
|
|
31
|
+
}) => Promise<ObjectDef>;
|
|
32
|
+
|
|
33
|
+
async function ensureProjects(ctx: OpsContext): Promise<{ created: boolean }> {
|
|
34
|
+
if (ops.listObjects(ctx).some((o) => o.id === "projects")) return { created: false };
|
|
35
|
+
await createObject(ctx, {
|
|
36
|
+
id: "projects", labelSingular: "Project", labelPlural: "Projects", icon: "FolderKanban", color: "violet",
|
|
37
|
+
fields: [
|
|
38
|
+
{ id: "status", label: "Status", type: "select", options: ["Planned", "Active", "Done"] },
|
|
39
|
+
{ id: "client", label: "Client", type: "relation", relation: { object: "companies", cardinality: "one" } },
|
|
40
|
+
{ id: "budget", label: "Budget", type: "currency" },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
return { created: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ProjectsPage() {
|
|
47
|
+
const { t } = useTranslation(ID);
|
|
48
|
+
const projects = useRecords("projects");
|
|
49
|
+
const project = useObject("projects");
|
|
50
|
+
if (!project) return <EmptyState title={t("noObject")} description={t("noObjectHint")} />;
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
<PageHeader title={t("title")} icon={<FolderKanban />} actions={<Link to={paths.objectList("projects")}>{t("openTable")}</Link>} />
|
|
54
|
+
<div className="grid gap-4 p-6 md:grid-cols-3">
|
|
55
|
+
{["planned", "active", "done"].map((status) => (
|
|
56
|
+
<Card key={status} title={t(`status.${status}`)}>
|
|
57
|
+
{projects.filter((r) => r.fields.status === status).map((r) => (
|
|
58
|
+
<Link key={r.id} to={paths.record("projects", r.id)} className="block py-1 text-sm">{recordLabel(project, r)}</Link>
|
|
59
|
+
))}
|
|
60
|
+
</Card>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// feature:
|
|
68
|
+
// routes: [{ path: "/acme-projects", element: <ProjectsPage /> }],
|
|
69
|
+
// navItems: [{ id: ID, label: `${ID}:nav`, icon: FolderKanban, to: "/acme-projects", section: "workspace", order: 40 }],
|
|
70
|
+
// aiTools: [{ name: "acme_projects_setup", description: "Creates the Projects object (status, client, budget) if it is missing.",
|
|
71
|
+
// inputSchema: { type: "object", properties: {} }, mutating: true, handler: (ctx) => ensureProjects(ctx) }],
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Select option ids are generated from the labels (`"Planned"` → `planned`); read `ops.listObjects(ctx)` for the exact ids.
|
|
75
|
+
- Creating objects needs an admin (`schema.write`). From the CLI: `agentcrm tools call acme_projects_setup --site <alias> --input '{}'`.
|
|
76
|
+
- Routes are paths of the admin's hash router (`#/acme-projects`). Use `paths.*` and `recordHref()` for links.
|
|
77
|
+
|
|
78
|
+
## 2. Record tab, panel and action
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
function InvoicesTab({ record, object }: RecordSlotProps) {
|
|
82
|
+
const { t } = useTranslation(ID);
|
|
83
|
+
const activities = useActivities(record.id);
|
|
84
|
+
return <div className="p-4 text-sm">{t("tabBody", { label: recordLabel(object, record), count: activities.length })}</div>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// feature.slots:
|
|
88
|
+
// recordDetailTabs: [{ id: "invoices", label: `${ID}:tab`, icon: Receipt, order: 60, component: InvoicesTab, when: (o) => o.id === "companies" }],
|
|
89
|
+
// recordDetailPanels: [{ id: "health", order: 10, component: HealthPanel, when: (o) => o.id === "deals" }],
|
|
90
|
+
// recordActions: [{ id: "sync", label: `${ID}:sync`, icon: RefreshCw, onSelect: ({ record }) => toast.info(record.id) }],
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`when(object)` limits a slot to some objects. Labels are i18n keys (`<id>:key`) or plain text.
|
|
94
|
+
|
|
95
|
+
## 3. Dashboard widget
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
function OpenDealsWidget() {
|
|
99
|
+
const { t } = useTranslation(ID);
|
|
100
|
+
const settings = useSettings();
|
|
101
|
+
const deals = useRecords("deals");
|
|
102
|
+
const enabled = useModuleEnabled("deals");
|
|
103
|
+
if (!enabled) return null; // the deals module can be switched off
|
|
104
|
+
const open = deals.filter((d) => !["won", "lost"].includes(String(d.fields.stage)));
|
|
105
|
+
const total = open.reduce((n, d) => n + Number(d.fields.amount ?? 0), 0);
|
|
106
|
+
return <Card title={t("widget")}><div className="text-2xl font-semibold tabular-nums">{formatCurrency(total, settings.defaultCurrency, intlLocale())}</div></Card>;
|
|
107
|
+
}
|
|
108
|
+
// feature.slots.dashboardWidgets: [{ id: "open-deals", order: 20, span: 4, component: OpenDealsWidget }] // span 4 | 6 | 8 | 12
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If the widget needs deals, declare `"dependsOn": ["deals"]` in `manifest.json`: the module can't be enabled while deals is off, and disabling deals asks to disable it too.
|
|
112
|
+
|
|
113
|
+
## 4. AI tool
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
aiTools: [{
|
|
117
|
+
name: "acme_projects_at_risk", // snake_case, prefixed, unique
|
|
118
|
+
description: "Active projects whose budget is above 80 000 or that have no activity for 30 days. Use to answer questions about project risk.",
|
|
119
|
+
inputSchema: { type: "object", properties: { days: { type: "number", description: "Inactivity threshold in days (default 30)." } } },
|
|
120
|
+
mutating: false, // true when it writes: MCP read-only mode hides it, the CLI takes the site lock
|
|
121
|
+
handler: (ctx, input) => {
|
|
122
|
+
const days = typeof input.days === "number" ? input.days : 30;
|
|
123
|
+
const page = ops.listRecords(ctx, "projects", { filters: [{ field: "status", op: "eq", value: "active" }], limit: 500 });
|
|
124
|
+
return { projects: page.items.filter((r) => Number(r.fields.budget ?? 0) > 80000 || ctx.now() - r.updatedAt > days * 86400000).map((r) => ({ id: r.id, name: r.fields.name })) };
|
|
125
|
+
},
|
|
126
|
+
}],
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Return plain JSON (small: the model reads it). Throw an `Error` with a clear message on bad input. The tool is in `agentcrm tools list`, `agentcrm tools call`, the MCP server's `tools/list` and the in-admin assistant as soon as the module is enabled.
|
|
130
|
+
|
|
131
|
+
## 5. Automation action
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const notifySlack: AutomationActionDef = {
|
|
135
|
+
id: `${ID}.flagRisk`, // "<moduleId>.<action>"
|
|
136
|
+
label: `${ID}:action.label`,
|
|
137
|
+
description: `${ID}:action.description`,
|
|
138
|
+
objects: ["projects"], // optional: only offered on these objects
|
|
139
|
+
params: [{ id: "note", label: `${ID}:action.note`, type: "text", template: true, placeholder: "{{name}} is at risk" }],
|
|
140
|
+
describe: ({ record }) => `Flag "${String(record.fields.name ?? record.id)}" as at risk`,
|
|
141
|
+
validate: (params) => (typeof params.note === "string" && params.note.length > 500 ? ["The note is too long."] : []),
|
|
142
|
+
async run(ctx, { params, record }) {
|
|
143
|
+
await ops.addNote(ctx, { object: record.object, id: record.id }, String(params.note || "At risk"), "Risk flag");
|
|
144
|
+
return "note added"; // shown in the automation run log
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
// defineModule({ automationActions: [notifySlack], … })
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The automation editor lists it next to the built-in actions; stored as `{ type: "extension", action: "acme-projects.flagRisk", params }`. It runs wherever automations run: in the admin on each write, and in `agentcrm automations run` (scheduled / inactivity triggers) because the CLI loads enabled modules. If the module is disabled, runs fail with "not available" (nothing else breaks).
|
|
151
|
+
|
|
152
|
+
## 6. Settings page with module config
|
|
153
|
+
|
|
154
|
+
Module config is a JSON document per module (`config/modules.config.<id>`, at most 256 KB, never secrets), shared by all users, written by admins.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
interface Config { budgetAlert: number }
|
|
158
|
+
const readConfig = (raw: unknown): Config => ({ budgetAlert: Number((raw as Partial<Config> | undefined)?.budgetAlert ?? 80000) });
|
|
159
|
+
|
|
160
|
+
function SettingsPage() {
|
|
161
|
+
const { t } = useTranslation(ID);
|
|
162
|
+
const ctx = useOps();
|
|
163
|
+
const canEdit = useCan("modules.write");
|
|
164
|
+
const config = readConfig(useModuleConfig(ID));
|
|
165
|
+
const [value, setValue] = useState(String(config.budgetAlert));
|
|
166
|
+
return (
|
|
167
|
+
<Card>
|
|
168
|
+
<form data-form-type="other" className="space-y-3" onSubmit={async (e) => {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
await ops.setModuleConfig(ctx, ID, { ...config, budgetAlert: Number(value) });
|
|
171
|
+
toast.success(t("saved"));
|
|
172
|
+
}}>
|
|
173
|
+
<FormField label={t("budgetAlert")}><Input type="number" value={value} disabled={!canEdit} onChange={(e) => setValue(e.target.value)} /></FormField>
|
|
174
|
+
{canEdit && <Button type="submit" variant="primary">{t("save")}</Button>}
|
|
175
|
+
</form>
|
|
176
|
+
</Card>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
// defineModule({ settingsPage: SettingsPage, … }) → Settings → Modules → <module> → /settings/modules/<id>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
In AI tools / actions read it with `readConfig(ops.getModuleConfig(ctx, ID))`. From the CLI:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules config get acme-projects --site acme --json
|
|
186
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.1 modules config set acme-projects --value '{"budgetAlert":50000}' --site acme --json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
(the second prints a plan; add `--yes`).
|
|
190
|
+
|
|
191
|
+
## 7. Hooks on CRM actions
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
register(api) {
|
|
195
|
+
api.addAction("stage.changed", async (ctx, { record, to }) => {
|
|
196
|
+
if (record.object !== "deals" || to !== "won") return;
|
|
197
|
+
await ops.createTask(ctx as OpsContext, { title: `Kick-off for ${String(record.fields.name)}`, dueAt: ctx.now() + 3 * 86400000, targets: [{ object: "deals", id: record.id }] });
|
|
198
|
+
});
|
|
199
|
+
api.addFilter("myModule.value", (v: number) => v * 2);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Actions: `record.created`, `record.updated` (`before`, `changed`), `record.deleted` (`hard`), `record.restored`, `stage.changed` (`pipelineId`, `from`, `to`), `activity.created`, `activity.updated`, `activity.completed`. The first argument is the context of the op that fired it (same user, same store). Everything registered through `api` is removed when the module is disabled or upgraded. A failing handler is logged and never blocks the write.
|
|
204
|
+
|
|
205
|
+
## 8. Strings (en + fr)
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const en = { nav: "Projects", title: "Projects board", status: { planned: "Planned", active: "Active", done: "Done" } };
|
|
209
|
+
const fr: typeof en = { nav: "Projets", title: "Tableau des projets", status: { planned: "Prévu", active: "En cours", done: "Terminé" } };
|
|
210
|
+
// feature.i18n: { en, fr } (or addBundles(ID, { en, fr }) at the top level)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`const { t } = useTranslation(ID)` in components; labels in nav items, slots and automation actions are keys like `` `${ID}:nav` ``.
|