@hasna/contacts 0.2.8 → 0.3.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/dist/cli/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -2387,6 +2369,85 @@ var init_database = __esm(() => {
2387
2369
  );
2388
2370
 
2389
2371
  CREATE INDEX IF NOT EXISTS idx_contact_notes_contact ON contact_notes(contact_id);
2372
+ `,
2373
+ `
2374
+ ALTER TABLE companies ADD COLUMN is_owned_entity INTEGER NOT NULL DEFAULT 0;
2375
+ ALTER TABLE companies ADD COLUMN entity_type TEXT CHECK(entity_type IN ('operating','holding','dissolved','nonprofit','trust','branch','other'));
2376
+
2377
+ ALTER TABLE company_relationships ADD COLUMN start_date TEXT;
2378
+ ALTER TABLE company_relationships ADD COLUMN end_date TEXT;
2379
+ ALTER TABLE company_relationships ADD COLUMN is_primary INTEGER NOT NULL DEFAULT 0;
2380
+ ALTER TABLE company_relationships ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','inactive','ended'));
2381
+
2382
+ CREATE TABLE IF NOT EXISTS org_members (
2383
+ id TEXT PRIMARY KEY,
2384
+ company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
2385
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
2386
+ title TEXT,
2387
+ specialization TEXT,
2388
+ office_phone TEXT,
2389
+ response_sla_hours INTEGER,
2390
+ notes TEXT,
2391
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2392
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
2393
+ UNIQUE(company_id, contact_id)
2394
+ );
2395
+
2396
+ CREATE TABLE IF NOT EXISTS vendor_communications (
2397
+ id TEXT PRIMARY KEY,
2398
+ company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
2399
+ contact_id TEXT REFERENCES contacts(id) ON DELETE SET NULL,
2400
+ comm_date TEXT NOT NULL,
2401
+ type TEXT NOT NULL DEFAULT 'email' CHECK(type IN ('email','call','meeting','invoice_request','invoice_received','payment','dispute','other')),
2402
+ direction TEXT NOT NULL DEFAULT 'outbound' CHECK(direction IN ('inbound','outbound')),
2403
+ subject TEXT,
2404
+ body TEXT,
2405
+ status TEXT NOT NULL DEFAULT 'sent' CHECK(status IN ('sent','awaiting_response','responded','no_response','resolved')),
2406
+ invoice_amount REAL,
2407
+ invoice_currency TEXT,
2408
+ invoice_ref TEXT,
2409
+ follow_up_date TEXT,
2410
+ follow_up_done INTEGER NOT NULL DEFAULT 0,
2411
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2412
+ );
2413
+
2414
+ CREATE TABLE IF NOT EXISTS contact_tasks (
2415
+ id TEXT PRIMARY KEY,
2416
+ title TEXT NOT NULL,
2417
+ description TEXT,
2418
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
2419
+ assigned_by TEXT,
2420
+ deadline TEXT,
2421
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','awaiting_response','in_progress','completed','cancelled','escalated')),
2422
+ priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('low','medium','high','critical')),
2423
+ entity_id TEXT REFERENCES companies(id) ON DELETE SET NULL,
2424
+ linked_todos_task_id TEXT,
2425
+ escalation_rules TEXT NOT NULL DEFAULT '[]',
2426
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2427
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2428
+ );
2429
+
2430
+ CREATE TABLE IF NOT EXISTS applications (
2431
+ id TEXT PRIMARY KEY,
2432
+ program_name TEXT NOT NULL,
2433
+ provider_company_id TEXT REFERENCES companies(id) ON DELETE SET NULL,
2434
+ type TEXT NOT NULL DEFAULT 'other' CHECK(type IN ('ai_credits','grant','startup_program','visa','trademark','tax_filing','loan','other')),
2435
+ value_usd REAL,
2436
+ applicant_contact_id TEXT REFERENCES contacts(id) ON DELETE SET NULL,
2437
+ primary_contact_id TEXT REFERENCES contacts(id) ON DELETE SET NULL,
2438
+ status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','submitted','pending','approved','rejected','follow_up_needed','expired','cancelled')),
2439
+ submitted_date TEXT,
2440
+ decision_date TEXT,
2441
+ follow_up_date TEXT,
2442
+ notes TEXT,
2443
+ method TEXT CHECK(method IN ('email','form','typeform','hubspot','manual','browser','feathery','other')),
2444
+ form_url TEXT,
2445
+ metadata TEXT NOT NULL DEFAULT '{}',
2446
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2447
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2448
+ );
2449
+
2450
+ ALTER TABLE contact_notes ADD COLUMN company_id TEXT REFERENCES companies(id) ON DELETE SET NULL;
2390
2451
  `
2391
2452
  ];
2392
2453
  });
@@ -2450,7 +2511,9 @@ function rowToCompany(row) {
2450
2511
  ...row,
2451
2512
  custom_fields: JSON.parse(row.custom_fields || "{}"),
2452
2513
  archived: !!row.archived,
2453
- project_id: row.project_id ?? null
2514
+ project_id: row.project_id ?? null,
2515
+ is_owned_entity: !!row.is_owned_entity,
2516
+ entity_type: row.entity_type ?? null
2454
2517
  };
2455
2518
  }
2456
2519
  function insertEmails(db, contactId, companyId, emails) {
@@ -2767,7 +2830,9 @@ function rowToCompany2(row) {
2767
2830
  ...row,
2768
2831
  custom_fields: JSON.parse(row.custom_fields || "{}"),
2769
2832
  archived: !!row.archived,
2770
- project_id: row.project_id ?? null
2833
+ project_id: row.project_id ?? null,
2834
+ is_owned_entity: !!row.is_owned_entity,
2835
+ entity_type: row.entity_type ?? null
2771
2836
  };
2772
2837
  }
2773
2838
  function insertEmails2(db, companyId, emails) {
@@ -2831,8 +2896,8 @@ function createCompany(input, db) {
2831
2896
  const d = db || getDatabase();
2832
2897
  const id = uuid();
2833
2898
  const timestamp = now();
2834
- d.run(`INSERT INTO companies (id, name, domain, logo_url, description, industry, size, founded_year, notes, custom_fields, created_at, updated_at)
2835
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2899
+ d.run(`INSERT INTO companies (id, name, domain, logo_url, description, industry, size, founded_year, notes, custom_fields, is_owned_entity, entity_type, created_at, updated_at)
2900
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2836
2901
  id,
2837
2902
  input.name,
2838
2903
  input.domain ?? null,
@@ -2843,6 +2908,8 @@ function createCompany(input, db) {
2843
2908
  input.founded_year ?? null,
2844
2909
  input.notes ?? null,
2845
2910
  JSON.stringify(input.custom_fields ?? {}),
2911
+ input.is_owned_entity ? 1 : 0,
2912
+ input.entity_type ?? null,
2846
2913
  timestamp,
2847
2914
  timestamp
2848
2915
  ]);
@@ -2879,6 +2946,7 @@ function listCompanies(opts = {}, db) {
2879
2946
  tag_id,
2880
2947
  project_id,
2881
2948
  archived = false,
2949
+ is_owned_entity,
2882
2950
  order_by = "name",
2883
2951
  order_dir = "asc"
2884
2952
  } = opts;
@@ -2898,6 +2966,10 @@ function listCompanies(opts = {}, db) {
2898
2966
  conditions.push("EXISTS (SELECT 1 FROM company_tags ct WHERE ct.company_id = co.id AND ct.tag_id = ?)");
2899
2967
  params.push(tag_id);
2900
2968
  }
2969
+ if (is_owned_entity !== undefined) {
2970
+ conditions.push("co.is_owned_entity = ?");
2971
+ params.push(is_owned_entity ? 1 : 0);
2972
+ }
2901
2973
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2902
2974
  const validOrderBy = ["name", "created_at", "updated_at"].includes(order_by) ? order_by : "name";
2903
2975
  const validOrderDir = order_dir === "desc" ? "DESC" : "ASC";
@@ -2953,6 +3025,14 @@ function updateCompany(id, input, db) {
2953
3025
  setClauses.push("project_id = ?");
2954
3026
  params.push(input.project_id);
2955
3027
  }
3028
+ if (input.is_owned_entity !== undefined) {
3029
+ setClauses.push("is_owned_entity = ?");
3030
+ params.push(input.is_owned_entity ? 1 : 0);
3031
+ }
3032
+ if ("entity_type" in input && input.entity_type !== undefined) {
3033
+ setClauses.push("entity_type = ?");
3034
+ params.push(input.entity_type);
3035
+ }
2956
3036
  params.push(id);
2957
3037
  d.run(`UPDATE companies SET ${setClauses.join(", ")} WHERE id = ?`, params);
2958
3038
  logActivity(d, { company_id: id, action: "company.updated", details: `Updated company: ${existing.name}` });
@@ -3747,9 +3827,374 @@ var {
3747
3827
  // src/cli/index.tsx
3748
3828
  init_contacts();
3749
3829
  init_companies();
3750
- init_tags();
3751
3830
  import chalk from "chalk";
3752
3831
 
3832
+ // src/db/relationships.ts
3833
+ init_types();
3834
+ init_database();
3835
+ function rowToCompanyRelationship(row) {
3836
+ return {
3837
+ ...row,
3838
+ relationship_type: row.relationship_type,
3839
+ start_date: row.start_date ?? null,
3840
+ end_date: row.end_date ?? null,
3841
+ is_primary: !!row.is_primary,
3842
+ status: row.status ?? "active"
3843
+ };
3844
+ }
3845
+ function createCompanyRelationship(input, db) {
3846
+ const d = db || getDatabase();
3847
+ const contact = d.query(`SELECT id FROM contacts WHERE id = ?`).get(input.contact_id);
3848
+ if (!contact)
3849
+ throw new ContactNotFoundError(input.contact_id);
3850
+ const company = d.query(`SELECT id FROM companies WHERE id = ?`).get(input.company_id);
3851
+ if (!company)
3852
+ throw new Error(`Company ${input.company_id} not found`);
3853
+ const id = uuid();
3854
+ d.run(`INSERT INTO company_relationships (id, contact_id, company_id, relationship_type, notes, start_date, end_date, is_primary, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3855
+ id,
3856
+ input.contact_id,
3857
+ input.company_id,
3858
+ input.relationship_type,
3859
+ input.notes ?? null,
3860
+ input.start_date ?? null,
3861
+ input.end_date ?? null,
3862
+ input.is_primary ? 1 : 0,
3863
+ input.status ?? "active"
3864
+ ]);
3865
+ return rowToCompanyRelationship(d.query(`SELECT * FROM company_relationships WHERE id = ?`).get(id));
3866
+ }
3867
+ function listCompanyRelationships(opts = {}, db) {
3868
+ const d = db || getDatabase();
3869
+ const conditions = [];
3870
+ const params = [];
3871
+ if (opts.contact_id) {
3872
+ conditions.push("contact_id = ?");
3873
+ params.push(opts.contact_id);
3874
+ }
3875
+ if (opts.company_id) {
3876
+ conditions.push("company_id = ?");
3877
+ params.push(opts.company_id);
3878
+ }
3879
+ if (opts.relationship_type) {
3880
+ conditions.push("relationship_type = ?");
3881
+ params.push(opts.relationship_type);
3882
+ }
3883
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3884
+ const rows = d.query(`SELECT * FROM company_relationships ${where} ORDER BY created_at DESC`).all(...params);
3885
+ return rows.map(rowToCompanyRelationship);
3886
+ }
3887
+
3888
+ // src/db/vendor-comms.ts
3889
+ init_database();
3890
+ function rowToVendorComm(row) {
3891
+ return {
3892
+ id: row.id,
3893
+ company_id: row.company_id,
3894
+ contact_id: row.contact_id,
3895
+ comm_date: row.comm_date,
3896
+ type: row.type,
3897
+ direction: row.direction,
3898
+ subject: row.subject,
3899
+ body: row.body,
3900
+ status: row.status,
3901
+ invoice_amount: row.invoice_amount,
3902
+ invoice_currency: row.invoice_currency,
3903
+ invoice_ref: row.invoice_ref,
3904
+ follow_up_date: row.follow_up_date,
3905
+ follow_up_done: !!row.follow_up_done,
3906
+ created_at: row.created_at
3907
+ };
3908
+ }
3909
+ function logVendorCommunication(input, db) {
3910
+ const d = db || getDatabase();
3911
+ const id = uuid();
3912
+ d.run(`INSERT INTO vendor_communications
3913
+ (id, company_id, contact_id, comm_date, type, direction, subject, body, status,
3914
+ invoice_amount, invoice_currency, invoice_ref, follow_up_date, follow_up_done)
3915
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3916
+ id,
3917
+ input.company_id,
3918
+ input.contact_id ?? null,
3919
+ input.comm_date,
3920
+ input.type ?? "email",
3921
+ input.direction ?? "outbound",
3922
+ input.subject ?? null,
3923
+ input.body ?? null,
3924
+ input.status ?? "sent",
3925
+ input.invoice_amount ?? null,
3926
+ input.invoice_currency ?? null,
3927
+ input.invoice_ref ?? null,
3928
+ input.follow_up_date ?? null,
3929
+ input.follow_up_done ? 1 : 0
3930
+ ]);
3931
+ return rowToVendorComm(d.query(`SELECT * FROM vendor_communications WHERE id = ?`).get(id));
3932
+ }
3933
+ function listVendorCommunications(companyId, opts = {}, db) {
3934
+ const d = db || getDatabase();
3935
+ const conditions = ["company_id = ?"];
3936
+ const params = [companyId];
3937
+ if (opts.type) {
3938
+ conditions.push("type = ?");
3939
+ params.push(opts.type);
3940
+ }
3941
+ if (opts.status) {
3942
+ conditions.push("status = ?");
3943
+ params.push(opts.status);
3944
+ }
3945
+ if (opts.direction) {
3946
+ conditions.push("direction = ?");
3947
+ params.push(opts.direction);
3948
+ }
3949
+ const where = conditions.join(" AND ");
3950
+ const rows = d.query(`SELECT * FROM vendor_communications WHERE ${where} ORDER BY comm_date DESC`).all(...params);
3951
+ return rows.map(rowToVendorComm);
3952
+ }
3953
+ function listPendingFollowUps(db) {
3954
+ const d = db || getDatabase();
3955
+ const today = new Date().toISOString().slice(0, 10);
3956
+ const rows = d.query(`SELECT * FROM vendor_communications
3957
+ WHERE follow_up_date <= ? AND follow_up_done = 0
3958
+ ORDER BY follow_up_date ASC`).all(today);
3959
+ return rows.map(rowToVendorComm);
3960
+ }
3961
+ function listMissingInvoices(db) {
3962
+ const d = db || getDatabase();
3963
+ const rows = d.query(`SELECT * FROM vendor_communications
3964
+ WHERE type = 'invoice_request' AND status IN ('awaiting_response','no_response')
3965
+ ORDER BY comm_date ASC`).all();
3966
+ return rows.map(rowToVendorComm);
3967
+ }
3968
+
3969
+ // src/db/contact-tasks.ts
3970
+ init_database();
3971
+ function rowToContactTask(row) {
3972
+ return {
3973
+ id: row.id,
3974
+ title: row.title,
3975
+ description: row.description,
3976
+ contact_id: row.contact_id,
3977
+ assigned_by: row.assigned_by,
3978
+ deadline: row.deadline,
3979
+ status: row.status,
3980
+ priority: row.priority,
3981
+ entity_id: row.entity_id,
3982
+ linked_todos_task_id: row.linked_todos_task_id,
3983
+ escalation_rules: JSON.parse(row.escalation_rules || "[]"),
3984
+ created_at: row.created_at,
3985
+ updated_at: row.updated_at
3986
+ };
3987
+ }
3988
+ function createContactTask(input, db) {
3989
+ const d = db || getDatabase();
3990
+ const id = uuid();
3991
+ const timestamp = now();
3992
+ d.run(`INSERT INTO contact_tasks
3993
+ (id, title, description, contact_id, assigned_by, deadline, status, priority,
3994
+ entity_id, linked_todos_task_id, escalation_rules, created_at, updated_at)
3995
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3996
+ id,
3997
+ input.title,
3998
+ input.description ?? null,
3999
+ input.contact_id,
4000
+ input.assigned_by ?? null,
4001
+ input.deadline ?? null,
4002
+ input.status ?? "pending",
4003
+ input.priority ?? "medium",
4004
+ input.entity_id ?? null,
4005
+ input.linked_todos_task_id ?? null,
4006
+ JSON.stringify(input.escalation_rules ?? []),
4007
+ timestamp,
4008
+ timestamp
4009
+ ]);
4010
+ return rowToContactTask(d.query(`SELECT * FROM contact_tasks WHERE id = ?`).get(id));
4011
+ }
4012
+ function listContactTasks(opts = {}, db) {
4013
+ const d = db || getDatabase();
4014
+ const conditions = [];
4015
+ const params = [];
4016
+ if (opts.contact_id) {
4017
+ conditions.push("contact_id = ?");
4018
+ params.push(opts.contact_id);
4019
+ }
4020
+ if (opts.entity_id) {
4021
+ conditions.push("entity_id = ?");
4022
+ params.push(opts.entity_id);
4023
+ }
4024
+ if (opts.status) {
4025
+ conditions.push("status = ?");
4026
+ params.push(opts.status);
4027
+ }
4028
+ if (opts.priority) {
4029
+ conditions.push("priority = ?");
4030
+ params.push(opts.priority);
4031
+ }
4032
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4033
+ const rows = d.query(`SELECT * FROM contact_tasks ${where} ORDER BY deadline ASC, priority DESC, created_at ASC`).all(...params);
4034
+ return rows.map(rowToContactTask);
4035
+ }
4036
+ function updateContactTask(id, input, db) {
4037
+ const d = db || getDatabase();
4038
+ const setClauses = ["updated_at = ?"];
4039
+ const params = [now()];
4040
+ if (input.title !== undefined) {
4041
+ setClauses.push("title = ?");
4042
+ params.push(input.title);
4043
+ }
4044
+ if ("description" in input) {
4045
+ setClauses.push("description = ?");
4046
+ params.push(input.description ?? null);
4047
+ }
4048
+ if ("assigned_by" in input) {
4049
+ setClauses.push("assigned_by = ?");
4050
+ params.push(input.assigned_by ?? null);
4051
+ }
4052
+ if ("deadline" in input) {
4053
+ setClauses.push("deadline = ?");
4054
+ params.push(input.deadline ?? null);
4055
+ }
4056
+ if (input.status !== undefined) {
4057
+ setClauses.push("status = ?");
4058
+ params.push(input.status);
4059
+ }
4060
+ if (input.priority !== undefined) {
4061
+ setClauses.push("priority = ?");
4062
+ params.push(input.priority);
4063
+ }
4064
+ if ("entity_id" in input) {
4065
+ setClauses.push("entity_id = ?");
4066
+ params.push(input.entity_id ?? null);
4067
+ }
4068
+ if ("linked_todos_task_id" in input) {
4069
+ setClauses.push("linked_todos_task_id = ?");
4070
+ params.push(input.linked_todos_task_id ?? null);
4071
+ }
4072
+ if (input.escalation_rules !== undefined) {
4073
+ setClauses.push("escalation_rules = ?");
4074
+ params.push(JSON.stringify(input.escalation_rules));
4075
+ }
4076
+ params.push(id);
4077
+ d.run(`UPDATE contact_tasks SET ${setClauses.join(", ")} WHERE id = ?`, params);
4078
+ return rowToContactTask(d.query(`SELECT * FROM contact_tasks WHERE id = ?`).get(id));
4079
+ }
4080
+ function listOverdueTasks(db) {
4081
+ const d = db || getDatabase();
4082
+ const now_iso = new Date().toISOString();
4083
+ const rows = d.query(`SELECT * FROM contact_tasks
4084
+ WHERE deadline < ? AND status NOT IN ('completed','cancelled')
4085
+ ORDER BY deadline ASC`).all(now_iso);
4086
+ return rows.map(rowToContactTask);
4087
+ }
4088
+
4089
+ // src/db/applications.ts
4090
+ init_database();
4091
+ function rowToApplication(row) {
4092
+ return {
4093
+ id: row.id,
4094
+ program_name: row.program_name,
4095
+ provider_company_id: row.provider_company_id,
4096
+ type: row.type,
4097
+ value_usd: row.value_usd,
4098
+ applicant_contact_id: row.applicant_contact_id,
4099
+ primary_contact_id: row.primary_contact_id,
4100
+ status: row.status,
4101
+ submitted_date: row.submitted_date,
4102
+ decision_date: row.decision_date,
4103
+ follow_up_date: row.follow_up_date,
4104
+ notes: row.notes,
4105
+ method: row.method ?? null,
4106
+ form_url: row.form_url,
4107
+ metadata: JSON.parse(row.metadata || "{}"),
4108
+ created_at: row.created_at,
4109
+ updated_at: row.updated_at
4110
+ };
4111
+ }
4112
+ function createApplication(input, db) {
4113
+ const d = db || getDatabase();
4114
+ const id = uuid();
4115
+ const timestamp = now();
4116
+ d.run(`INSERT INTO applications
4117
+ (id, program_name, provider_company_id, type, value_usd, applicant_contact_id, primary_contact_id,
4118
+ status, submitted_date, decision_date, follow_up_date, notes, method, form_url, metadata, created_at, updated_at)
4119
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4120
+ id,
4121
+ input.program_name,
4122
+ input.provider_company_id ?? null,
4123
+ input.type ?? "other",
4124
+ input.value_usd ?? null,
4125
+ input.applicant_contact_id ?? null,
4126
+ input.primary_contact_id ?? null,
4127
+ input.status ?? "draft",
4128
+ input.submitted_date ?? null,
4129
+ input.decision_date ?? null,
4130
+ input.follow_up_date ?? null,
4131
+ input.notes ?? null,
4132
+ input.method ?? null,
4133
+ input.form_url ?? null,
4134
+ JSON.stringify(input.metadata ?? {}),
4135
+ timestamp,
4136
+ timestamp
4137
+ ]);
4138
+ return rowToApplication(d.query(`SELECT * FROM applications WHERE id = ?`).get(id));
4139
+ }
4140
+ function listApplications(opts = {}, db) {
4141
+ const d = db || getDatabase();
4142
+ const conditions = [];
4143
+ const params = [];
4144
+ if (opts.type) {
4145
+ conditions.push("type = ?");
4146
+ params.push(opts.type);
4147
+ }
4148
+ if (opts.status) {
4149
+ conditions.push("status = ?");
4150
+ params.push(opts.status);
4151
+ }
4152
+ if (opts.provider_company_id) {
4153
+ conditions.push("provider_company_id = ?");
4154
+ params.push(opts.provider_company_id);
4155
+ }
4156
+ if (opts.applicant_contact_id) {
4157
+ conditions.push("applicant_contact_id = ?");
4158
+ params.push(opts.applicant_contact_id);
4159
+ }
4160
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4161
+ const rows = d.query(`SELECT * FROM applications ${where} ORDER BY created_at DESC`).all(...params);
4162
+ return rows.map(rowToApplication);
4163
+ }
4164
+ function listFollowUpDue(db) {
4165
+ const d = db || getDatabase();
4166
+ const today = new Date().toISOString().slice(0, 10);
4167
+ const rows = d.query(`SELECT * FROM applications
4168
+ WHERE follow_up_date <= ? AND status = 'follow_up_needed'
4169
+ ORDER BY follow_up_date ASC`).all(today);
4170
+ return rows.map(rowToApplication);
4171
+ }
4172
+
4173
+ // src/db/org-members.ts
4174
+ init_database();
4175
+ function rowToOrgMember(row) {
4176
+ return {
4177
+ id: row.id,
4178
+ company_id: row.company_id,
4179
+ contact_id: row.contact_id,
4180
+ title: row.title,
4181
+ specialization: row.specialization,
4182
+ office_phone: row.office_phone,
4183
+ response_sla_hours: row.response_sla_hours,
4184
+ notes: row.notes,
4185
+ created_at: row.created_at,
4186
+ updated_at: row.updated_at
4187
+ };
4188
+ }
4189
+ function listOrgMembersForContact(contactId, db) {
4190
+ const d = db || getDatabase();
4191
+ const rows = d.query(`SELECT * FROM org_members WHERE contact_id = ? ORDER BY created_at ASC`).all(contactId);
4192
+ return rows.map(rowToOrgMember);
4193
+ }
4194
+
4195
+ // src/cli/index.tsx
4196
+ init_tags();
4197
+
3753
4198
  // src/db/groups.ts
3754
4199
  init_database();
3755
4200
  function createGroup(db, input) {
@@ -4655,4 +5100,436 @@ complete -F _contacts_completion contacts`);
4655
5100
  console.error(chalk.red("Supported shells: bash, zsh, fish"));
4656
5101
  }
