axis-crm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +122 -0
  3. package/cli/index.js +106 -0
  4. package/package.json +36 -0
  5. package/template/CLAUDE.md +38 -0
  6. package/template/CRM Dashboard.md +53 -0
  7. package/template/automations/README.md +62 -0
  8. package/template/automations/client-research.json +383 -0
  9. package/template/automations/fireflies-to-crm.json +306 -0
  10. package/template/clients/acme-consulting/README.md +43 -0
  11. package/template/clients/acme-consulting/communication/2026-03-01-cena-dohodnuta.md +24 -0
  12. package/template/clients/acme-consulting/contacts/jana-novakova.md +17 -0
  13. package/template/clients/acme-consulting/contacts/tomas-dvorak.md +16 -0
  14. package/template/clients/acme-consulting/files/faktury/.gitkeep +0 -0
  15. package/template/clients/acme-consulting/files/nabidky/.gitkeep +0 -0
  16. package/template/clients/acme-consulting/files/smlouvy/.gitkeep +0 -0
  17. package/template/clients/acme-consulting/meetings/2026-02-10-kickoff.md +25 -0
  18. package/template/clients/acme-consulting/meetings/2026-03-15-progress-review.md +25 -0
  19. package/template/clients/acme-consulting/projects/web-redesign/README.md +53 -0
  20. package/template/clients/acme-consulting/projects/web-redesign/deliverables/.gitkeep +0 -0
  21. package/template/clients/acme-consulting/projects/web-redesign/notes/.gitkeep +0 -0
  22. package/template/clients/acme-consulting/projects/web-redesign/zadani.md +33 -0
  23. package/template/dashboards/client-overview.md +25 -0
  24. package/template/dashboards/hot-leads.md +23 -0
  25. package/template/dashboards/pipeline.md +52 -0
  26. package/template/leads/bytovy-architekt-kovar.md +22 -0
  27. package/template/leads/green-energy-as.md +22 -0
  28. package/template/leads/nova-digital-sro.md +22 -0
  29. package/template/leads/restaurace-u-zlateho-lva.md +23 -0
  30. package/template/leads/techpro-solutions.md +23 -0
  31. package/template/rules/fields.md +45 -0
  32. package/template/rules/lifecycle.md +51 -0
  33. package/template/rules/scoring.md +43 -0
  34. package/template/rules/structure.md +82 -0
  35. package/template/scripts/convert-to-client.sh +133 -0
  36. package/template/scripts/new-lead.sh +68 -0
  37. package/template/scripts/new-project.sh +69 -0
  38. package/template/scripts/validate.sh +149 -0
  39. package/template/templates/client/README.md +31 -0
  40. package/template/templates/client/communication/.gitkeep +0 -0
  41. package/template/templates/client/contacts/.gitkeep +0 -0
  42. package/template/templates/client/files/.gitkeep +0 -0
  43. package/template/templates/client/files/faktury/.gitkeep +0 -0
  44. package/template/templates/client/files/nabidky/.gitkeep +0 -0
  45. package/template/templates/client/files/smlouvy/.gitkeep +0 -0
  46. package/template/templates/client/meetings/.gitkeep +0 -0
  47. package/template/templates/client/projects/.gitkeep +0 -0
  48. package/template/templates/communication.md +17 -0
  49. package/template/templates/contact.md +14 -0
  50. package/template/templates/lead.md +20 -0
  51. package/template/templates/meeting.md +17 -0
  52. package/template/templates/project/README.md +34 -0
  53. package/template/templates/project/deliverables/.gitkeep +0 -0
  54. package/template/templates/project/notes/.gitkeep +0 -0
  55. package/template/templates/project/zadani.md +19 -0
