axis-crm 1.0.1 → 1.1.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.
package/README.md CHANGED
@@ -52,7 +52,7 @@ clients/acme-consulting/
52
52
  projects/
53
53
  web-redesign/
54
54
  README.md # scope, timeline, status, value
55
- zadani.md # client brief
55
+ brief.md # client brief
56
56
  deliverables/
57
57
  notes/
58
58
  communication/
package/cli/index.js CHANGED
@@ -95,12 +95,14 @@ countFiles(targetDir);
95
95
  console.log(` Done! ${fileCount} files created.\n`);
96
96
  console.log(" Next steps:\n");
97
97
  console.log(` cd ${targetName}`);
98
- console.log(" git init");
99
- console.log(' git add -A && git commit -m "init: axis-crm"');
100
- console.log(' ./scripts/new-lead.sh "First Lead Inc"');
98
+ console.log(" ./scripts/setup.sh");
99
+ console.log(' ./scripts/new-lead.sh "First Lead Inc" --source manual');
100
+ console.log(" ./scripts/validate.sh");
101
101
  console.log("");
102
+ console.log(
103
+ " Open CLAUDE.md to use with AI coding agents (Claude Code, Cursor).",
104
+ );
102
105
  console.log(" Open in Obsidian for dashboards (install Dataview plugin).");
103
- console.log(" Open CLAUDE.md to use with AI coding agents.");
104
106
  console.log("");
105
107
  console.log(" Docs: https://github.com/PatrikSchick-AI/axis-crm");