4657
5102
  });
5103
+ var entities = program.command("entities").description("Manage your owned legal entities");
5104
+ entities.command("list").description("List all owned legal entities").action(() => {
5105
+ const db = getDatabase();
5106
+ const result = listCompanies({ limit: 200 }, db);
5107
+ const owned = result.companies.filter((c) => c.is_owned_entity);
5108
+ if (!owned.length) {
5109
+ console.log(chalk.yellow("No owned entities found. Add one with: contacts companies add"));
5110
+ return;
5111
+ }
5112
+ console.log();
5113
+ renderTable(["Name", "Type", "Industry", "Description"], owned.map((e) => ({
5114
+ Name: e.name,
5115
+ Type: e.entity_type || "operating",
5116
+ Industry: e.industry || "",
5117
+ Description: e.description || ""
5118
+ })));
5119
+ console.log(chalk.gray(`
5120
+ ${owned.length} owned entity/entities
5121
+ `));
5122
+ });
5123
+ entities.command("show <id>").description("Show entity with full team").action((id) => {
5124
+ const db = getDatabase();
5125
+ const company = getCompany(id);
5126
+ if (!company) {
5127
+ console.error(chalk.red(`
5128
+ Entity not found: ${id}
5129
+ `));
5130
+ process.exit(1);
5131
+ }
5132
+ console.log(`
5133
+ ` + chalk.bold.blue("\u2501\u2501\u2501 Entity: ") + chalk.bold(company.name) + chalk.bold.blue(" \u2501\u2501\u2501"));
5134
+ console.log();
5135
+ if (company.industry)
5136
+ console.log(chalk.gray(" Industry: ") + company.industry);
5137
+ if (company.description)
5138
+ console.log(chalk.gray(" Description: ") + company.description);
5139
+ console.log(chalk.gray(` ID: ${company.id}`));
5140
+ console.log();
5141
+ const team = listCompanyRelationships({ company_id: id }, db);
5142
+ if (team.length === 0) {
5143
+ console.log(chalk.gray(` No team members assigned.
5144
+ `));
5145
+ return;
5146
+ }
5147
+ console.log(chalk.yellow(` Team (${team.length}):
5148
+ `));
5149
+ const rows = team.map((r) => {
5150
+ let name = r.contact_id;
5151
+ try {
5152
+ const c = getContact(r.contact_id);
5153
+ name = c.display_name;
5154
+ } catch {}
5155
+ return {
5156
+ Contact: name,
5157
+ Role: r.relationship_type,
5158
+ Primary: r.is_primary ? "yes" : "",
5159
+ Status: r.status
5160
+ };
5161
+ });
5162
+ renderTable(["Contact", "Role", "Primary", "Status"], rows);
5163
+ console.log();
5164
+ });
5165
+ entities.command("add-contact <entity-id> <contact-id>").description("Link a contact to an entity with a role").option("--role <role>", "Role (tax_preparer|bank_manager|attorney|registered_agent|accountant|payroll_provider|insurance_broker|advisor|other)", "other").option("--primary", "Mark as primary contact for this role").action((entityId, contactId, opts) => {
5166
+ const db = getDatabase();
5167
+ const contact = getContact(contactId);
5168
+ const company = getCompany(entityId);
5169
+ if (!company) {
5170
+ console.error(chalk.red(`
5171
+ Entity not found: ${entityId}
5172
+ `));
5173
+ process.exit(1);
5174
+ }
5175
+ const rel = createCompanyRelationship({
5176
+ contact_id: contactId,
5177
+ company_id: entityId,
5178
+ relationship_type: opts.role || "other",
5179
+ is_primary: opts.primary || false
5180
+ }, db);
5181
+ console.log(chalk.green(`
5182
+ \u2713 Linked ${contact.display_name} to ${company.name} as ${rel.relationship_type}
5183
+ `));
5184
+ });
5185
+ entities.command("team <id>").description("Show full team matrix for an entity").action((id) => {
5186
+ const db = getDatabase();
5187
+ const company = getCompany(id);
5188
+ if (!company) {
5189
+ console.error(chalk.red(`
5190
+ Entity not found: ${id}
5191
+ `));
5192
+ process.exit(1);
5193
+ }
5194
+ const team = listCompanyRelationships({ company_id: id }, db);
5195
+ if (team.length === 0) {
5196
+ console.log(chalk.yellow(`
5197
+ No team members for ${company.name}.
5198
+ `));
5199
+ return;
5200
+ }
5201
+ console.log(`
5202
+ ` + chalk.bold(`Team: ${company.name}`) + `
5203
+ `);
5204
+ const rows = team.map((r) => {
5205
+ let name = r.contact_id;
5206
+ try {
5207
+ const c = getContact(r.contact_id);
5208
+ name = c.display_name;
5209
+ } catch {}
5210
+ return {
5211
+ Contact: name,
5212
+ Role: r.relationship_type,
5213
+ Primary: r.is_primary ? "yes" : "",
5214
+ Status: r.status
5215
+ };
5216
+ });
5217
+ renderTable(["Contact", "Role", "Primary", "Status"], rows);
5218
+ console.log();
5219
+ });
5220
+ program.command("workload <id>").description("Show workload summary for a contact").action((id) => {
5221
+ const db = getDatabase();
5222
+ const contact = getContact(id);
5223
+ const companyRels = listCompanyRelationships({ contact_id: id }, db);
5224
+ const overdue = listOverdueTasks(db).filter((t) => t.contact_id === id);
5225
+ const orgMemberships = listOrgMembersForContact(id, db);
5226
+ console.log(chalk.bold(`
5227
+ ${contact.display_name}`));
5228
+ if (contact.job_title)
5229
+ console.log(chalk.gray(` ${contact.job_title}`));
5230
+ console.log();
5231
+ if (companyRels.length) {
5232
+ console.log(chalk.cyan(` Manages/linked to ${companyRels.length} company relationship(s):`));
5233
+ for (const r of companyRels.slice(0, 5)) {
5234
+ console.log(chalk.gray(` \u2022 ${r.relationship_type}${r.notes ? ` \u2014 ${r.notes}` : ""}`));
5235
+ }
5236
+ }
5237
+ if (orgMemberships.length) {
5238
+ console.log(chalk.cyan(` Org memberships: ${orgMemberships.length}`));
5239
+ }
5240
+ if (overdue.length) {
5241
+ console.log(chalk.red(` \u26A0 Overdue tasks: ${overdue.length}`));
5242
+ } else {
5243
+ console.log(chalk.green(" No overdue tasks"));
5244
+ }
5245
+ if (contact.last_contacted_at) {
5246
+ const days = Math.floor((Date.now() - new Date(contact.last_contacted_at).getTime()) / 86400000);
5247
+ const color = days > 30 ? chalk.red : days > 14 ? chalk.yellow : chalk.green;
5248
+ console.log(color(` Last contact: ${days} day(s) ago`));
5249
+ } else {
5250
+ console.log(chalk.gray(" Never contacted"));
5251
+ }
5252
+ console.log();
5253
+ });
5254
+ var vendor = program.command("vendor").description("Track vendor communications and invoices");
5255
+ vendor.command("log <company-id>").description("Log a vendor communication").option("--type <type>", "Type: email|invoice_request|invoice_received|call|payment|dispute|other", "email").option("--subject <subject>", "Subject").option("--body <body>", "Body/notes").option("--amount <n>", "Invoice amount").option("--currency <c>", "Currency", "USD").option("--ref <ref>", "Invoice reference").option("--follow-up <date>", "Follow-up date (YYYY-MM-DD)").option("--status <status>", "Status: sent|awaiting_response|responded|no_response|resolved", "sent").option("--contact <id>", "Contact ID (optional)").option("--direction <dir>", "Direction: inbound|outbound", "outbound").action((companyId, opts) => {
5256
+ const db = getDatabase();
5257
+ const company = getCompany(companyId);
5258
+ if (!company) {
5259
+ console.error(chalk.red(`
5260
+ Company not found: ${companyId}
5261
+ `));
5262
+ process.exit(1);
5263
+ }
5264
+ const comm = logVendorCommunication({
5265
+ company_id: companyId,
5266
+ contact_id: opts.contact,
5267
+ comm_date: new Date().toISOString().slice(0, 10),
5268
+ type: opts.type || "email",
5269
+ direction: opts.direction || "outbound",
5270
+ subject: opts.subject,
5271
+ body: opts.body,
5272
+ status: opts.status || "sent",
5273
+ invoice_amount: opts.amount ? parseFloat(opts.amount) : undefined,
5274
+ invoice_currency: opts.currency,
5275
+ invoice_ref: opts.ref,
5276
+ follow_up_date: opts.followUp
5277
+ }, db);
5278
+ console.log(chalk.green(`
5279
+ \u2713 Logged ${comm.type} communication with ${company.name} (${comm.id})
5280
+ `));
5281
+ });
5282
+ vendor.command("missing-invoices").description("Show all invoice requests with no response").action(() => {
5283
+ const db = getDatabase();
5284
+ const missing = listMissingInvoices(db);
5285
+ if (!missing.length) {
5286
+ console.log(chalk.green(`
5287
+ No missing invoices.
5288
+ `));
5289
+ return;
5290
+ }
5291
+ console.log(chalk.yellow(`
5292
+ ${missing.length} missing invoice(s):
5293
+ `));
5294
+ renderTable(["Company", "Date", "Subject", "Status"], missing.map((m) => ({
5295
+ Company: m.company_id,
5296
+ Date: m.comm_date,
5297
+ Subject: m.subject || "",
5298
+ Status: m.status
5299
+ })));
5300
+ console.log();
5301
+ });
5302
+ vendor.command("pending-followups").description("Show vendor follow-ups due today or overdue").action(() => {
5303
+ const db = getDatabase();
5304
+ const pending = listPendingFollowUps(db);
5305
+ if (!pending.length) {
5306
+ console.log(chalk.green(`
5307
+ No pending follow-ups.
5308
+ `));
5309
+ return;
5310
+ }
5311
+ console.log(chalk.yellow(`
5312
+ ${pending.length} pending follow-up(s):
5313
+ `));
5314
+ renderTable(["Company", "Type", "Follow-up", "Subject"], pending.map((p) => ({
5315
+ Company: p.company_id,
5316
+ Type: p.type,
5317
+ "Follow-up": p.follow_up_date || "",
5318
+ Subject: p.subject || ""
5319
+ })));
5320
+ console.log();
5321
+ });
5322
+ vendor.command("history <company-id>").description("Show vendor communication history for a company").action((id) => {
5323
+ const db = getDatabase();
5324
+ const company = getCompany(id);
5325
+ if (!company) {
5326
+ console.error(chalk.red(`
5327
+ Company not found: ${id}
5328
+ `));
5329
+ process.exit(1);
5330
+ }
5331
+ const comms = listVendorCommunications(id, {}, db);
5332
+ if (!comms.length) {
5333
+ console.log(chalk.gray(`
5334
+ No communications logged for ${company.name}.
5335
+ `));
5336
+ return;
5337
+ }
5338
+ console.log(chalk.bold(`
5339
+ Communications: ${company.name} (${comms.length})
5340
+ `));
5341
+ renderTable(["Date", "Type", "Direction", "Subject", "Status"], comms.map((c) => ({
5342
+ Date: c.comm_date,
5343
+ Type: c.type,
5344
+ Direction: c.direction,
5345
+ Subject: c.subject || "",
5346
+ Status: c.status
5347
+ })));
5348
+ console.log();
5349
+ });
5350
+ var taskCmd = program.command("task").description("Contact-assigned tasks with escalation");
5351
+ taskCmd.command("create").description("Create a contact task").option("--contact <id>", "Contact ID (required)").option("--title <title>", "Task title (required)").option("--deadline <date>", "Deadline (YYYY-MM-DD)").option("--priority <p>", "Priority: low|medium|high|critical", "medium").option("--entity <id>", "Entity/company this task is for").option("--escalate <rule>", "Escalation rule: contactId:afterDays (repeatable)", collect, []).action(async (opts) => {
5352
+ const db = getDatabase();
5353
+ let contactId = opts.contact;
5354
+ let title = opts.title;
5355
+ if (!contactId) {
5356
+ contactId = await prompt("Contact ID (required):");
5357
+ if (!contactId) {
5358
+ console.error(chalk.red("Contact ID is required."));
5359
+ process.exit(1);
5360
+ }
5361
+ }
5362
+ if (!title) {
5363
+ title = await prompt("Task title (required):");
5364
+ if (!title) {
5365
+ console.error(chalk.red("Task title is required."));
5366
+ process.exit(1);
5367
+ }
5368
+ }
5369
+ const escalationRules = opts.escalate.map((rule) => {
5370
+ const [contactIdPart, daysPart] = rule.split(":");
5371
+ return {
5372
+ escalate_to_contact_id: contactIdPart || "",
5373
+ after_days: parseInt(daysPart || "7", 10),
5374
+ method: "email"
5375
+ };
5376
+ });
5377
+ const contact = getContact(contactId);
5378
+ const task = createContactTask({
5379
+ title,
5380
+ contact_id: contactId,
5381
+ deadline: opts.deadline,
5382
+ priority: opts.priority || "medium",
5383
+ entity_id: opts.entity,
5384
+ escalation_rules: escalationRules.length ? escalationRules : undefined
5385
+ }, db);
5386
+ console.log(chalk.green(`
5387
+ \u2713 Task created for ${contact.display_name}: ${task.title} (${task.id})
5388
+ `));
5389
+ });
5390
+ taskCmd.command("list [contact-id]").description("List contact tasks").option("--overdue", "Show only overdue tasks").option("--status <s>", "Filter by status").action((contactId, opts) => {
5391
+ const db = getDatabase();
5392
+ let tasks;
5393
+ if (opts.overdue) {
5394
+ tasks = listOverdueTasks(db);
5395
+ if (contactId)
5396
+ tasks = tasks.filter((t) => t.contact_id === contactId);
5397
+ } else {
5398
+ tasks = listContactTasks({
5399
+ contact_id: contactId,
5400
+ status: opts.status
5401
+ }, db);
5402
+ }
5403
+ if (!tasks.length) {
5404
+ console.log(chalk.gray(`
5405
+ No tasks found.
5406
+ `));
5407
+ return;
5408
+ }
5409
+ console.log();
5410
+ renderTable(["Title", "Contact", "Priority", "Status", "Deadline"], tasks.map((t) => {
5411
+ let contactName = t.contact_id;
5412
+ try {
5413
+ contactName = getContact(t.contact_id).display_name;
5414
+ } catch {}
5415
+ return {
5416
+ Title: t.title,
5417
+ Contact: contactName,
5418
+ Priority: t.priority,
5419
+ Status: t.status,
5420
+ Deadline: t.deadline ? t.deadline.slice(0, 10) : ""
5421
+ };
5422
+ }));
5423
+ console.log(chalk.gray(`
5424
+ ${tasks.length} task(s)
5425
+ `));
5426
+ });
5427
+ taskCmd.command("done <id>").description("Mark a contact task as completed").action((id) => {
5428
+ const db = getDatabase();
5429
+ const updated = updateContactTask(id, { status: "completed" }, db);
5430
+ console.log(chalk.green(`
5431
+ \u2713 Task completed: ${updated.title}
5432
+ `));
5433
+ });
5434
+ var appsCmd = program.command("applications").description("Track grant/credit/program applications");
5435
+ appsCmd.command("list").description("List applications").option("--status <s>", "Filter by status").option("--type <t>", "Filter by type").action((opts) => {
5436
+ const db = getDatabase();
5437
+ const apps = listApplications({
5438
+ status: opts.status,
5439
+ type: opts.type
5440
+ }, db);
5441
+ if (!apps.length) {
5442
+ console.log(chalk.gray(`
5443
+ No applications found.
5444
+ `));
5445
+ return;
5446
+ }
5447
+ console.log();
5448
+ renderTable(["Program", "Type", "Status", "Value", "Follow-up"], apps.map((a) => ({
5449
+ Program: a.program_name,
5450
+ Type: a.type,
5451
+ Status: a.status,
5452
+ Value: a.value_usd ? `$${a.value_usd.toLocaleString()}` : "",
5453
+ "Follow-up": a.follow_up_date ? a.follow_up_date.slice(0, 10) : ""
5454
+ })));
5455
+ console.log(chalk.gray(`
5456
+ ${apps.length} application(s)
5457
+ `));
5458
+ });
5459
+ appsCmd.command("add").description("Add a new application").option("--name <name>", "Program name (required)").option("--type <type>", "Type: ai_credits|grant|startup_program|other", "other").option("--value <usd>", "Value in USD").option("--status <status>", "Status: draft|submitted|pending|approved|rejected|follow_up_needed|expired|cancelled", "draft").option("--follow-up <date>", "Follow-up date (YYYY-MM-DD)").option("--provider <id>", "Provider company ID").option("--contact <id>", "Primary contact ID").option("--method <method>", "Application method: email|form|typeform|hubspot|manual|browser|feathery|other").option("--url <url>", "Form URL").action(async (opts) => {
5460
+ const db = getDatabase();
5461
+ let name = opts.name;
5462
+ if (!name) {
5463
+ name = await prompt("Program name (required):");
5464
+ if (!name) {
5465
+ console.error(chalk.red("Program name is required."));
5466
+ process.exit(1);
5467
+ }
5468
+ }
5469
+ const app = createApplication({
5470
+ program_name: name,
5471
+ type: opts.type || "other",
5472
+ value_usd: opts.value ? parseFloat(opts.value) : undefined,
5473
+ status: opts.status || "draft",
5474
+ follow_up_date: opts.followUp,
5475
+ provider_company_id: opts.provider,
5476
+ primary_contact_id: opts.contact,
5477
+ method: opts.method,
5478
+ form_url: opts.url
5479
+ }, db);
5480
+ console.log(chalk.green(`
5481
+ \u2713 Application created: ${app.program_name} (${app.id})
5482
+ `));
5483
+ });
5484
+ appsCmd.command("followup").description("Show applications with follow-up due").action(() => {
5485
+ const db = getDatabase();
5486
+ const apps = listFollowUpDue(db);
5487
+ if (!apps.length) {
5488
+ console.log(chalk.green(`
5489
+ No follow-ups due.
5490
+ `));
5491
+ return;
5492
+ }
5493
+ console.log(chalk.yellow(`
5494
+ ${apps.length} application(s) need follow-up:
5495
+ `));
5496
+ renderTable(["Program", "Type", "Status", "Follow-up"], apps.map((a) => ({
5497
+ Program: a.program_name,
5498
+ Type: a.type,
5499
+ Status: a.status,
5500
+ "Follow-up": a.follow_up_date ? a.follow_up_date.slice(0, 10) : ""
5501
+ })));
5502
+ console.log();
5503
+ });
5504
+ program.command("seed").description("Seed demo contacts, companies, and relationships").option("--demo", "Seed demo data (professional services example)").option("--clear", "Clear existing demo data first").action(async (opts) => {
5505
+ if (!opts.demo) {
5506
+ console.log("Use --demo flag to seed demo data");
5507
+ return;
5508
+ }
5509
+ const db = getDatabase();
5510
+ console.log(chalk.cyan("Seeding demo contacts..."));
5511
+ const kpmg = createCompany({ name: "KPMG Romania", industry: "Accounting", description: "Romanian accounting, tax, and payroll firm" }, db);
5512
+ const escalon = createCompany({ name: "Escalon Services", industry: "Tax & Finance", description: "US tax preparation for multi-entity businesses", domain: "escalon.services" }, db);
5513
+ const revisionLegal = createCompany({ name: "Revision Legal PLLC", industry: "Legal", description: "Trademark and IP law firm", domain: "revisionlegal.com" }, db);
5514
+ createCompany({ name: "RAW Financial", industry: "Tax & Finance", description: "US tax preparation for dissolution entities" }, db);
5515
+ const svb = createCompany({ name: "Silicon Valley Bank", industry: "Banking", domain: "svb.com" }, db);
5516
+ const hasnaInc = createCompany({ name: "Hasna, Inc.", is_owned_entity: true, entity_type: "operating", industry: "AI/Software", description: "Primary US operating entity (C-Corp, Delaware)" }, db);
5517
+ const beepMedia = createCompany({ name: "Beep Media International LLC", is_owned_entity: true, entity_type: "operating", industry: "Media/Technology" }, db);
5518
+ createCompany({ name: "Hasna Global SRL", is_owned_entity: true, entity_type: "operating", industry: "AI/Software", description: "Romanian operating entity" }, db);
5519
+ const alina = createContact({ display_name: "Alina Turlea", first_name: "Alina", last_name: "Turlea", job_title: "Tax Consultant", emails: [{ address: "alinaturlea@kpmg.com", type: "work", is_primary: true }], source: "manual" });
5520
+ const lucia = createContact({ display_name: "Lucia Grecu", first_name: "Lucia", last_name: "Grecu", job_title: "Compliance & Billing", emails: [{ address: "lsipos@kpmg.com", type: "work", is_primary: true }], source: "manual" });
5521
+ const elizabeth = createContact({ display_name: "Elizabeth Robles", first_name: "Elizabeth", last_name: "Robles", job_title: "Tax Manager", emails: [{ address: "elizabeth.robles@escalon.services", type: "work", is_primary: true }], source: "manual" });
5522
+ const drew = createContact({ display_name: "Andrew Jurgensen", first_name: "Andrew", last_name: "Jurgensen", job_title: "Partner", emails: [{ address: "drew@revisionlegal.com", type: "work", is_primary: true }], source: "manual" });
5523
+ const donna = createContact({ display_name: "Donna Yang", first_name: "Donna", last_name: "Yang", job_title: "Relationship Manager", emails: [{ address: "DYang@svb.com", type: "work", is_primary: true }], source: "manual" });
5524
+ createCompanyRelationship({ contact_id: alina.id, company_id: kpmg.id, relationship_type: "accountant", is_primary: true }, db);
5525
+ createCompanyRelationship({ contact_id: lucia.id, company_id: kpmg.id, relationship_type: "accountant" }, db);
5526
+ createCompanyRelationship({ contact_id: elizabeth.id, company_id: escalon.id, relationship_type: "tax_preparer", is_primary: true }, db);
5527
+ createCompanyRelationship({ contact_id: drew.id, company_id: revisionLegal.id, relationship_type: "attorney", is_primary: true }, db);
5528
+ createCompanyRelationship({ contact_id: donna.id, company_id: svb.id, relationship_type: "bank_manager", is_primary: true }, db);
5529
+ createCompanyRelationship({ contact_id: alina.id, company_id: hasnaInc.id, relationship_type: "tax_preparer", is_primary: true }, db);
5530
+ createCompanyRelationship({ contact_id: alina.id, company_id: beepMedia.id, relationship_type: "tax_preparer", is_primary: true }, db);
5531
+ console.log(chalk.green(`\u2713 Seeded: 5 companies, 3 owned entities, 5 key contacts`));
5532
+ console.log(chalk.gray(" Try: contacts entities list"));
5533
+ console.log(chalk.gray(" Try: contacts workload " + alina.id));
5534
+ });
4658
5535
  program.parse(process.argv);