@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.
Files changed (129) hide show
  1. package/LICENSE +143 -0
  2. package/README.md +223 -2
  3. package/changelog.json +76 -0
  4. package/dist/admin/admin-files.json +26 -0
  5. package/dist/admin/assets/chartsImpl-B45AAqnr.js +91 -0
  6. package/dist/admin/assets/firebase-C1x_l9P9.js +2 -0
  7. package/dist/admin/assets/gitJson-CAUSxl8d.js +3 -0
  8. package/dist/admin/assets/index-ChFauO8y.js +1181 -0
  9. package/dist/admin/assets/index-TcjySLVg.css +1 -0
  10. package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
  11. package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
  12. package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
  13. package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
  14. package/dist/admin/assets/lucide-react.min--rznIkmQ.js +6 -0
  15. package/dist/admin/assets/sdk-DgzLACmL.js +1717 -0
  16. package/dist/admin/config.js +3 -0
  17. package/dist/admin/favicon.svg +1 -0
  18. package/dist/admin/index.html +139 -0
  19. package/dist/admin/runtime/agentcrm-runtime.js +104 -0
  20. package/dist/admin/runtime/lucide-react.js +791 -0
  21. package/dist/admin/runtime/react-dom-client.js +17 -0
  22. package/dist/admin/runtime/react-dom.js +20 -0
  23. package/dist/admin/runtime/react-i18next.js +19 -0
  24. package/dist/admin/runtime/react-jsx-runtime.js +17 -0
  25. package/dist/admin/runtime/react.js +22 -0
  26. package/dist/agent/agentcrm.mjs +4057 -0
  27. package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
  28. package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
  29. package/dist/agent/chunks/api-Cz0Ubj-B.mjs +98 -0
  30. package/dist/agent/chunks/appearance-BloHbiLA.mjs +174 -0
  31. package/dist/agent/chunks/automations-DwWruxmu.mjs +698 -0
  32. package/dist/agent/chunks/backup-W3CjgkAJ.mjs +116 -0
  33. package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
  34. package/dist/agent/chunks/compact-BGHI92tm.mjs +69 -0
  35. package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
  36. package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
  37. package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
  38. package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
  39. package/dist/agent/chunks/exportCsv-C6e6XEmB.mjs +130 -0
  40. package/dist/agent/chunks/extCheck-GgHG1dYB.mjs +200 -0
  41. package/dist/agent/chunks/extPackage-DuZ6GZhp.mjs +115 -0
  42. package/dist/agent/chunks/extScaffold-Aqyjf1nr.mjs +320 -0
  43. package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
  44. package/dist/agent/chunks/extensions-Bp72uZ9a.mjs +164 -0
  45. package/dist/agent/chunks/featureRegistry-DSsZbhrh.mjs +149 -0
  46. package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
  47. package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
  48. package/dist/agent/chunks/files-D1daDgY0.mjs +367 -0
  49. package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
  50. package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
  51. package/dist/agent/chunks/forms-BfuWaQjA.mjs +847 -0
  52. package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
  53. package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
  54. package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
  55. package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
  56. package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
  57. package/dist/agent/chunks/importCsv-LVmMTaWU.mjs +334 -0
  58. package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
  59. package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
  60. package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
  61. package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
  62. package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
  63. package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
  64. package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
  65. package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
  66. package/dist/agent/chunks/install-CbpD_7gd.mjs +188 -0
  67. package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
  68. package/dist/agent/chunks/loader-MPD9iAvh.mjs +184 -0
  69. package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
  70. package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
  71. package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
  72. package/dist/agent/chunks/modules-clWdE-K2.mjs +202 -0
  73. package/dist/agent/chunks/nodeImport-ylfRDvqt.mjs +38 -0
  74. package/dist/agent/chunks/package-B7mI6HGi.mjs +194 -0
  75. package/dist/agent/chunks/papaparse-Df7i1dHr.mjs +1309 -0
  76. package/dist/agent/chunks/pipelines-BAKpPk1w.mjs +234 -0
  77. package/dist/agent/chunks/plan-BmTat6-T.mjs +254 -0
  78. package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
  79. package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
  80. package/dist/agent/chunks/records-DcHFelq5.mjs +2195 -0
  81. package/dist/agent/chunks/registry-COfJrSmg.mjs +182 -0
  82. package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
  83. package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
  84. package/dist/agent/chunks/reports-BACHGccj.mjs +189 -0
  85. package/dist/agent/chunks/runtime-C0Xf0Axi.mjs +14873 -0
  86. package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
  87. package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
  88. package/dist/agent/chunks/schema-C4lSslXv.mjs +373 -0
  89. package/dist/agent/chunks/schema-DnLj8dUO.mjs +213 -0
  90. package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
  91. package/dist/agent/chunks/seed-6MRmQQBR.mjs +541 -0
  92. package/dist/agent/chunks/server-DC3t8r30.mjs +242 -0
  93. package/dist/agent/chunks/settings-Di8nzNEL.mjs +111 -0
  94. package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
  95. package/dist/agent/chunks/stats-BfuLWhVV.mjs +51 -0
  96. package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
  97. package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
  98. package/dist/agent/chunks/tools-iSH-yMpl.mjs +884 -0
  99. package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
  100. package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
  101. package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
  102. package/dist/agent/chunks/views-DH1n9Lrt.mjs +109 -0
  103. package/dist/version.json +7 -0
  104. package/package.json +97 -4
  105. package/plugin/.claude-plugin/plugin.json +17 -0
  106. package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
  107. package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
  108. package/plugin/skills/agentcrm-admin/references/import.md +16 -0
  109. package/plugin/skills/agentcrm-admin/references/records.md +30 -0
  110. package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
  111. package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
  112. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
  113. package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
  114. package/plugin/skills/agentcrm-extensions/references/testing.md +78 -0
  115. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
  116. package/plugin/skills/agentcrm-install/SKILL.md +116 -0
  117. package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
  118. package/plugin/skills/agentcrm-install/references/github.md +20 -0
  119. package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
  120. package/plugin/skills/agentcrm-install/references/plans.md +45 -0
  121. package/plugin/skills/agentcrm-install/references/s3.md +24 -0
  122. package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
  123. package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
  124. package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
  125. package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
  126. package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
  127. package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
  128. package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
  129. 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 `![name](file:<fileId>)` (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` ``.