@startanaicompany/crm 2.14.0 → 2.15.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 (2) hide show
  1. package/index.js +129 -6
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1120,6 +1120,38 @@ leadsCmd
1120
1120
  } catch (err) { handleError(err); }
1121
1121
  });
1122
1122
 
1123
+ // Sprint 52 T2: leads escalate + leads unassign
1124
+ leadsCmd
1125
+ .command('escalate <lead-id>')
1126
+ .description('Manually escalate a lead — sets escalated=true and fires lead.escalated webhook (Sprint 52 T2)')
1127
+ .option('--reason <text>', 'Reason for escalation')
1128
+ .option('--to <agent>', 'Reassign to this agent/user on escalation')
1129
+ .action(async (leadId, opts) => {
1130
+ const globalOpts = program.opts();
1131
+ const client = getClient(globalOpts);
1132
+ try {
1133
+ const body = {};
1134
+ if (opts.reason) body.reason = opts.reason;
1135
+ if (opts.to) body.to = opts.to;
1136
+ const res = await client.post(`/leads/${leadId}/escalate`, body);
1137
+ console.log(`Lead ${leadId} escalated.`);
1138
+ printJSON(res.data);
1139
+ } catch (err) { handleError(err); }
1140
+ });
1141
+
1142
+ leadsCmd
1143
+ .command('unassign <lead-id>')
1144
+ .description('Remove assignment from a lead (sets assigned_to to null) (Sprint 52 T2)')
1145
+ .action(async (leadId) => {
1146
+ const globalOpts = program.opts();
1147
+ const client = getClient(globalOpts);
1148
+ try {
1149
+ const res = await client.patch(`/leads/${leadId}`, { assigned_to: null });
1150
+ console.log(`Lead ${leadId} unassigned.`);
1151
+ printJSON(res.data);
1152
+ } catch (err) { handleError(err); }
1153
+ });
1154
+
1123
1155
  // Sprint 51 T3: leads export / leads import
1124
1156
  leadsCmd
1125
1157
  .command('export')
@@ -1187,11 +1219,14 @@ const contactsCmd = program
1187
1219
  contactsCmd
1188
1220
  .command('create')
1189
1221
  .description('Create a new contact')
1190
- .requiredOption('--first-name <name>', 'First name')
1222
+ .option('--name <name>', 'Full name (alias for --first-name)')
1223
+ .option('--first-name <name>', 'First name')
1191
1224
  .option('--last-name <name>', 'Last name')
1192
- .option('--email <email>', 'Email address (unique)')
1225
+ .requiredOption('--email <email>', 'Email address (unique)')
1193
1226
  .option('--phone <phone>', 'Phone number')
1194
- .option('--company <company>', 'Company name')
1227
+ .option('--company <company>', 'Company name (text)')
1228
+ .option('--company-id <uuid>', 'Company UUID (links to companies table)')
1229
+ .option('--lead-id <uuid>', 'Lead UUID to link to this contact on creation')
1195
1230
  .option('--title <title>', 'Job title')
1196
1231
  .option('--tags <tags>', 'Comma-separated tags')
1197
1232
  .option('--do-not-contact', 'Mark as do not contact')
@@ -1203,11 +1238,14 @@ contactsCmd
1203
1238
  const client = getClient(globalOpts, agentName);