106
108
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axis-crm",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "A git-based CRM for freelancers and small teams. Markdown + YAML + Obsidian + n8n.",
5
5
  "bin": {
6
6
  "axis-crm": "./cli/index.js"
@@ -1,38 +1,172 @@
1
- # CLAUDE.md
1
+ # Axis CRM — AI Agent Instructions
2
2
 
3
- Axis CRM — git-based CRM system. Markdown files with YAML frontmatter, versioned in git.
3
+ > Git-based CRM for freelancers and small teams.
4
+ > You (the AI agent) are the primary interface. Users send you context and you manage the CRM.
4
5
 
5
- ## Structure
6
+ ## Quick Reference
6
7
 
7
- - `clients/` each client = a folder (README.md, contacts/, meetings/, projects/, communication/, files/)
8
- - `leads/` — each lead = a single .md file
9
- - `templates/` — templates for all record types
10
- - `rules/` — lifecycle, scoring, field definitions, folder structure
11
- - `scripts/` — new-lead.sh, convert-to-client.sh, new-project.sh, validate.sh
12
- - `automations/` — n8n workflow JSON exports
13
- - `dashboards/` — Obsidian Dataview dashboards
8
+ All scripts are in `scripts/`. They are non-interactive (no prompts, all input via flags).
14
9
 
15
- ## Conventions
10
+ ### Create lead
11
+ ```bash
12
+ ./scripts/new-lead.sh "Company Name" \
13
+ --source referral \
14
+ --city Prague \
15
+ --industry consulting \
16
+ --email info@example.com \
17
+ --phone "+420123456789" \
18
+ --web https://example.com
19
+ ```
20
+ All flags are optional. Creates `leads/company-name.md`.
21
+
22
+ ### Convert lead to client
23
+ ```bash
24
+ ./scripts/convert-to-client.sh company-name
25
+ ```
26
+ Reads lead file, creates full client folder structure, deletes lead (history in git).
27
+
28
+ ### Create project for client
29
+ ```bash
30
+ ./scripts/new-project.sh client-slug "Project Name"
31
+ ```
32
+ Creates `clients/client-slug/projects/project-name/` with README.md + brief.md.
33
+
34
+ ### Validate data
35
+ ```bash
36
+ ./scripts/validate.sh # check all
37
+ ./scripts/validate.sh leads # leads only
38
+ ./scripts/validate.sh clients # clients only
39
+ ./scripts/validate.sh --fix # show actionable fix commands
40
+ ```
41
+
42
+ ### Setup check
43
+ ```bash
44
+ ./scripts/setup.sh # verify environment
45
+ ```
46
+
47
+ ## Common Operations
48
+
49
+ ### Add a contact to a client
50
+ Create a markdown file in `clients/{slug}/contacts/`:
51
+ ```yaml
52
+ ---
53
+ title: "Jane Smith"
54
+ created: "2026-04-14"
55
+ tags: [contact]
56
+ type: contact
57
+ role: "CEO"
58
+ email: "jane@example.com"
59
+ phone: "+420123456789"
60
+ linkedin: ""
61
+ last_contact: ""
62
+ notes: ""
63
+ ---
64
+
65
+ ## Notes
66
+
67
+ Key decision maker. Prefers email communication.
68
+ ```
69
+
70
+ ### Add a meeting note
71
+ Create a file in `clients/{slug}/meetings/`:
72
+ ```yaml
73
+ ---
74
+ title: "Kickoff meeting"
75
+ created: "2026-04-14"
76
+ tags: [meeting]
77
+ type: meeting
78
+ attendees: [Jane Smith, John Doe]
79
+ ---
80
+
81
+ ## Agenda
82
+
83
+ 1. Project scope review
84
+ 2. Timeline discussion
85
+
86
+ ## Notes
87
+
88
+ - Agreed on 3-month timeline
89
+ - Budget confirmed at 50k CZK/month
16
90
 
17
- - Code, commits, branches: English
18
- - Commit messages: `feat:`, `fix:`, `docs:`, `chore:`
19
- - YAML frontmatter on every .md file
20
- - Never use emoji as icons use SVG icons (Lucide, Heroicons)
91
+ ## Action Items
92
+
93
+ - [ ] Send proposal by Friday
94
+ - [ ] Schedule follow-up for next week
95
+ ```
96
+
97
+ ### Add a communication entry
98
+ Create a file in `clients/{slug}/communication/`:
99
+ ```yaml
100
+ ---
101
+ title: "Price agreed"
102
+ created: "2026-04-14"
103
+ tags: [communication, decision]
104
+ type: communication
105
+ channel: email
106
+ ---
107
+
108
+ ## Summary
109
+
110
+ Agreed on hourly rate of 2000 CZK. Starting next Monday.
111
+ ```
112
+
113
+ ### Update client rates
114
+ Edit `clients/{slug}/README.md` frontmatter:
115
+ ```yaml
116
+ hourly_rate: 2000
117
+ manday_rate: 15000
118
+ currency: "CZK"
119
+ ```
120
+
121
+ ### Score a lead
122
+ Review `rules/scoring.md` for criteria, then update `leads/{slug}.md`:
123
+ ```yaml
124
+ score: 75
125
+ ```
126
+ Thresholds: 0-39 = low, 40-69 = medium, 70-100 = high priority.
127
+
128
+ ## File Structure
129
+
130
+ ```
131
+ clients/ — each client = a folder
132
+ {slug}/
133
+ README.md — overview, rates, status, frontmatter
134
+ contacts/ — key people (.md per person)
135
+ meetings/ — meeting notes (.md per meeting)
136
+ projects/ — project folders (README.md + brief.md)
137
+ communication/ — key decisions, emails (.md per entry)
138
+ files/ — contracts/, proposals/, invoices/
139
+
140
+ leads/ — each lead = a single .md file
141
+ {slug}.md — frontmatter with all lead data
142
+
143
+ templates/ — templates for all record types
144
+ rules/ — lifecycle, scoring, field definitions
145
+ scripts/ — new-lead.sh, convert-to-client.sh, new-project.sh, validate.sh, setup.sh
146
+ automations/ — n8n workflow JSON exports
147
+ dashboards/ — Obsidian Dataview dashboards
148
+ ```
149
+
150
+ ## Lead Lifecycle
151
+
152
+ ```
153
+ new -> enriched -> qualified -> contacted -> responded -> opportunity -> client
154
+ ```
155
+
156
+ Each stage has required fields. Run `./scripts/validate.sh` to check.
21
157
 
22
158
  ## Rules
23
159
 
24
160
  - A lead is a file. A client is a folder.
25
- - Convert lead to client: `scripts/convert-to-client.sh slug`
26
- - Create new lead: `scripts/new-lead.sh "Company Name"`
27
- - Validate data: `scripts/validate.sh`
28
- - Lead lifecycle: new enriched qualified → contacted → responded → opportunity → client
29
- - Scoring: 0-100, thresholds in `rules/scoring.md`
30
- - Client rates: `hourly_rate`, `manday_rate`, `currency` fields in client README.md
161
+ - All scripts are non-interactive — designed for AI agent usage
162
+ - All flags have argument guards — missing value = clear error message
163
+ - Slug = lowercase, hyphens, ASCII only (diacritics stripped)
164
+ - Never commit .env, API keys, or credentials
165
+ - Never use emoji as icons in any output
31
166
 
32
- ## Build & Check
167
+ ## Integration with Axis Tasks
33
168
 
34
- ```bash
35
- ./scripts/validate.sh # Validate all leads + clients
36
- ./scripts/validate.sh leads # Leads only
37
- ./scripts/validate.sh clients # Clients only
38
- ```
169
+ If [Axis Tasks](https://github.com/PatrikSchick-AI/Axis-Tasks) is installed as a sibling directory (`../tasks/`):
170
+ - Meeting action items can be converted to tasks
171
+ - Client slug from CRM is used as `--client` in task creation
172
+ - Time reports can be filtered per client
@@ -56,7 +56,7 @@
56
56
  "typeVersion": 2,
57
57
  "position": [1050, 300],
58
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}];"
59
+ "jsCode": "const data = $input.first().json;\n\nconst prompt = `Analyze the company and create a structured profile.\n\nCompany: ${data.companyName}\nWebsite: ${data.website}\nEmails found on website: ${data.extractedEmails.join(', ')}\nPhones found on website: ${data.extractedPhones.join(', ')}\n\nWebsite content (5000 chars):\n${data.textContent.substring(0, 5000)}\n\nRespond with ONLY valid JSON:\n{\n \"company_name\": \"Official name\",\n \"description\": \"1-2 sentences about what the company does\",\n \"industry\": \"industry\",\n \"services\": [\"service1\", \"service2\"],\n \"city\": \"city\",\n \"country\": \"CZ/SK/DE/AT/...\",\n \"size_estimate\": \"micro/small/medium/large\",\n \"contacts\": [{\"name\": \"Name\", \"role\": \"position\", \"email\": \"email\", \"phone\": \"phone\"}],\n \"social\": {\"linkedin\": \"\", \"facebook\": \"\"},\n \"web_quality\": \"modern/outdated/basic\",\n \"notes\": \"additional notes\"\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
60
  }