@@ -0,0 +1,383 @@
1
+ {
2
+ "name": "Axis CRM — Client Research",
3
+ "nodes": [
4
+ {
5
+ "id": "w2-webhook",
6
+ "name": "Research Webhook",
7
+ "type": "n8n-nodes-base.webhook",
8
+ "typeVersion": 2,
9
+ "position": [250, 300],
10
+ "parameters": {
11
+ "httpMethod": "POST",
12
+ "path": "axis-crm-research",
13
+ "options": {}
14
+ },
15
+ "webhookId": "axis-crm-research"
16
+ },
17
+ {
18
+ "id": "w2-parse",
19
+ "name": "Parse Input",
20
+ "type": "n8n-nodes-base.code",
21
+ "typeVersion": 2,
22
+ "position": [450, 300],
23
+ "parameters": {
24
+ "jsCode": "const body = $input.first().json.body || $input.first().json;\nconst companyName = body.company || body.name || body.firma || '';\nconst website = body.website || body.web || body.url || '';\nconst slug = body.slug || '';\nif (!companyName && !website) throw new Error('Provide company name or website');\nreturn [{ json: { companyName, website, slug } }];"
25
+ }
26
+ },
27
+ {
28
+ "id": "w2-scrape",
29
+ "name": "Scrape Website",
30
+ "type": "n8n-nodes-base.httpRequest",
31
+ "typeVersion": 4.2,
32
+ "position": [650, 300],
33
+ "parameters": {
34
+ "method": "GET",
35
+ "url": "={{ $json.website }}",
36
+ "options": {
37
+ "timeout": 15000
38
+ }
39
+ },
40
+ "continueOnFail": true
41
+ },
42
+ {
43
+ "id": "w2-extract",
44
+ "name": "Extract Text & Contacts",
45
+ "type": "n8n-nodes-base.code",
46
+ "typeVersion": 2,
47
+ "position": [850, 300],
48
+ "parameters": {
49
+ "jsCode": "const html = $input.first().json.data || $input.first().json.body || '';\nconst text = html.replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .substring(0, 8000);\nconst emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\nconst emails = [...new Set((html.match(emailRegex) || []).filter(e => !e.includes('example.com') && !e.includes('wixpress') && !e.includes('sentry')))];\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s-]?)?(?:\\(?\\d{2,4}\\)?[\\s-]?)?\\d{3}[\\s-]?\\d{3}[\\s-]?\\d{0,4}/g;\nconst phones = [...new Set((html.match(phoneRegex) || []).filter(p => p.replace(/\\D/g, '').length >= 9))];\nreturn [{\n json: {\n textContent: text,\n extractedEmails: emails.slice(0, 10),\n extractedPhones: phones.slice(0, 10),\n companyName: $('Parse Input').first().json.companyName,\n website: $('Parse Input').first().json.website,\n slug: $('Parse Input').first().json.slug\n }\n}];"
50
+ }
51
+ },
52
+ {
53
+ "id": "w2-prep-ai",
54
+ "name": "Prepare AI Request",
55
+ "type": "n8n-nodes-base.code",
56
+ "typeVersion": 2,
57
+ "position": [1050, 300],
58
+ "parameters": {
59
+ "jsCode": "const data = $input.first().json;\n\nconst prompt = `Analyzuj firmu a vytvor strukturovany profil.\n\nFirma: ${data.companyName}\nWeb: ${data.website}\nEmaily nalezene na webu: ${data.extractedEmails.join(', ')}\nTelefony nalezene na webu: ${data.extractedPhones.join(', ')}\n\nObsah webu (5000 znaku):\n${data.textContent.substring(0, 5000)}\n\nOdpovez POUZE validnim JSON:\n{\n \"company_name\": \"Oficialni nazev\",\n \"description\": \"1-2 vety co firma dela\",\n \"industry\": \"obor\",\n \"services\": [\"sluzba1\", \"sluzba2\"],\n \"city\": \"mesto\",\n \"country\": \"CZ/SK/DE/AT/...\",\n \"size_estimate\": \"micro/small/medium/large\",\n \"contacts\": [{\"name\": \"Jmeno\", \"role\": \"pozice\", \"email\": \"email\", \"phone\": \"tel\"}],\n \"social\": {\"linkedin\": \"\", \"facebook\": \"\"},\n \"web_quality\": \"modern/outdated/basic\",\n \"notes\": \"dalsi poznamky\"\n}`;\n\nreturn [{\n json: {\n ...data,\n aiRequestBody: JSON.stringify({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 1024,\n messages: [{ role: 'user', content: prompt }]\n })\n }\n}];"
60
+ }
61
+ },
62
+ {
63
+ "id": "w2-ai",
64
+ "name": "AI Analyze Company",
65
+ "type": "n8n-nodes-base.httpRequest",
66
+ "typeVersion": 4.2,
67
+ "position": [1250, 300],
68
+ "parameters": {
69
+ "method": "POST",
70
+ "url": "https://api.anthropic.com/v1/messages",
71
+ "authentication": "predefinedCredentialType",
72
+ "nodeCredentialType": "anthropicApi",
73
+ "sendBody": true,
74
+ "specifyBody": "json",
75
+ "jsonBody": "={{ $json.aiRequestBody }}",
76
+ "sendHeaders": true,
77
+ "headerParameters": {
78
+ "parameters": [
79
+ {
80
+ "name": "anthropic-version",
81
+ "value": "2023-06-01"
82
+ }
83
+ ]
84
+ },
85
+ "options": {
86
+ "timeout": 30000
87
+ }
88
+ },
89
+ "credentials": {
90
+ "anthropicApi": {
91
+ "id": "YOUR_ANTHROPIC_CREDENTIAL_ID",
92
+ "name": "Anthropic API"
93
+ }
94
+ }
95
+ },
96
+ {
97
+ "id": "w2-parse-ai",
98
+ "name": "Parse AI Response",
99
+ "type": "n8n-nodes-base.code",
100
+ "typeVersion": 2,
101
+ "position": [1450, 300],
102
+ "parameters": {
103
+ "jsCode": "const aiResponse = $input.first().json;\nconst aiText = aiResponse.content?.[0]?.text || '';\nconst input = $('Parse Input').first().json;\nlet analysis;\ntry {\n const jsonMatch = aiText.match(/\\{[\\s\\S]*\\}/);\n analysis = JSON.parse(jsonMatch[0]);\n} catch(e) {\n analysis = { company_name: input.companyName, description: '', industry: '', services: [], city: '', country: '', size_estimate: '', contacts: [], social: {}, web_quality: '', notes: '' };\n}\nconst isCzech = (analysis.country || '').toUpperCase() === 'CZ' || (input.website || '').endsWith('.cz');\nreturn [{ json: { ...analysis, website: input.website, slug: input.slug, isCzech } }];"
104
+ }
105
+ },
106
+ {
107
+ "id": "w2-if-czech",
108
+ "name": "Czech Company?",
109
+ "type": "n8n-nodes-base.if",
110
+ "typeVersion": 2.2,
111
+ "position": [1650, 300],
112
+ "parameters": {
113
+ "conditions": {
114
+ "options": {
115
+ "version": 2
116
+ },
117
+ "combinator": "and",
118
+ "conditions": [
119
+ {
120
+ "leftValue": "={{ $json.isCzech }}",
121
+ "rightValue": true,
122
+ "operator": {
123
+ "type": "boolean",
124
+ "operation": "equals"
125
+ }
126
+ }
127
+ ]
128
+ }
129
+ }
130
+ },
131
+ {
132
+ "id": "w2-ares",
133
+ "name": "ARES Lookup",
134
+ "type": "n8n-nodes-base.httpRequest",
135
+ "typeVersion": 4.2,
136
+ "position": [1850, 200],
137
+ "parameters": {
138
+ "method": "GET",
139
+ "url": "=https://ares.gov.cz/ekonomicke-subjekty-v-be/rest/ekonomicke-subjekty/vyhledat?obchodniJmeno={{ encodeURIComponent($json.company_name) }}",
140
+ "options": {
141
+ "timeout": 10000
142
+ }
143
+ },
144
+ "continueOnFail": true
145
+ },
146
+ {
147
+ "id": "w2-merge",
148
+ "name": "Merge ARES Data",
149
+ "type": "n8n-nodes-base.code",
150
+ "typeVersion": 2,
151
+ "position": [2050, 200],
152
+ "parameters": {
153
+ "jsCode": "const prevData = $('Parse AI Response').first().json;\nconst ares = $input.first().json;\nlet aresData = {};\ntry {\n const subjects = ares.ekonomickeSubjekty || [];\n if (subjects.length > 0) {\n const s = subjects[0];\n aresData = {\n ico: s.ico || '',\n dic: s.dic || '',\n legal_form: (s.pravniForma && s.pravniForma.nazev) || '',\n founded: s.datumVzniku || '',\n employee_count: s.pocetZamestnancu || ''\n };\n }\n} catch(e) {}\nreturn [{ json: { ...prevData, ares: aresData } }];"
154
+ }
155
+ },
156
+ {
157
+ "id": "w2-build",
158
+ "name": "Build CRM Output",
159
+ "type": "n8n-nodes-base.code",
160
+ "typeVersion": 2,
161
+ "position": [2250, 300],
162
+ "parameters": {
163
+ "jsCode": "const data = $input.first().json;\nconst ares = data.ares || {};\nconst today = new Date().toISOString().split('T')[0];\n\nlet score = 0;\nif (data.website) score += 5;\nif (data.contacts && data.contacts.length > 0) score += 10;\nif (data.contacts && data.contacts.some(c => c.email)) score += 10;\nif (ares.ico) score += 10;\nif (data.industry) score += 5;\nif (data.city) score += 5;\nif (data.web_quality === 'outdated' || data.web_quality === 'basic') score += 10;\n\nconst slug = data.slug || data.company_name.toLowerCase()\n .replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').substring(0, 50);\n\nconst contactsMd = (data.contacts || []).map(c =>\n '- **' + c.name + '** (' + (c.role || 'N/A') + ') — ' + (c.email || '') + ' ' + (c.phone || '')\n).join('\\n');\nconst servicesMd = (data.services || []).map(s => '- ' + s).join('\\n');\n\nconst lines = [\n '---',\n \"title: '\" + (data.company_name || '').replace(/'/g, \"''\") + \"'\",\n \"created: '\" + today + \"'\",\n 'tags: [client]',\n 'type: client',\n \"status: 'research'\",\n \"city: '\" + (data.city || '') + \"'\",\n \"size: '\" + (data.size_estimate || '') + \"'\",\n \"industry: '\" + (data.industry || '') + \"'\",\n \"source: 'research'\",\n \"email: '\" + ((data.contacts?.[0]?.email) || '') + \"'\",\n \"ico: '\" + (ares.ico || '') + \"'\",\n \"web: '\" + (data.website || '') + \"'\",\n 'score: ' + score,\n '---',\n '',\n '## Overview',\n '',\n data.description || '',\n '',\n '## Services',\n '',\n servicesMd || 'N/A',\n '',\n '## Contacts',\n '',\n contactsMd || 'No contacts found.',\n '',\n '## Company Info',\n '',\n '| Field | Value |',\n '|---|---|',\n '| Industry | ' + (data.industry || '') + ' |',\n '| City | ' + (data.city || '') + ' |',\n '| Country | ' + (data.country || '') + ' |',\n '| Size | ' + (data.size_estimate || '') + ' |',\n '| Web Quality | ' + (data.web_quality || '') + ' |'\n];\nif (ares.ico) {\n lines.push('| ICO | ' + ares.ico + ' |');\n if (ares.founded) lines.push('| Founded | ' + ares.founded + ' |');\n if (ares.employee_count) lines.push('| Employees | ' + ares.employee_count + ' |');\n}\nlines.push('', '## Notes', '', data.notes || '');\n\nconst readme = lines.join('\\n');\nconst readmePath = 'clients/' + slug + '/README.md';\n\n// Prepare GitHub API body\nconst githubBody = JSON.stringify({\n message: 'feat(crm): auto-research client ' + slug,\n branch: 'crm', content: Buffer.from(readme).toString('base64')\n});\n\n// Prepare ntfy body\nconst ntfyBody = 'Research done: ' + data.company_name + ' (score: ' + score + ')';\n\nreturn [{\n json: { slug, score, companyName: data.company_name, readmePath, githubBody, ntfyBody }\n}];"
164
+ }
165
+ },
166
+ {
167
+ "id": "w2-github",
168
+ "name": "Save to GitHub",
169
+ "type": "n8n-nodes-base.httpRequest",
170
+ "typeVersion": 4.2,
171
+ "position": [2450, 300],
172
+ "parameters": {
173
+ "method": "PUT",
174
+ "url": "=https://api.github.com/repos/your-username/axis-crm/contents/{{ $json.readmePath }}",
175
+ "authentication": "predefinedCredentialType",
176
+ "nodeCredentialType": "githubApi",
177
+ "sendBody": true,
178
+ "specifyBody": "json",
179
+ "jsonBody": "={{ $json.githubBody }}",
180
+ "sendHeaders": true,
181
+ "headerParameters": {
182
+ "parameters": [
183
+ {
184
+ "name": "Accept",
185
+ "value": "application/vnd.github.v3+json"
186
+ }
187
+ ]
188
+ },
189
+ "options": {
190
+ "timeout": 15000
191
+ }
192
+ },
193
+ "credentials": {
194
+ "githubApi": {
195
+ "id": "YOUR_GITHUB_CREDENTIAL_ID",
196
+ "name": "GitHub API"
197
+ }
198
+ }
199
+ },
200
+ {
201
+ "id": "w2-notify",
202
+ "name": "Notify",
203
+ "type": "n8n-nodes-base.httpRequest",
204
+ "typeVersion": 4.2,
205
+ "position": [2650, 300],
206
+ "parameters": {
207
+ "method": "POST",
208
+ "url": "https://ntfy.sh/your-topic",
209
+ "sendBody": true,
210
+ "specifyBody": "string",
211
+ "body": "={{ $json.ntfyBody }}",
212
+ "sendHeaders": true,
213
+ "headerParameters": {
214
+ "parameters": [
215
+ {
216
+ "name": "Title",
217
+ "value": "Axis CRM: Research complete"
218
+ },
219
+ {
220
+ "name": "Priority",
221
+ "value": "3"
222
+ },
223
+ {
224
+ "name": "Tags",
225
+ "value": "mag"
226
+ }
227
+ ]
228
+ },
229
+ "options": {}
230
+ }
231
+ }
232
+ ],
233
+ "connections": {
234
+ "Research Webhook": {
235
+ "main": [
236
+ [
237
+ {
238
+ "node": "Parse Input",
239
+ "type": "main",
240
+ "index": 0
241
+ }
242
+ ]
243
+ ]
244
+ },
245
+ "Parse Input": {
246
+ "main": [
247
+ [
248
+ {
249
+ "node": "Scrape Website",
250
+ "type": "main",
251
+ "index": 0
252
+ }
253
+ ]
254
+ ]
255
+ },
256
+ "Scrape Website": {
257
+ "main": [
258
+ [
259
+ {
260
+ "node": "Extract Text & Contacts",
261
+ "type": "main",
262
+ "index": 0
263
+ }
264
+ ]
265
+ ]
266
+ },
267
+ "Extract Text & Contacts": {
268
+ "main": [
269
+ [
270
+ {
271
+ "node": "Prepare AI Request",
272
+ "type": "main",
273
+ "index": 0
274
+ }
275
+ ]
276
+ ]
277
+ },
278
+ "Prepare AI Request": {
279
+ "main": [
280
+ [
281
+ {
282
+ "node": "AI Analyze Company",
283
+ "type": "main",
284
+ "index": 0
285
+ }
286
+ ]
287
+ ]
288
+ },
289
+ "AI Analyze Company": {
290
+ "main": [
291
+ [
292
+ {
293
+ "node": "Parse AI Response",
294
+ "type": "main",
295
+ "index": 0
296
+ }
297
+ ]
298
+ ]
299
+ },
300
+ "Parse AI Response": {
301
+ "main": [
302
+ [
303
+ {
304
+ "node": "Czech Company?",
305
+ "type": "main",
306
+ "index": 0
307
+ }
308
+ ]
309
+ ]
310
+ },
311
+ "Czech Company?": {
312
+ "main": [
313
+ [
314
+ {
315
+ "node": "ARES Lookup",
316
+ "type": "main",
317
+ "index": 0
318
+ }
319
+ ],
320
+ [
321
+ {
322
+ "node": "Build CRM Output",
323
+ "type": "main",
324
+ "index": 0
325
+ }
326
+ ]
327
+ ]
328
+ },
329
+ "ARES Lookup": {
330
+ "main": [
331
+ [
332
+ {
333
+ "node": "Merge ARES Data",
334
+ "type": "main",
335
+ "index": 0
336
+ }
337
+ ]
338
+ ]
339
+ },
340
+ "Merge ARES Data": {
341
+ "main": [
342
+ [
343
+ {
344
+ "node": "Build CRM Output",
345
+ "type": "main",
346
+ "index": 0
347
+ }
348
+ ]
349
+ ]
350
+ },
351
+ "Build CRM Output": {
352
+ "main": [
353
+ [
354
+ {
355
+ "node": "Save to GitHub",
356
+ "type": "main",
357
+ "index": 0
358
+ }
359
+ ]
360
+ ]
361
+ },
362
+ "Save to GitHub": {
363
+ "main": [
364
+ [
365
+ {
366
+ "node": "Notify",
367
+ "type": "main",
368
+ "index": 0
369
+ }
370
+ ]
371
+ ]
372
+ }
373
+ },
374
+ "settings": {
375
+ "executionOrder": "v1",
376
+ "saveExecutionProgress": true,
377
+ "saveManualExecutions": true,
378
+ "timezone": "Europe/Prague",
379
+ "callerPolicy": "workflowsFromSameOwner",
380
+ "availableInMCP": false,
381
+ "binaryMode": "separate"
382
+ }
383
+ }