fullstackgtm 0.44.0 → 0.46.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 (149) hide show
  1. package/CHANGELOG.md +293 -1
  2. package/INSTALL_FOR_AGENTS.md +13 -11
  3. package/README.md +45 -24
  4. package/dist/audit.d.ts +3 -1
  5. package/dist/audit.js +29 -2
  6. package/dist/backfill.d.ts +86 -0
  7. package/dist/backfill.js +251 -0
  8. package/dist/bin.js +4 -1
  9. package/dist/cli/audit.d.ts +15 -0
  10. package/dist/cli/audit.js +392 -0
  11. package/dist/cli/auth.d.ts +82 -0
  12. package/dist/cli/auth.js +607 -0
  13. package/dist/cli/backfill.d.ts +1 -0
  14. package/dist/cli/backfill.js +125 -0
  15. package/dist/cli/backfillRuns.d.ts +1 -0
  16. package/dist/cli/backfillRuns.js +187 -0
  17. package/dist/cli/call.d.ts +1 -0
  18. package/dist/cli/call.js +387 -0
  19. package/dist/cli/capabilities.d.ts +30 -0
  20. package/dist/cli/capabilities.js +197 -0
  21. package/dist/cli/draft.d.ts +7 -0
  22. package/dist/cli/draft.js +87 -0
  23. package/dist/cli/enrich.d.ts +8 -0
  24. package/dist/cli/enrich.js +806 -0
  25. package/dist/cli/fix.d.ts +33 -0
  26. package/dist/cli/fix.js +346 -0
  27. package/dist/cli/help.d.ts +25 -0
  28. package/dist/cli/help.js +646 -0
  29. package/dist/cli/icp.d.ts +7 -0
  30. package/dist/cli/icp.js +229 -0
  31. package/dist/cli/init.d.ts +7 -0
  32. package/dist/cli/init.js +58 -0
  33. package/dist/cli/market.d.ts +1 -0
  34. package/dist/cli/market.js +391 -0
  35. package/dist/cli/plans.d.ts +5 -0
  36. package/dist/cli/plans.js +456 -0
  37. package/dist/cli/schedule.d.ts +8 -0
  38. package/dist/cli/schedule.js +479 -0
  39. package/dist/cli/shared.d.ts +71 -0
  40. package/dist/cli/shared.js +342 -0
  41. package/dist/cli/signals.d.ts +7 -0
  42. package/dist/cli/signals.js +412 -0
  43. package/dist/cli/suggest.d.ts +49 -0
  44. package/dist/cli/suggest.js +135 -0
  45. package/dist/cli/tam.d.ts +9 -0
  46. package/dist/cli/tam.js +387 -0
  47. package/dist/cli/ui.d.ts +142 -0
  48. package/dist/cli/ui.js +427 -0
  49. package/dist/cli.d.ts +1 -57
  50. package/dist/cli.js +123 -5157
  51. package/dist/connector.d.ts +23 -0
  52. package/dist/connector.js +47 -0
  53. package/dist/connectors/hubspot.d.ts +10 -1
  54. package/dist/connectors/hubspot.js +244 -10
  55. package/dist/connectors/hubspotAuth.js +5 -1
  56. package/dist/connectors/linkedin.js +14 -14
  57. package/dist/connectors/salesforce.d.ts +10 -1
  58. package/dist/connectors/salesforce.js +39 -6
  59. package/dist/connectors/signalSources.js +7 -59
  60. package/dist/connectors/stripe.d.ts +37 -0
  61. package/dist/connectors/stripe.js +103 -31
  62. package/dist/enrich.d.ts +7 -0
  63. package/dist/enrich.js +7 -0
  64. package/dist/health.d.ts +11 -69
  65. package/dist/health.js +4 -134
  66. package/dist/healthScore.d.ts +71 -0
  67. package/dist/healthScore.js +143 -0
  68. package/dist/icp.d.ts +15 -11
  69. package/dist/icp.js +19 -36
  70. package/dist/index.d.ts +3 -1
  71. package/dist/index.js +3 -1
  72. package/dist/judge.d.ts +2 -0
  73. package/dist/judge.js +6 -0
  74. package/dist/llm.d.ts +29 -0
  75. package/dist/llm.js +206 -0
  76. package/dist/market.d.ts +39 -1
  77. package/dist/market.js +116 -44
  78. package/dist/marketClassify.d.ts +11 -1
  79. package/dist/marketClassify.js +17 -2
  80. package/dist/marketTaxonomy.d.ts +6 -1
  81. package/dist/marketTaxonomy.js +4 -2
  82. package/dist/mcp-bin.js +31 -2
  83. package/dist/mcp.js +372 -166
  84. package/dist/progress.d.ts +96 -0
  85. package/dist/progress.js +142 -0
  86. package/dist/runReport.d.ts +24 -0
  87. package/dist/runReport.js +139 -4
  88. package/dist/schedule.d.ts +80 -2
  89. package/dist/schedule.js +254 -1
  90. package/dist/spoolFiles.d.ts +44 -0
  91. package/dist/spoolFiles.js +114 -0
  92. package/dist/types.d.ts +29 -1
  93. package/docs/api.md +75 -8
  94. package/docs/architecture.md +2 -0
  95. package/docs/linkedin-connector-spec.md +1 -1
  96. package/docs/signal-spool-format.md +13 -0
  97. package/llms.txt +18 -9
  98. package/package.json +10 -3
  99. package/skills/fullstackgtm/SKILL.md +2 -1
  100. package/src/audit.ts +27 -1
  101. package/src/backfill.ts +340 -0
  102. package/src/bin.ts +5 -1
  103. package/src/cli/audit.ts +447 -0
  104. package/src/cli/auth.ts +669 -0
  105. package/src/cli/backfill.ts +156 -0
  106. package/src/cli/backfillRuns.ts +198 -0
  107. package/src/cli/call.ts +414 -0
  108. package/src/cli/capabilities.ts +215 -0
  109. package/src/cli/draft.ts +101 -0
  110. package/src/cli/enrich.ts +908 -0
  111. package/src/cli/fix.ts +373 -0
  112. package/src/cli/help.ts +702 -0
  113. package/src/cli/icp.ts +265 -0
  114. package/src/cli/init.ts +65 -0
  115. package/src/cli/market.ts +423 -0
  116. package/src/cli/plans.ts +524 -0
  117. package/src/cli/schedule.ts +526 -0
  118. package/src/cli/shared.ts +392 -0
  119. package/src/cli/signals.ts +434 -0
  120. package/src/cli/suggest.ts +151 -0
  121. package/src/cli/tam.ts +435 -0
  122. package/src/cli/ui.ts +497 -0
  123. package/src/cli.ts +122 -5855
  124. package/src/connector.ts +66 -0
  125. package/src/connectors/hubspot.ts +273 -9
  126. package/src/connectors/hubspotAuth.ts +5 -1
  127. package/src/connectors/linkedin.ts +14 -14
  128. package/src/connectors/salesforce.ts +51 -3
  129. package/src/connectors/signalSources.ts +7 -56
  130. package/src/connectors/stripe.ts +154 -34
  131. package/src/enrich.ts +13 -0
  132. package/src/health.ts +14 -213
  133. package/src/healthScore.ts +223 -0
  134. package/src/icp.ts +19 -35
  135. package/src/index.ts +28 -1
  136. package/src/judge.ts +7 -0
  137. package/src/llm.ts +239 -0
  138. package/src/market.ts +157 -44
  139. package/src/marketClassify.ts +26 -2
  140. package/src/marketTaxonomy.ts +10 -2
  141. package/src/mcp-bin.ts +34 -2
  142. package/src/mcp.ts +270 -63
  143. package/src/progress.ts +197 -0
  144. package/src/runReport.ts +159 -4
  145. package/src/schedule.ts +310 -3
  146. package/src/spoolFiles.ts +116 -0
  147. package/src/types.ts +32 -2
  148. package/docs/dx-punch-list.md +0 -87
  149. package/docs/roadmap-to-1.0.md +0 -211
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Backfill: Stripe paid invoices → closed-won CRM deals, through the normal
3
+ * governed plan/apply flow.
4
+ *
5
+ * Product assumption this encodes: billing (Stripe) is the revenue source
6
+ * of truth; the CRM should retroactively carry ONE closed-won deal per paid
7
+ * invoice (amount = invoice total, close date = paid date, associated to the
8
+ * customer's company, deduped by invoice id). This module only BUILDS the
9
+ * dry-run plan — nothing here touches a CRM. Writes happen exclusively via
10
+ * `apply` on operations approved through `plans approve`, where the HubSpot
11
+ * connector re-resolves each invoice id (resolve-first) and creates only on
12
+ * a confirmed miss.
13
+ */
14
+ import type { CanonicalGtmSnapshot, PatchPlan } from "./types.ts";
15
+ import type { StripePaidInvoice } from "./connectors/stripe.ts";
16
+ export declare const DEFAULT_BACKFILL_MATCH_PROPERTY = "stripe_invoice_id";
17
+ export type StripeBackfillOptions = {
18
+ /** Pipeline id or case-insensitive label; absent = the portal's default pipeline. */
19
+ pipeline?: string;
20
+ /** Deal dedupe property stamped with the invoice id (default "stripe_invoice_id"). */
21
+ matchProperty?: string;
22
+ /** Source label recorded on payloads/evidence (default "stripe:invoices"). */
23
+ source?: string;
24
+ /**
25
+ * When a customer matches no CRM account, propose creating the account as
26
+ * part of this plan (default true). The account op is explicit and
27
+ * approval-gated like everything else; apply is resolve-first, so an
28
+ * account that appeared in the meantime is reused, not duplicated. False =
29
+ * the conservative original behavior: unmatched customers are reported
30
+ * only and their invoices produce no operations.
31
+ */
32
+ createMissingAccounts?: boolean;
33
+ /** Injectable clock for deterministic tests. */
34
+ now?: () => Date;
35
+ };
36
+ /** One invoice whose customer could not be matched to any CRM account. */
37
+ export type StripeBackfillUnmatched = {
38
+ invoiceId: string;
39
+ invoiceNumber?: string;
40
+ customerName?: string;
41
+ customerDomain?: string;
42
+ amountPaid: number;
43
+ currency?: string;
44
+ paidAt?: string;
45
+ };
46
+ /** One account this plan proposes to create for an unmatched Stripe customer. */
47
+ export type StripeBackfillProposedAccount = {
48
+ name: string;
49
+ domain?: string;
50
+ /** Paid invoices from this customer that the plan attaches to the account. */
51
+ invoiceCount: number;
52
+ };
53
+ export type StripeBackfillCounts = {
54
+ invoices: number;
55
+ /** create_record deal ops emitted. */
56
+ planned: number;
57
+ /** create_record account ops emitted for unmatched customers. */
58
+ accountsProposed: number;
59
+ /** Customers with no CRM match AND no usable name/domain — reported only. */
60
+ unmatched: number;
61
+ /** Plan-time prefilter: a deal with the same name already exists. */
62
+ alreadyInCrm: number;
63
+ };
64
+ export type StripeBackfillResult = {
65
+ plan: PatchPlan;
66
+ counts: StripeBackfillCounts;
67
+ unmatched: StripeBackfillUnmatched[];
68
+ proposedAccounts: StripeBackfillProposedAccount[];
69
+ };
70
+ /**
71
+ * Match each paid invoice's customer to a snapshot account (normalized domain
72
+ * first — the accurate key — then exact case-insensitive name) and emit one
73
+ * create_record deal op per invoice that has no deal yet.
74
+ *
75
+ * Unmatched customers (default): the plan ALSO proposes creating the account —
76
+ * one explicit, approval-gated account create_record op per new customer,
77
+ * with the customer's invoices attached as deals. Freemail billing domains
78
+ * (gmail/outlook/…) are never used as a company domain. A customer with no
79
+ * usable name or domain stays report-only. `createMissingAccounts: false`
80
+ * restores the original conservative behavior (unmatched = reported, no ops).
81
+ *
82
+ * The by-deal-name prefilter here is best-effort plan hygiene; the
83
+ * AUTHORITATIVE duplicate gate is the connector's apply-time resolve-first
84
+ * search on the invoice-id property (and resolve-by-domain/name for accounts).
85
+ */
86
+ export declare function buildStripeBackfillPlan(invoices: StripePaidInvoice[], snapshot: CanonicalGtmSnapshot, opts?: StripeBackfillOptions): StripeBackfillResult;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Backfill: Stripe paid invoices → closed-won CRM deals, through the normal
3
+ * governed plan/apply flow.
4
+ *
5
+ * Product assumption this encodes: billing (Stripe) is the revenue source
6
+ * of truth; the CRM should retroactively carry ONE closed-won deal per paid
7
+ * invoice (amount = invoice total, close date = paid date, associated to the
8
+ * customer's company, deduped by invoice id). This module only BUILDS the
9
+ * dry-run plan — nothing here touches a CRM. Writes happen exclusively via
10
+ * `apply` on operations approved through `plans approve`, where the HubSpot
11
+ * connector re-resolves each invoice id (resolve-first) and creates only on
12
+ * a confirmed miss.
13
+ */
14
+ import { normalizeDomain } from "./merge.js";
15
+ // Mirrors stableHash in rules.ts (FNV-1a); duplicated to keep backfill.ts
16
+ // importable without pulling the audit engine (the enrich.ts precedent).
17
+ function fnv1a(value) {
18
+ let hash = 0x811c9dc5;
19
+ for (let i = 0; i < value.length; i += 1) {
20
+ hash ^= value.charCodeAt(i);
21
+ hash = Math.imul(hash, 0x01000193);
22
+ }
23
+ return (hash >>> 0).toString(16).padStart(8, "0");
24
+ }
25
+ export const DEFAULT_BACKFILL_MATCH_PROPERTY = "stripe_invoice_id";
26
+ // Freemail domains never become a company domain: stamping "gmail.com" on an
27
+ // account (or resolving a company BY gmail.com) would collapse unrelated
28
+ // customers onto one record. Mirrors FREE_MAIL in connectors/signalSources.ts
29
+ // (duplicated to keep backfill.ts importable without connector code, the
30
+ // fnv1a precedent above).
31
+ const FREE_MAIL = new Set([
32
+ "gmail.com",
33
+ "yahoo.com",
34
+ "hotmail.com",
35
+ "outlook.com",
36
+ "icloud.com",
37
+ "aol.com",
38
+ "proton.me",
39
+ "protonmail.com",
40
+ ]);
41
+ /** `Invoice <number || id> — <customer name>` (the plan-time dedupe name). */
42
+ function backfillDealName(invoice, companyName) {
43
+ return `Invoice ${invoice.number ?? invoice.id} — ${invoice.customerName ?? companyName}`;
44
+ }
45
+ /**
46
+ * Match each paid invoice's customer to a snapshot account (normalized domain
47
+ * first — the accurate key — then exact case-insensitive name) and emit one
48
+ * create_record deal op per invoice that has no deal yet.
49
+ *
50
+ * Unmatched customers (default): the plan ALSO proposes creating the account —
51
+ * one explicit, approval-gated account create_record op per new customer,
52
+ * with the customer's invoices attached as deals. Freemail billing domains
53
+ * (gmail/outlook/…) are never used as a company domain. A customer with no
54
+ * usable name or domain stays report-only. `createMissingAccounts: false`
55
+ * restores the original conservative behavior (unmatched = reported, no ops).
56
+ *
57
+ * The by-deal-name prefilter here is best-effort plan hygiene; the
58
+ * AUTHORITATIVE duplicate gate is the connector's apply-time resolve-first
59
+ * search on the invoice-id property (and resolve-by-domain/name for accounts).
60
+ */
61
+ export function buildStripeBackfillPlan(invoices, snapshot, opts = {}) {
62
+ const nowIso = (opts.now ?? (() => new Date()))().toISOString();
63
+ const matchProperty = opts.matchProperty ?? DEFAULT_BACKFILL_MATCH_PROPERTY;
64
+ const source = opts.source ?? "stripe:invoices";
65
+ // Snapshot lookups. First occurrence wins on a duplicate key — the
66
+ // connector's own resolve/associate step handles ambiguity at apply time.
67
+ const accountsByDomain = new Map();
68
+ const accountsByName = new Map();
69
+ for (const account of snapshot.accounts ?? []) {
70
+ const domain = normalizeDomain(account.domain);
71
+ if (domain && !accountsByDomain.has(domain))
72
+ accountsByDomain.set(domain, account);
73
+ const name = account.name?.trim().toLowerCase();
74
+ if (name && !accountsByName.has(name))
75
+ accountsByName.set(name, account);
76
+ }
77
+ const existingDealNames = new Set((snapshot.deals ?? [])
78
+ .map((deal) => deal.name?.trim().toLowerCase())
79
+ .filter((name) => Boolean(name)));
80
+ const createMissingAccounts = opts.createMissingAccounts ?? true;
81
+ const operations = [];
82
+ const evidence = [];
83
+ const unmatched = [];
84
+ const proposedByKey = new Map();
85
+ const counts = {
86
+ invoices: invoices.length,
87
+ planned: 0,
88
+ accountsProposed: 0,
89
+ unmatched: 0,
90
+ alreadyInCrm: 0,
91
+ };
92
+ for (const invoice of invoices) {
93
+ // Match the customer to a CRM account: domain first, then exact name.
94
+ const domain = normalizeDomain(invoice.customerDomain);
95
+ let account = domain ? accountsByDomain.get(domain) : undefined;
96
+ if (!account && invoice.customerName) {
97
+ account = accountsByName.get(invoice.customerName.trim().toLowerCase());
98
+ }
99
+ // The company the deal will land on: the matched account, or (default) a
100
+ // new account this plan proposes for the unmatched customer.
101
+ let companyName = account?.name;
102
+ let companyDomain = normalizeDomain(account?.domain);
103
+ let accountIsNew = false;
104
+ if (!account && createMissingAccounts) {
105
+ const usableDomain = domain && !FREE_MAIL.has(domain) ? domain : undefined;
106
+ const name = invoice.customerName?.trim() || usableDomain;
107
+ if (name) {
108
+ companyName = name;
109
+ companyDomain = usableDomain;
110
+ accountIsNew = true;
111
+ }
112
+ }
113
+ if (!companyName) {
114
+ counts.unmatched += 1;
115
+ unmatched.push({
116
+ invoiceId: invoice.id,
117
+ invoiceNumber: invoice.number,
118
+ customerName: invoice.customerName,
119
+ customerDomain: invoice.customerDomain,
120
+ amountPaid: invoice.amountPaid,
121
+ currency: invoice.currency,
122
+ paidAt: invoice.paidAt,
123
+ });
124
+ continue;
125
+ }
126
+ // Plan-time dupe prefilter by deal name (best-effort; apply-time
127
+ // resolve-first on the invoice-id property is the authoritative gate).
128
+ const dealName = backfillDealName(invoice, companyName);
129
+ if (existingDealNames.has(dealName.trim().toLowerCase())) {
130
+ counts.alreadyInCrm += 1;
131
+ continue;
132
+ }
133
+ const invoiceLabel = invoice.number ?? invoice.id;
134
+ const amountLabel = `${invoice.amountPaid}${invoice.currency ? ` ${invoice.currency}` : ""}`;
135
+ const recordEvidence = {
136
+ id: `ev_bkf_${fnv1a(`${source}:${invoice.id}`)}`,
137
+ sourceSystem: "web",
138
+ sourceObjectType: "invoice",
139
+ sourceObjectId: invoice.id,
140
+ title: `Stripe paid invoice ${invoiceLabel}`,
141
+ text: `Stripe invoice ${invoiceLabel} (${invoice.id}) for ${invoice.customerName ?? companyName}: ` +
142
+ `${amountLabel} paid${invoice.paidAt ? ` on ${invoice.paidAt}` : ""}.`,
143
+ capturedAt: nowIso,
144
+ metadata: {
145
+ source,
146
+ invoiceId: invoice.id,
147
+ amountPaid: invoice.amountPaid,
148
+ currency: invoice.currency ?? null,
149
+ paidAt: invoice.paidAt ?? null,
150
+ customerId: invoice.customerId ?? null,
151
+ },
152
+ };
153
+ evidence.push(recordEvidence);
154
+ // One explicit account op per NEW customer (not per invoice): the reviewer
155
+ // sees exactly which companies this plan will create. Apply resolves by
156
+ // domain-then-name first, so a concurrently-created account is reused.
157
+ if (accountIsNew) {
158
+ const accountKey = (companyDomain ?? companyName).toLowerCase();
159
+ const existing = proposedByKey.get(accountKey);
160
+ if (existing) {
161
+ existing.invoiceCount += 1;
162
+ }
163
+ else {
164
+ proposedByKey.set(accountKey, {
165
+ name: companyName,
166
+ ...(companyDomain ? { domain: companyDomain } : {}),
167
+ invoiceCount: 1,
168
+ });
169
+ const accountPayload = {
170
+ properties: {
171
+ name: companyName,
172
+ ...(companyDomain ? { domain: companyDomain } : {}),
173
+ },
174
+ matchKey: "name",
175
+ matchValue: companyName,
176
+ source,
177
+ };
178
+ operations.push({
179
+ id: `op_bkf_${fnv1a(`${source}:account:${accountKey}`)}`,
180
+ objectType: "account",
181
+ objectId: `create:stripe:customer:${invoice.customerId ?? accountKey}`,
182
+ operation: "create_record",
183
+ beforeValue: null,
184
+ afterValue: accountPayload,
185
+ reason: `Stripe customer "${companyName}"${companyDomain ? ` (${companyDomain})` : ""} has paid ` +
186
+ `invoice(s) but no CRM account — create it so the backfilled deals have a home. ` +
187
+ `Resolve-first by ${companyDomain ? "domain, then " : ""}name at apply.`,
188
+ sourceRuleOrPolicy: `backfill:${source}`,
189
+ riskLevel: "medium",
190
+ approvalRequired: true,
191
+ rollback: "Archive the created company (it was net-new).",
192
+ evidenceIds: [recordEvidence.id],
193
+ });
194
+ counts.accountsProposed += 1;
195
+ }
196
+ }
197
+ // Prefer the matched ACCOUNT's own name/domain for the association so the
198
+ // connector's resolve-first company step lands on the matched record; for
199
+ // a new customer these are the proposed account's name/domain.
200
+ const payload = {
201
+ properties: {
202
+ dealname: dealName,
203
+ amount: String(invoice.amountPaid),
204
+ ...(invoice.paidAt ? { closedate: invoice.paidAt } : {}),
205
+ ...(invoice.description ? { description: invoice.description } : {}),
206
+ },
207
+ matchKey: matchProperty,
208
+ matchValue: invoice.id,
209
+ source,
210
+ dealStage: "closed_won",
211
+ ...(opts.pipeline ? { dealPipeline: opts.pipeline } : {}),
212
+ associateCompanyName: companyName,
213
+ ...(companyDomain ? { associateCompanyDomain: companyDomain } : {}),
214
+ };
215
+ operations.push({
216
+ id: `op_bkf_${fnv1a(`${source}:deal:${invoice.id}`)}`,
217
+ objectType: "deal",
218
+ objectId: `create:stripe:invoice:${invoice.id}`,
219
+ operation: "create_record",
220
+ beforeValue: null,
221
+ afterValue: payload,
222
+ reason: `Paid Stripe invoice ${invoiceLabel} (${amountLabel}, paid ${invoice.paidAt ?? "date unknown"}) ` +
223
+ `has no CRM deal — backfill as closed-won on ${accountIsNew ? "NEW account" : "account"} ` +
224
+ `"${companyName}"${accountIsNew ? " (created by this plan)" : ""} ` +
225
+ `(deduped by ${matchProperty}=${invoice.id}).`,
226
+ sourceRuleOrPolicy: `backfill:${source}`,
227
+ riskLevel: "medium",
228
+ approvalRequired: true,
229
+ rollback: "Archive the created deal (it was net-new).",
230
+ evidenceIds: [recordEvidence.id],
231
+ });
232
+ counts.planned += 1;
233
+ }
234
+ const plan = {
235
+ id: `patch_plan_backfill_${fnv1a(`${source}:${nowIso}:${invoices.map((invoice) => invoice.id).join(",")}`)}`,
236
+ title: `Backfill closed-won deals — ${source}`,
237
+ createdAt: nowIso,
238
+ status: operations.length > 0 ? "needs_approval" : "draft",
239
+ dryRun: true,
240
+ summary: `${counts.planned} closed-won deal(s) proposed from ${counts.invoices} paid Stripe invoice(s)` +
241
+ `${counts.accountsProposed > 0 ? `, plus ${counts.accountsProposed} new account(s) proposed for customers the CRM doesn't know` : ""} ` +
242
+ `(${counts.alreadyInCrm} already have a matching deal, ${counts.unmatched} ` +
243
+ `${createMissingAccounts ? "customer(s) with no usable name/domain" : "unmatched customer(s)"} reported only` +
244
+ `${createMissingAccounts ? "" : " — account creation disabled"}). ` +
245
+ `Deduped by ${matchProperty} = invoice id; apply re-checks resolve-first before creating.`,
246
+ findings: [],
247
+ evidence,
248
+ operations,
249
+ };
250
+ return { plan, counts, unmatched, proposedAccounts: [...proposedByKey.values()] };
251
+ }
package/dist/bin.js CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { runCli } from "./cli.js";
3
- import { flushRunReport } from "./runReport.js";
3
+ import { beginRunReport, flushRunReport } from "./runReport.js";
4
4
  const args = process.argv.slice(2);
5
5
  const startedAt = Date.now();
6
+ // Arm live progress heartbeats (paired CLIs stream long runs to the hosted
7
+ // app under the same clientRunId the final flush below will upsert).
8
+ beginRunReport(args, startedAt);
6
9
  runCli(args)
7
10
  .then(async () => {
8
11
  // exitCode 2 (audit findings / create-gate) or 1 (no value) = "partial":
@@ -0,0 +1,15 @@
1
+ export declare function snapshotCommand(args: string[]): Promise<void>;
2
+ export declare function audit(args: string[]): Promise<void>;
3
+ /**
4
+ * Roll up the active profile's health timeline (accrued by `audit --save`):
5
+ * current deterministic score, change since the last audit, and per-rule
6
+ * deltas. Read-only — it only reads `health.jsonl`, never re-audits.
7
+ */
8
+ export declare function healthCommand(args: string[]): void;
9
+ /**
10
+ * Render an audit as a client-facing deliverable. Same sources and audit
11
+ * options as `audit`; `--plan` instead renders an existing plan JSON without
12
+ * re-fetching (useful for a plan produced earlier or by another machine).
13
+ */
14
+ export declare function reportCommand(args: string[]): Promise<void>;
15
+ export declare function rulesCommand(args: string[]): Promise<void>;