61
61
  },
62
62
  {
@@ -47,7 +47,7 @@
47
47
  "typeVersion": 2,
48
48
  "position": [850, 300],
49
49
  "parameters": {
50
- "jsCode": "const data = $input.first().json;\n\nconst prompt = `Analyzuj tento meeting a urcni ke kteremu klientovi patri.\n\nMeeting: ${data.title}\nUcastnici: ${data.participants.join(', ')}\nSummary: ${data.summary}\nTranscript (prvnich 2000 znaku):\n${data.transcript.substring(0, 2000)}\n\nOdpovez POUZE validnim JSON (bez markdown code blocku):\n{\n \"client_slug\": \"nazev-firmy-lowercase-pomlcky-bez-sro\",\n \"client_name\": \"Oficialni nazev firmy\",\n \"is_new\": true,\n \"attendees\": [\"jmeno1\", \"jmeno2\"],\n \"summary\": \"2-3 vety co se resilo\",\n \"action_items\": [\"ukol1\", \"ukol2\"],\n \"topics\": [\"tema1\", \"tema2\"]\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}];"
50
+ "jsCode": "const data = $input.first().json;\n\nconst prompt = `Analyze this meeting and identify which client it belongs to.\n\nMeeting: ${data.title}\nParticipants: ${data.participants.join(', ')}\nSummary: ${data.summary}\nTranscript (first 2000 chars):\n${data.transcript.substring(0, 2000)}\n\nRespond with ONLY valid JSON (no markdown code blocks):\n{\n \"client_slug\": \"company-name-lowercase-dashes-no-legal-suffix\",\n \"client_name\": \"Official company name\",\n \"is_new\": true,\n \"attendees\": [\"name1\", \"name2\"],\n \"summary\": \"2-3 sentences about what was discussed\",\n \"action_items\": [\"task1\", \"task2\"],\n \"topics\": [\"topic1\", \"topic2\"]\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}];"
51
51
  }
52
52
  },
