@startanaicompany/crm 2.7.0 → 2.10.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/index.js +77 -2
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -398,6 +398,10 @@ leadsCmd
|
|
|
398
398
|
.option('--source <source>', 'Source: api|import|referral|linkedin|cold_email|website|inbound|partner|event|other', 'api')
|
|
399
399
|
.option('--deal-value <amount>', 'Deal value (numeric)')
|
|
400
400
|
.option('--pipeline-stage <stage>', 'Pipeline stage: new|prospecting|discovery|qualified|demo_scheduled|demo_completed|proposal_sent|negotiation|contract_sent|closed_won|closed_lost|no_decision|dormant')
|
|
401
|
+
.option('--score <n>', 'Lead score (0-100 integer). Conventions: 0-19 Cold, 20-39 Warm, 40-59 Developing, 60-79 Qualified, 80-100 Hot')
|
|
402
|
+
.option('--score-reason <text>', 'Score reason/explanation')
|
|
403
|
+
.option('--close-probability <n>', 'Close probability 0-100 integer')
|
|
404
|
+
.option('--close-reason <reason>', `Close reason (${['price','timing','competition','no_budget','no_decision','fit','other'].join('|')})`)
|
|
401
405
|
.option('--notes <notes>', 'Notes')
|
|
402
406
|
.option('--assigned-to <assignedTo>', 'Assigned to')
|
|
403
407
|
.option('--tag <tag>', 'Tag (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
@@ -419,6 +423,11 @@ leadsCmd
|
|
|
419
423
|
source: opts.source,
|
|
420
424
|
...(opts.dealValue !== undefined && { deal_value: parseFloat(opts.dealValue) }),
|
|
421
425
|
...(opts.pipelineStage && { pipeline_stage: opts.pipelineStage }),
|
|
426
|
+
// --score 0 is valid — use !== undefined not falsy check
|
|
427
|
+
...(opts.score !== undefined && { score: parseInt(opts.score) }),
|
|
428
|
+
...(opts.scoreReason && { score_reason: opts.scoreReason }),
|
|
429
|
+
...(opts.closeProbability !== undefined && { close_probability: parseInt(opts.closeProbability) }),
|
|
430
|
+
...(opts.closeReason && { close_reason: opts.closeReason }),
|
|
422
431
|
...(opts.notes && { notes: opts.notes }),
|
|
423
432
|
...(opts.assignedTo && { assigned_to: opts.assignedTo }),
|
|
424
433
|
...(opts.tag.length > 0 && { tags: opts.tag }),
|
|
@@ -444,8 +453,13 @@ leadsCmd
|
|
|
444
453
|
.option('--tag <tag>', 'Filter by tag — repeatable, AND logic', (v, prev) => prev.concat([v]), [])
|
|
445
454
|
.option('--api-key-id <id>', 'Filter by the API key ID that created the lead')
|
|
446
455
|
.option('--self', 'Filter leads created by the current API key (shorthand for --api-key-id self)')
|
|
447
|
-
.option('--sort-by <field>', 'Sort field: created_at|updated_at|deal_value|name|company|last_activity_at|status_changed_at')
|
|
456
|
+
.option('--sort-by <field>', 'Sort field: created_at|updated_at|deal_value|name|company|last_activity_at|status_changed_at|score')
|
|
448
457
|
.option('--order <dir>', 'Sort direction: asc|desc', 'desc')
|
|
458
|
+
.option('--score-min <n>', 'Filter leads with score >= n (0-100)')
|
|
459
|
+
.option('--score-max <n>', 'Filter leads with score <= n (0-100)')
|
|
460
|
+
.option('--close-probability-min <n>', 'Filter leads with close_probability >= n')
|
|
461
|
+
.option('--close-probability-max <n>', 'Filter leads with close_probability <= n')
|
|
462
|
+
.option('--not-contacted-since-days <n>', 'Filter stale leads not contacted in N days (excludes converted/lost)')
|
|
449
463
|
.option('--created-after <date>', 'Filter leads created after this ISO8601 date (e.g. 2026-01-01T00:00:00Z)')
|
|
450
464
|
.option('--created-before <date>', 'Filter leads created before this ISO8601 date')
|
|
451
465
|
.option('--updated-after <date>', 'Filter leads updated after this ISO8601 date')
|
|
@@ -467,6 +481,11 @@ leadsCmd
|
|
|
467
481
|
...(apiKeyIdFilter && { api_key_id: apiKeyIdFilter }),
|
|
468
482
|
...(opts.sortBy && { sort_by: opts.sortBy }),
|
|
469
483
|
...(opts.order && { order: opts.order }),
|
|
484
|
+
...(opts.scoreMin !== undefined && { score_min: opts.scoreMin }),
|
|
485
|
+
...(opts.scoreMax !== undefined && { score_max: opts.scoreMax }),
|
|
486
|
+
...(opts.closeProbabilityMin !== undefined && { close_probability_min: opts.closeProbabilityMin }),
|
|
487
|
+
...(opts.closeProbabilityMax !== undefined && { close_probability_max: opts.closeProbabilityMax }),
|
|
488
|
+
...(opts.notContactedSinceDays !== undefined && { not_contacted_since_days: opts.notContactedSinceDays }),
|
|
470
489
|
...(opts.createdAfter && { created_after: opts.createdAfter }),
|
|
471
490
|
...(opts.createdBefore && { created_before: opts.createdBefore }),
|
|
472
491
|
...(opts.updatedAfter && { updated_after: opts.updatedAfter }),
|
|
@@ -514,9 +533,30 @@ leadsCmd
|
|
|
514
533
|
}
|
|
515
534
|
});
|
|
516
535
|
|
|
536
|
+
leadsCmd
|
|
537
|
+
.command('score')
|
|
538
|
+
.description('Set lead score (0-100). Conventions: 0-19 Cold, 20-39 Warm, 40-59 Developing, 60-79 Qualified, 80-100 Hot')
|
|
539
|
+
.requiredOption('--id <uuid>', 'Lead UUID')
|
|
540
|
+
.requiredOption('--score <n>', 'Score value (0-100 integer)')
|
|
541
|
+
.option('--reason <text>', 'Score reason/explanation')
|
|
542
|
+
.action(async (opts) => {
|
|
543
|
+
const globalOpts = program.opts();
|
|
544
|
+
const client = getClient(globalOpts);
|
|
545
|
+
const body = {
|
|
546
|
+
score: parseInt(opts.score),
|
|
547
|
+
...(opts.reason && { score_reason: opts.reason })
|
|
548
|
+
};
|
|
549
|
+
try {
|
|
550
|
+
const res = await client.patch(`/leads/${opts.id}/score`, body);
|
|
551
|
+
printJSON(res.data);
|
|
552
|
+
} catch (err) {
|
|
553
|
+
handleError(err);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
517
557
|
leadsCmd
|
|
518
558
|
.command('get <id>')
|
|
519
|
-
.description('Get a single lead by ID')
|
|
559
|
+
.description('Get a single lead by ID (includes activity_summary)')
|
|
520
560
|
.action(async (id) => {
|
|
521
561
|
const globalOpts = program.opts();
|
|
522
562
|
const client = getClient(globalOpts);
|
|
@@ -1319,7 +1359,42 @@ emailsCmd
|
|
|
1319
1359
|
}
|
|
1320
1360
|
});
|
|
1321
1361
|
|
|
1362
|
+
emailsCmd
|
|
1363
|
+
.command('sync')
|
|
1364
|
+
.description('Sync Gmail inbox into CRM (deduped by Gmail message ID)')
|
|
1365
|
+
.option('--max-results <n>', 'Max number of messages to fetch (default 100, max 500)', '100')
|
|
1366
|
+
.action(async (opts) => {
|
|
1367
|
+
const globalOpts = program.opts();
|
|
1368
|
+
const client = getClient(globalOpts);
|
|
1369
|
+
try {
|
|
1370
|
+
const res = await client.post('/emails/sync', { max_results: parseInt(opts.maxResults) || 100 });
|
|
1371
|
+
printJSON(res.data);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
handleError(err);
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1322
1376
|
|
|
1377
|
+
emailsCmd
|
|
1378
|
+
.command('send')
|
|
1379
|
+
.description('Send an email via Gmail (rate-limited: 1/30min, 30/day per workspace)')
|
|
1380
|
+
.requiredOption('--to <email>', 'Recipient email address')
|
|
1381
|
+
.requiredOption('--subject <subject>', 'Email subject')
|
|
1382
|
+
.requiredOption('--body <text>', 'Email body text')
|
|
1383
|
+
.option('--lead-id <id>', 'Link sent email to a lead ID')
|
|
1384
|
+
.option('--dry-run', 'Validate only — do not send or consume rate limit quota')
|
|
1385
|
+
.action(async (opts) => {
|
|
1386
|
+
const globalOpts = program.opts();
|
|
1387
|
+
const client = getClient(globalOpts);
|
|
1388
|
+
try {
|
|
1389
|
+
const body = { to: opts.to, subject: opts.subject, body: opts.body };
|
|
1390
|
+
if (opts.leadId) body.lead_id = opts.leadId;
|
|
1391
|
+
if (opts.dryRun) body.dry_run = true;
|
|
1392
|
+
const res = await client.post('/emails/send', body);
|
|
1393
|
+
printJSON(res.data);
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
handleError(err);
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1323
1398
|
|
|
1324
1399
|
// ============================================================
|
|
1325
1400
|
// QUOTES commands
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startanaicompany/crm",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI-first CRM CLI
|
|
3
|
+
"version": "2.10.0",
|
|
4
|
+
"description": "AI-first CRM CLI — manage leads and API keys from the terminal",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"saac_crm": "./index.js"
|