1204
1239
  try {
1205
1240
  const body = {
1206
- first_name: opts.firstName,
1241
+ name: opts.name || opts.firstName,
1242
+ first_name: opts.firstName || opts.name,
1207
1243
  last_name: opts.lastName,
1208
1244
  email: opts.email,
1209
1245
  phone: opts.phone,
1210
1246
  company: opts.company,
1247
+ company_id: opts.companyId,
1248
+ lead_id: opts.leadId,
1211
1249
  title: opts.title,
1212
1250
  notes: opts.notes,
1213
1251
  do_not_contact: opts.doNotContact === true,
@@ -1224,8 +1262,10 @@ contactsCmd
1224
1262
  .command('list')
1225
1263
  .description('List contacts')
1226
1264
  .option('--email <email>', 'Filter by email (partial match)')
1227
- .option('--company <company>', 'Filter by company (partial match)')
1265
+ .option('--company <company>', 'Filter by company name (partial match)')
1266
+ .option('--company-id <uuid>', 'Filter by company UUID')
1228
1267
  .option('--tag <tag>', 'Filter by tag')
1268
+ .option('--limit <n>', 'Max results per page (default 20)')
1229
1269
  .option('--page <n>', 'Page number', '1')
1230
1270
  .option('--per-page <n>', 'Results per page', '20')
1231
1271
  .action(async (opts) => {
@@ -1233,8 +1273,10 @@ contactsCmd
1233
1273
  const client = getClient(globalOpts);
1234
1274
  try {
1235
1275
  const params = { page: opts.page, per_page: opts.perPage };
1276
+ if (opts.limit) params.limit = opts.limit;
1236
1277
  if (opts.email) params.email = opts.email;
1237
1278
  if (opts.company) params.company = opts.company;
1279
+ if (opts.companyId) params.company_id = opts.companyId;
1238
1280
  if (opts.tag) params.tag = opts.tag;
1239
1281
  const res = await client.get('/contacts', { params });
1240
1282
  printJSON(res.data);
@@ -1260,11 +1302,13 @@ contactsCmd
1260
1302
  contactsCmd
1261
1303
  .command('update <id>')
1262
1304
  .description('Update a contact')
1305
+ .option('--name <name>', 'Full name (alias for --first-name)')
1263
1306
  .option('--first-name <name>', 'First name')
1264
1307
  .option('--last-name <name>', 'Last name')
1265
1308
  .option('--email <email>', 'Email address')
1266
1309
  .option('--phone <phone>', 'Phone number')
1267
- .option('--company <company>', 'Company name')
1310
+ .option('--company <company>', 'Company name (text)')
1311
+ .option('--company-id <uuid>', 'Company UUID (links to companies table)')
1268
1312
  .option('--title <title>', 'Job title')
1269
1313
  .option('--role <role>', 'Role: champion|economic_buyer|technical_buyer|gatekeeper|influencer|end_user')
1270
1314
  .option('--tags <tags>', 'Comma-separated tags')
@@ -1274,11 +1318,13 @@ contactsCmd
1274
1318
  const client = getClient(globalOpts);
1275
1319
  try {
1276
1320
  const body = {};
1321
+ if (opts.name !== undefined) body.name = opts.name;
1277
1322
  if (opts.firstName !== undefined) body.first_name = opts.firstName;
1278
1323
  if (opts.lastName !== undefined) body.last_name = opts.lastName;
1279
1324
  if (opts.email !== undefined) body.email = opts.email;
1280
1325
  if (opts.phone !== undefined) body.phone = opts.phone;
1281
1326
  if (opts.company !== undefined) body.company = opts.company;
1327
+ if (opts.companyId !== undefined) body.company_id = opts.companyId;
1282
1328
  if (opts.title !== undefined) body.title = opts.title;
1283
1329
  if (opts.role !== undefined) body.role = opts.role;
1284
1330
  if (opts.tags !== undefined) body.tags = opts.tags.split(',').map(t => t.trim());
@@ -3442,4 +3488,81 @@ gdprCmd
3442
3488
  }
3443
3489
  });
3444
3490
 
3491
+ // ============================================================
3492
+ // REPORTS COMMANDS — Sprint 52 T3
3493
+ // ============================================================
3494
+ const reportsCmd = program.command('reports').description('Manage and run saved CRM reports (Sprint 52 T3)');
3495
+
3496
+ reportsCmd
3497
+ .command('list')
3498
+ .description('List all saved reports')
3499
+ .action(async () => {
3500
+ const globalOpts = program.opts();
3501
+ const client = getClient(globalOpts);
3502
+ try {
3503
+ const res = await client.get('/reports');
3504
+ const reports = res.data.data || res.data;
3505
+ if (!reports || reports.length === 0) { console.log('No reports found.'); return; }
3506
+ reports.forEach(r => console.log(`[${r.id}] ${r.name} — type: ${r.dimension}/${r.metric}, period: ${r.period}`));
3507
+ } catch (err) { handleError(err); }
3508
+ });
3509
+
3510
+ reportsCmd
3511
+ .command('create')
3512
+ .description('Create and save a report definition')
3513
+ .requiredOption('--name <name>', 'Report name')
3514
+ .option('--type <type>', 'Report type: leads (count by stage) | pipeline (deal value by pipeline) | revenue (deal value by stage)', 'leads')
3515
+ .option('--period <period>', 'Time period: 7d | 30d | 90d | 365d | all', '30d')
3516
+ .option('--filter-status <status>', 'Filter leads by status')
3517
+ .option('--filter-stage <stage>', 'Filter leads by pipeline stage')
3518
+ .option('--chart-type <type>', 'Chart type: bar | line | pie | table', 'bar')
3519
+ .action(async (opts) => {
3520
+ const globalOpts = program.opts();
3521
+ const client = getClient(globalOpts);
3522
+ try {
3523
+ const body = {
3524
+ name: opts.name,
3525
+ type: opts.type,
3526
+ period: opts.period,
3527
+ chart_type: opts.chartType,
3528
+ };
3529
+ if (opts.filterStatus) body.filter_status = opts.filterStatus;
3530
+ if (opts.filterStage) body.filter_stage = opts.filterStage;
3531
+ const res = await client.post('/reports', body);
3532
+ const r = res.data.data || res.data;
3533
+ console.log(`Report created: [${r.id}] ${r.name}`);
3534
+ printJSON(res.data);
3535
+ } catch (err) { handleError(err); }
3536
+ });
3537
+
3538
+ reportsCmd
3539
+ .command('run <report-id>')
3540
+ .description('Run a saved report and display or save results')
3541
+ .option('--output <file>', 'Save results to this JSON file')
3542
+ .action(async (reportId, opts) => {
3543
+ const globalOpts = program.opts();
3544
+ const client = getClient(globalOpts);
3545
+ try {
3546
+ const res = await client.get(`/reports/${reportId}/run`);
3547
+ if (opts.output) {
3548
+ fs.writeFileSync(opts.output, JSON.stringify(res.data, null, 2));
3549
+ console.log(`Report results saved to ${opts.output}`);
3550
+ } else {
3551
+ printJSON(res.data);
3552
+ }
3553
+ } catch (err) { handleError(err); }
3554
+ });
3555
+
3556
+ reportsCmd
3557
+ .command('delete <report-id>')
3558
+ .description('Delete a saved report')
3559
+ .action(async (reportId) => {
3560
+ const globalOpts = program.opts();
3561
+ const client = getClient(globalOpts);
3562
+ try {
3563
+ await client.delete(`/reports/${reportId}`);
3564
+ console.log(`Report ${reportId} deleted.`);
3565
+ } catch (err) { handleError(err); }
3566
+ });
3567
+
3445
3568
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startanaicompany/crm",
3
- "version": "2.14.0",
3
+ "version": "2.15.0",
4
4
  "description": "AI-first CRM CLI — manage leads and API keys from the terminal",
5
5
  "main": "index.js",
6
6
  "bin": {