53
53
  {
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: 'Zadani — Web Redesign'
2
+ title: 'Brief — Web Redesign'
3
3
  created: '2026-02-10'
4
4
  type: brief
5
5
  ---
@@ -13,7 +13,7 @@ clients/company-name/
13
13
  contacts/ # people from the company
14
14
  jan-novak.md # contact person
15
15
  communication/ # key decisions and communication
16
- 2026-04-10-cena-dohodnuta.md
16
+ 2026-04-10-price-agreed.md
17
17
  files/ # documents
18
18
  contracts/ # signed contracts, NDAs, amendments
19
19
  proposals/ # price quotes, proposals
@@ -23,7 +23,7 @@ clients/company-name/
23
23
  projects/ # projects for the client
24
24
  project-name/ # each project = folder
25
25
  README.md # scope, status, timeline
26
- zadani.md # brief from the client
26
+ brief.md # brief from the client
27
27
  deliverables/ # outputs
28
28
  notes/ # ongoing notes
29
29
  ```
@@ -43,7 +43,7 @@ Important decisions and communication. NOT every email — only what changes dir
43
43
 
44
44
  - Filename format: `YYYY-MM-DD-description.md`
45
45
  - Frontmatter: date, channel (email/call/chat), participants, decision
46
- - Example: `2026-04-10-cena-dohodnuta.md` — client approved the quote
46
+ - Example: `2026-04-10-price-agreed.md` — client approved the quote
47
47
 
48
48
  ### files/
49
49
 
@@ -69,7 +69,7 @@ Meeting notes.
69
69
  Each project = its own folder.
70
70
 
71
71
  - `README.md` — scope, status, timeline, deliverables
72
- - `zadani.md` — original brief from the client
72
+ - `brief.md` — original brief from the client
73
73
  - `deliverables/` — final outputs
74
74
  - `notes/` — ongoing notes, drafts
75
75
 
@@ -70,9 +70,9 @@ mkdir -p "$CLIENT_DIR/contacts"
70
70
  mkdir -p "$CLIENT_DIR/projects"
71
71
  mkdir -p "$CLIENT_DIR/meetings"
72
72
  mkdir -p "$CLIENT_DIR/communication"
73
- mkdir -p "$CLIENT_DIR/files/smlouvy"
74
- mkdir -p "$CLIENT_DIR/files/nabidky"
75
- mkdir -p "$CLIENT_DIR/files/faktury"
73
+ mkdir -p "$CLIENT_DIR/files/contracts"
74
+ mkdir -p "$CLIENT_DIR/files/proposals"
75
+ mkdir -p "$CLIENT_DIR/files/invoices"
76
76
 
77
77
  # Create README.md from template with lead data
78
78
  sed -e "s|^title: ''|title: \"$TITLE\"|" \
@@ -4,15 +4,24 @@ set -euo pipefail
4
4
  # new-lead.sh — Create a new lead from template
5
5
  #
6
6
  # Usage:
7
- # ./new-lead.sh "Firma s.r.o." --source firmy-cz --city Plzen --industry ucetnictvi
8
- # ./new-lead.sh "Firma s.r.o." (interactive prompts for fields)
7
+ # ./new-lead.sh "Company Name" --source firmy-cz --city Prague --industry accounting
8
+ # ./new-lead.sh "Company Name" --email info@example.com --web https://example.com
9
+ # ./new-lead.sh "Company Name" (creates with defaults, fill in later)
9
10
 
10
11
  CRM_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
12
  TEMPLATE="$CRM_DIR/templates/lead.md"
12
13
  LEADS_DIR="$CRM_DIR/leads"
13
14
 
14
15
  if [ $# -lt 1 ]; then
15
- echo "Usage: new-lead.sh \"Firma Name\" [--source X] [--city X] [--industry X] [--email X] [--phone X] [--web X]"
16
+ echo "Usage: new-lead.sh \"Company Name\" [flags]"
17
+ echo ""
18
+ echo "Flags:"
19
+ echo " --source Lead source (e.g. firmy-cz, apify, referral, manual)"
20
+ echo " --city City"
21
+ echo " --industry Industry"
22
+ echo " --email Email"
23
+ echo " --phone Phone"
24
+ echo " --web Website URL"
16
25
  exit 1
17
26
  fi
18
27
 
@@ -24,35 +33,36 @@ SLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed
24
33
  TODAY=$(date +%Y-%m-%d)
25
34
  OUTPUT="$LEADS_DIR/$SLUG.md"
26
35
 
36
+ if [ -z "$SLUG" ]; then
37
+ echo "Error: Cannot generate slug from title '$TITLE'"
38
+ exit 1
39
+ fi
40
+
27
41
  if [ -f "$OUTPUT" ]; then
28
42
  echo "Error: Lead already exists: $OUTPUT"
29
43
  exit 1
30
44
  fi
31
45
 
32
- # Parse optional flags
46
+ # Parse optional flags (with argument guards)
33
47
  SOURCE="" CITY="" INDUSTRY="" EMAIL="" PHONE="" WEB=""
34
48
  while [ $# -gt 0 ]; do
35
49
  case "$1" in
36
- --source) SOURCE="$2"; shift 2 ;;
37
- --city) CITY="$2"; shift 2 ;;
38
- --industry) INDUSTRY="$2"; shift 2 ;;
39
- --email) EMAIL="$2"; shift 2 ;;
40
- --phone) PHONE="$2"; shift 2 ;;
41
- --web) WEB="$2"; shift 2 ;;
50
+ --source) [ $# -ge 2 ] || { echo "Error: --source requires a value"; exit 1; }
51
+ SOURCE="$2"; shift 2 ;;
52
+ --city) [ $# -ge 2 ] || { echo "Error: --city requires a value"; exit 1; }
53
+ CITY="$2"; shift 2 ;;
54
+ --industry) [ $# -ge 2 ] || { echo "Error: --industry requires a value"; exit 1; }
55
+ INDUSTRY="$2"; shift 2 ;;
56
+ --email) [ $# -ge 2 ] || { echo "Error: --email requires a value"; exit 1; }
57
+ EMAIL="$2"; shift 2 ;;
58
+ --phone) [ $# -ge 2 ] || { echo "Error: --phone requires a value"; exit 1; }
59
+ PHONE="$2"; shift 2 ;;
60
+ --web) [ $# -ge 2 ] || { echo "Error: --web requires a value"; exit 1; }
61
+ WEB="$2"; shift 2 ;;
42
62
  *) echo "Unknown flag: $1"; exit 1 ;;
43
63
  esac
44
64
  done
45
65
 
46
- # If no flags provided, prompt interactively
47
- if [ -z "$SOURCE" ] && [ -t 0 ]; then
48
- read -p "Source (e.g. firmy-cz, apify, manual): " SOURCE
49
- read -p "City: " CITY
50
- read -p "Industry: " INDUSTRY
51
- read -p "Email: " EMAIL
52
- read -p "Phone: " PHONE
53
- read -p "Web: " WEB
54
- fi
55
-
56
66
  # Create lead from template
57
67
  sed -e "s|^title: ''|title: \"$TITLE\"|" \
58
68
  -e "s|^created: ''|created: \"$TODAY\"|" \
@@ -65,4 +75,5 @@ sed -e "s|^title: ''|title: \"$TITLE\"|" \
65
75
  "$TEMPLATE" > "$OUTPUT"
66
76
 
67
77
  echo "Created: $OUTPUT"
68
- echo "Status: new (next step: enrich with ARES data)"
78
+ echo "Status: new | Source: ${SOURCE:-none}"
79
+ exit 0
@@ -4,18 +4,21 @@ set -uo pipefail
4
4
  # new-project.sh — Create a new project folder for a client
5
5
  #
6
6
  # Usage:
7
- # ./new-project.sh client-slug project-name
7
+ # ./new-project.sh client-slug "Project Name"
8
8
  # ./new-project.sh acme-consulting "Web Redesign"
9
9
 
10
10
  CRM_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
11
  CLIENTS_DIR="$CRM_DIR/clients"
12
12
  TEMPLATE_DIR="$CRM_DIR/templates/project"
13
13
 
14
+ # Portable sed -i
15
+ _sed_i() { if [[ "$OSTYPE" == "darwin"* ]]; then sed -i '' "$@"; else sed -i "$@"; fi; }
16
+
14
17
  if [ $# -lt 2 ]; then
15
18
  echo "Usage: new-project.sh <client-slug> \"Project Name\""
16
19
  echo ""
17
20
  echo "Available clients:"
18
- ls "$CLIENTS_DIR"
21
+ ls "$CLIENTS_DIR" 2>/dev/null || echo " (none)"
19
22
  exit 1
20
23
  fi
21
24
 
@@ -27,7 +30,7 @@ if [ ! -d "$CLIENT_DIR" ]; then
27
30
  echo "Error: Client not found: $CLIENT_DIR"
28
31
  echo ""
29
32
  echo "Available clients:"
30
- ls "$CLIENTS_DIR"
33
+ ls "$CLIENTS_DIR" 2>/dev/null || echo " (none)"
31
34
  exit 1
32
35
  fi
33
36
 
@@ -36,6 +39,11 @@ PROJECT_SLUG=$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0
36
39
  PROJECT_DIR="$CLIENT_DIR/projects/$PROJECT_SLUG"
37
40
  TODAY=$(date +%Y-%m-%d)
38
41
 
42
+ if [ -z "$PROJECT_SLUG" ]; then
43
+ echo "Error: Cannot generate slug from project name '$PROJECT_NAME'"
44
+ exit 1
45
+ fi
46
+
39
47
  if [ -d "$PROJECT_DIR" ]; then
40
48
  echo "Error: Project already exists: $PROJECT_DIR"
41
49
  exit 1
@@ -45,25 +53,22 @@ fi
45
53
  cp -r "$TEMPLATE_DIR" "$PROJECT_DIR"
46
54
 
47
55
  # Fill in README.md
48
- sed -i '' \
56
+ _sed_i \
49
57
  -e "s|^title: ''|title: '$PROJECT_NAME'|" \
50
58
  -e "s|^created: ''|created: '$TODAY'|" \
51
59
  -e "s|^start: ''|start: '$TODAY'|" \
52
60
  "$PROJECT_DIR/README.md"
53
61
 
54
- # Fill in zadani.md
55
- sed -i '' \
56
- -e "s|^title: 'Zadani — '|title: 'Zadani — $PROJECT_NAME'|" \
62
+ # Fill in brief.md
63
+ _sed_i \
64
+ -e "s|^title: 'Brief — '|title: 'Brief — $PROJECT_NAME'|" \
57
65
  -e "s|^created: ''|created: '$TODAY'|" \
58
- "$PROJECT_DIR/zadani.md"
66
+ "$PROJECT_DIR/brief.md"
59
67
 
60
68
  echo "Project created: $PROJECT_DIR/"
61
69
  echo ""
62
70
  echo " README.md — scope, status, timeline"
63
- echo " zadani.md — brief od klienta"
64
- echo " deliverables/ — vystupy"
65
- echo " notes/ — prubezne poznamky"
66
- echo ""
67
- echo "Next steps:"
68
- echo " 1. Vyplnit zadani.md"
69
- echo " 2. Doplnit scope a timeline v README.md"
71
+ echo " brief.md client brief"
72
+ echo " deliverables/ — outputs"
73
+ echo " notes/ — ongoing notes"
74
+ exit 0
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # setup.sh — Verify environment and initialize Axis CRM
5
+ #
6
+ # Usage:
7
+ # ./setup.sh # check + init
8
+ # ./setup.sh --check # check only, no changes
9
+
10
+ CRM_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
+ CHECK_ONLY=false
12
+ [ "${1:-}" = "--check" ] && CHECK_ONLY=true
13
+
14
+ PASS=0
15
+ WARN=0
16
+ FAIL=0
17
+
18
+ ok() { echo " [OK] $1"; PASS=$((PASS + 1)); }
19
+ warn() { echo " [WARN] $1"; WARN=$((WARN + 1)); }
20
+ fail() { echo " [FAIL] $1"; FAIL=$((FAIL + 1)); }
21
+
22
+ echo ""
23
+ echo "=== Axis CRM — Setup Check ==="
24
+ echo ""
25
+
26
+ # --- Required ---
27
+
28
+ echo "Required:"
29
+
30
+ if command -v bash &>/dev/null; then
31
+ ok "bash $(bash --version | head -1 | sed 's/.*version //' | sed 's/ .*//')"
32
+ else
33
+ fail "bash not found"
34
+ fi
35
+
36
+ if command -v git &>/dev/null; then
37
+ ok "git $(git --version | sed 's/git version //')"
38
+ else
39
+ fail "git not found"
40
+ fi
41
+
42
+ echo ""
43
+ echo "Optional:"
44
+
45
+ # Axis Tasks integration
46
+ TASKS_DIR=""
47
+ for candidate in "$CRM_DIR/../tasks" "$CRM_DIR/../10-tasks"; do
48
+ if [ -d "$candidate/scripts" ]; then
49
+ TASKS_DIR="$(cd "$candidate" && pwd)"
50
+ break
51
+ fi
52
+ done
53
+
54
+ if [ -n "$TASKS_DIR" ]; then
55
+ ok "Axis Tasks found ($TASKS_DIR)"
56
+ echo " Task creation from meetings enabled"
57
+ else
58
+ warn "Axis Tasks not found — task integration disabled"
59
+ echo " Install: https://github.com/PatrikSchick-AI/Axis-Tasks"
60
+ fi
61
+
62
+ echo ""
63
+ echo "Directories:"
64
+
65
+ DIRS="clients leads templates scripts rules dashboards automations"
66
+ for dir in $DIRS; do
67
+ if [ -d "$CRM_DIR/$dir" ]; then
68
+ ok "$dir/"
69
+ else
70
+ if [ "$CHECK_ONLY" = true ]; then
71
+ fail "$dir/ missing"
72
+ else
73
+ mkdir -p "$CRM_DIR/$dir"
74
+ ok "$dir/ (created)"
75
+ fi
76
+ fi
77
+ done
78
+
79
+ echo ""
80
+ echo "Scripts:"
81
+
82
+ for script in new-lead.sh convert-to-client.sh new-project.sh validate.sh; do
83
+ SCRIPT_PATH="$CRM_DIR/scripts/$script"
84
+ if [ ! -f "$SCRIPT_PATH" ]; then
85
+ fail "$script missing"
86
+ elif [ -x "$SCRIPT_PATH" ]; then
87
+ ok "$script"
88
+ else
89
+ if [ "$CHECK_ONLY" = true ]; then
90
+ warn "$script not executable"
91
+ else
92
+ chmod +x "$SCRIPT_PATH"
93
+ ok "$script (made executable)"
94
+ fi
95
+ fi
96
+ done
97
+
98
+ echo ""
99
+
100
+ TOTAL=$((PASS + WARN + FAIL))
101
+ echo "=== Summary: $PASS passed, $WARN warnings, $FAIL failed (of $TOTAL checks) ==="
102
+
103
+ if [ "$FAIL" -gt 0 ]; then
104
+ echo ""
105
+ echo "Fix the failures above before using Axis CRM."
106
+ exit 1
107
+ fi
108
+
109
+ if [ "$WARN" -gt 0 ]; then
110
+ echo ""
111
+ echo "Axis CRM is ready. Warnings are optional enhancements."
112
+ fi
113
+
114
+ echo ""
115
+ exit 0
@@ -7,7 +7,7 @@ set -uo pipefail
7
7
  # ./validate.sh # Check everything
8
8
  # ./validate.sh leads # Check only leads
9
9
  # ./validate.sh clients # Check only clients
10
- # ./validate.sh --fix # Show what needs fixing (actionable output)
10
+ # ./validate.sh --fix # Show actionable fix commands
11
11
 
12
12
  CRM_DIR="$(cd "$(dirname "$0")/.." && pwd)"
13
13
  LEADS_DIR="$CRM_DIR/leads"
@@ -16,16 +16,27 @@ CLIENTS_DIR="$CRM_DIR/clients"
16
16
  ERRORS=0
17
17
  WARNINGS=0
18
18
  CHECKED=0
19
- MODE="${1:-all}"
19
+ FIX_MODE=false
20
+ MODE="all"
21
+
22
+ # Parse arguments
23
+ for arg in "$@"; do
24
+ case "$arg" in
25
+ --fix) FIX_MODE=true ;;
26
+ leads|clients|all) MODE="$arg" ;;
27
+ *) echo "Unknown argument: $arg"; exit 1 ;;
28
+ esac
29
+ done
20
30
 
21
31
  RED='\033[0;31m'
22
32
  YELLOW='\033[0;33m'
23
33
  GREEN='\033[0;32m'
24
34
  NC='\033[0m'
25
35
 
26
- error() { echo -e " ${RED}ERROR${NC}: $1"; ERRORS=$((ERRORS + 1)); }
27
- warn() { echo -e " ${YELLOW}WARN${NC}: $1"; WARNINGS=$((WARNINGS + 1)); }
28
- ok() { echo -e " ${GREEN}OK${NC}: $1"; }
36
+ error() { printf " ${RED}ERROR${NC}: %s\n" "$1"; ERRORS=$((ERRORS + 1)); }
37
+ warn() { printf " ${YELLOW}WARN${NC}: %s\n" "$1"; WARNINGS=$((WARNINGS + 1)); }
38
+ ok() { printf " ${GREEN}OK${NC}: %s\n" "$1"; }
39
+ fix() { printf " ${GREEN}FIX${NC}: %s\n" "$1"; }
29
40
 
30
41
  # Extract frontmatter value (handles quoted and unquoted)
31
42
  get_field() {
@@ -51,13 +62,23 @@ check_lead() {
51
62
 
52
63
  # Always required
53
64
  for f in title created source; do
54
- has_field "$file" "$f" || error "$name: missing '$f' (required for all leads)"
65
+ if ! has_field "$file" "$f"; then
66
+ error "$name: missing '$f' (required for all leads)"
67
+ if [ "$FIX_MODE" = true ]; then
68
+ fix "Add '$f' to frontmatter in leads/$name.md"
69
+ fi
70
+ fi
55
71
  done
56
72
 
57
73
  # Enriched+
58
74
  if [[ "$status" =~ ^(enriched|qualified|contacted|responded|opportunity)$ ]]; then
59
75
  for f in city industry ico web email; do
60
- has_field "$file" "$f" || error "$name: missing '$f' (required for status=$status)"
76
+ if ! has_field "$file" "$f"; then
77
+ error "$name: missing '$f' (required for status=$status)"
78
+ if [ "$FIX_MODE" = true ]; then
79
+ fix "Add '$f: \"value\"' to leads/$name.md frontmatter"
80
+ fi
81
+ fi
61
82
  done
62
83
  fi
63
84
 
@@ -67,19 +88,29 @@ check_lead() {
67
88
  score=$(get_field "$file" "score")
68
89
  if [ -z "$score" ] || [ "$score" = "0" ]; then
69
90
  warn "$name: score is 0 (expected > 40 for status=$status)"
91
+ if [ "$FIX_MODE" = true ]; then
92
+ fix "Run scoring: review rules/scoring.md and update 'score:' in leads/$name.md"
93
+ fi
70
94
  fi
71
95
  fi
72
96
 
73
97
  # Contacted+
74
98
  if [[ "$status" =~ ^(contacted|responded|opportunity)$ ]]; then
75
99
  for f in campaign first_contact; do
76
- has_field "$file" "$f" || error "$name: missing '$f' (required for status=$status)"
100
+ if ! has_field "$file" "$f"; then
101
+ error "$name: missing '$f' (required for status=$status)"
102
+ if [ "$FIX_MODE" = true ]; then
103
+ fix "Add '$f: \"value\"' to leads/$name.md frontmatter"
104
+ fi
105
+ fi
77
106
  done
78
107
  fi
79
108
 
80
109
  # Opportunity
81
110
  if [ "$status" = "opportunity" ]; then
82
- has_field "$file" "priority" || warn "$name: missing 'priority' (recommended for opportunity)"
111
+ if ! has_field "$file" "priority"; then
112
+ warn "$name: missing 'priority' (recommended for opportunity)"
113
+ fi
83
114
  fi
84
115
  }
85
116
 
@@ -92,17 +123,27 @@ check_client() {
92
123
 
93
124
  if [ ! -f "$readme" ]; then
94
125
  error "$name: missing README.md"
126
+ if [ "$FIX_MODE" = true ]; then
127
+ fix "cp templates/client/README.md clients/$name/README.md"
128
+ fi
95
129
  return
96
130
  fi
97
131
 
98
132
  # Required fields
99
133
  for f in title status; do
100
- has_field "$readme" "$f" || error "$name: missing '$f' in README.md"
134
+ if ! has_field "$readme" "$f"; then
135
+ error "$name: missing '$f' in README.md"
136
+ fi
101
137
  done
102
138
 
103
139
  # Check folder structure
104
140
  for subdir in contacts projects meetings communication files; do
105
- [ -d "$dir/$subdir" ] || warn "$name: missing $subdir/ directory"
141
+ if [ ! -d "$dir/$subdir" ]; then
142
+ warn "$name: missing $subdir/ directory"
143
+ if [ "$FIX_MODE" = true ]; then
144
+ fix "mkdir -p clients/$name/$subdir"
145
+ fi
146
+ fi
106
147
  done
107
148
 
108
149
  # Check for at least one contact
@@ -115,35 +156,45 @@ check_client() {
115
156
 
116
157
  # Run checks
117
158
  echo "=== Axis CRM Validation ==="
159
+ [ "$FIX_MODE" = true ] && echo "(fix mode — showing actionable suggestions)"
118
160
  echo ""
119
161
 
120
162
  if [ "$MODE" = "all" ] || [ "$MODE" = "leads" ]; then
121
163
  echo "--- Leads ---"
164
+ HAS_LEADS=false
122
165
  for f in "$LEADS_DIR"/*.md; do
123
- [ -f "$f" ] && check_lead "$f"
166
+ [ -f "$f" ] || continue
167
+ HAS_LEADS=true
168
+ check_lead "$f"
124
169
  done
170
+ [ "$HAS_LEADS" = false ] && echo " (no leads found)"
125
171
  echo ""
126
172
  fi
127
173
 
128
174
  if [ "$MODE" = "all" ] || [ "$MODE" = "clients" ]; then
129
175
  echo "--- Clients ---"
176
+ HAS_CLIENTS=false
130
177
  for d in "$CLIENTS_DIR"/*/; do
131
- [ -d "$d" ] && check_client "$d"
178
+ [ -d "$d" ] || continue
179
+ HAS_CLIENTS=true
180
+ check_client "$d"
132
181
  done
182
+ [ "$HAS_CLIENTS" = false ] && echo " (no clients found)"
133
183
  echo ""
134
184
  fi
135
185
 
136
186
  # Summary
137
187
  echo "=== Summary ==="
138
188
  echo " Checked: $CHECKED"
139
- echo -e " Errors: ${RED}$ERRORS${NC}"
140
- echo -e " Warnings: ${YELLOW}$WARNINGS${NC}"
189
+ printf " Errors: ${RED}%d${NC}\n" "$ERRORS"
190
+ printf " Warnings: ${YELLOW}%d${NC}\n" "$WARNINGS"
141
191
 
142
- if [ "$ERRORS" -gt 0 ]; then
192
+ if [ "$ERRORS" -gt 0 ] && [ "$FIX_MODE" = false ]; then
143
193
  echo ""
144
194
  echo "Run with '--fix' flag for actionable suggestions."
145
195
  exit 1
146
- else
196
+ elif [ "$ERRORS" -eq 0 ]; then
147
197
  echo ""
148
- echo -e "${GREEN}All checks passed.${NC}"
198
+ printf "${GREEN}All checks passed.${NC}\n"
149
199
  fi
200
+ exit 0
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: 'Zadani — '
2
+ title: 'Brief — '
3
3
  created: ''
4
4
  type: brief
5
5
  ---