fullstackgtm 0.43.0 → 0.45.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/CHANGELOG.md +378 -1
- package/README.md +37 -14
- package/dist/audit.d.ts +3 -1
- package/dist/audit.js +29 -2
- package/dist/cli/audit.d.ts +15 -0
- package/dist/cli/audit.js +392 -0
- package/dist/cli/auth.d.ts +80 -0
- package/dist/cli/auth.js +500 -0
- package/dist/cli/call.d.ts +1 -0
- package/dist/cli/call.js +373 -0
- package/dist/cli/capabilities.d.ts +30 -0
- package/dist/cli/capabilities.js +197 -0
- package/dist/cli/draft.d.ts +7 -0
- package/dist/cli/draft.js +87 -0
- package/dist/cli/enrich.d.ts +8 -0
- package/dist/cli/enrich.js +788 -0
- package/dist/cli/fix.d.ts +33 -0
- package/dist/cli/fix.js +344 -0
- package/dist/cli/help.d.ts +25 -0
- package/dist/cli/help.js +609 -0
- package/dist/cli/icp.d.ts +7 -0
- package/dist/cli/icp.js +229 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +58 -0
- package/dist/cli/market.d.ts +1 -0
- package/dist/cli/market.js +391 -0
- package/dist/cli/plans.d.ts +5 -0
- package/dist/cli/plans.js +454 -0
- package/dist/cli/schedule.d.ts +8 -0
- package/dist/cli/schedule.js +479 -0
- package/dist/cli/shared.d.ts +70 -0
- package/dist/cli/shared.js +331 -0
- package/dist/cli/signals.d.ts +7 -0
- package/dist/cli/signals.js +412 -0
- package/dist/cli/suggest.d.ts +49 -0
- package/dist/cli/suggest.js +135 -0
- package/dist/cli/tam.d.ts +9 -0
- package/dist/cli/tam.js +387 -0
- package/dist/cli/ui.d.ts +121 -0
- package/dist/cli/ui.js +375 -0
- package/dist/cli.d.ts +1 -57
- package/dist/cli.js +104 -4556
- package/dist/connector.d.ts +15 -0
- package/dist/connector.js +35 -0
- package/dist/connectors/hubspot.d.ts +3 -1
- package/dist/connectors/hubspot.js +10 -3
- package/dist/connectors/outboxChannel.d.ts +64 -0
- package/dist/connectors/outboxChannel.js +170 -0
- package/dist/connectors/prospectSources.d.ts +22 -0
- package/dist/connectors/prospectSources.js +43 -0
- package/dist/connectors/salesforce.d.ts +3 -1
- package/dist/connectors/salesforce.js +12 -5
- package/dist/connectors/signalSources.d.ts +105 -0
- package/dist/connectors/signalSources.js +264 -0
- package/dist/connectors/theirstack.d.ts +84 -0
- package/dist/connectors/theirstack.js +125 -0
- package/dist/icp.d.ts +52 -4
- package/dist/icp.js +112 -35
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init.js +3 -0
- package/dist/judge.d.ts +2 -0
- package/dist/judge.js +6 -0
- package/dist/judgeEval.d.ts +7 -0
- package/dist/judgeEval.js +8 -1
- package/dist/marketClassify.d.ts +2 -0
- package/dist/marketClassify.js +7 -1
- package/dist/mcp-bin.js +2 -2
- package/dist/mcp.js +259 -166
- package/dist/runReport.d.ts +26 -0
- package/dist/runReport.js +15 -0
- package/dist/schedule.d.ts +80 -2
- package/dist/schedule.js +272 -5
- package/dist/signals.d.ts +54 -0
- package/dist/signals.js +64 -0
- package/dist/spoolFiles.d.ts +44 -0
- package/dist/spoolFiles.js +114 -0
- package/dist/tam.d.ts +225 -0
- package/dist/tam.js +470 -0
- package/dist/types.d.ts +11 -0
- package/docs/api.md +91 -11
- package/docs/outbox-format.md +92 -0
- package/docs/recipes.md +37 -0
- package/docs/roadmap-to-1.0.md +31 -1
- package/docs/signal-spool-format.md +175 -0
- package/docs/tam.md +195 -0
- package/llms.txt +83 -11
- package/package.json +1 -1
- package/skills/fullstackgtm/SKILL.md +4 -3
- package/src/audit.ts +27 -1
- package/src/cli/audit.ts +447 -0
- package/src/cli/auth.ts +549 -0
- package/src/cli/call.ts +398 -0
- package/src/cli/capabilities.ts +215 -0
- package/src/cli/draft.ts +101 -0
- package/src/cli/enrich.ts +885 -0
- package/src/cli/fix.ts +372 -0
- package/src/cli/help.ts +664 -0
- package/src/cli/icp.ts +265 -0
- package/src/cli/init.ts +65 -0
- package/src/cli/market.ts +423 -0
- package/src/cli/plans.ts +523 -0
- package/src/cli/schedule.ts +526 -0
- package/src/cli/shared.ts +375 -0
- package/src/cli/signals.ts +434 -0
- package/src/cli/suggest.ts +151 -0
- package/src/cli/tam.ts +435 -0
- package/src/cli/ui.ts +426 -0
- package/src/cli.ts +103 -5170
- package/src/connector.ts +46 -0
- package/src/connectors/hubspot.ts +14 -2
- package/src/connectors/outboxChannel.ts +202 -0
- package/src/connectors/prospectSources.ts +57 -0
- package/src/connectors/salesforce.ts +18 -2
- package/src/connectors/signalSources.ts +314 -0
- package/src/connectors/theirstack.ts +170 -0
- package/src/icp.ts +120 -34
- package/src/index.ts +32 -0
- package/src/init.ts +3 -0
- package/src/judge.ts +7 -0
- package/src/judgeEval.ts +8 -1
- package/src/marketClassify.ts +8 -1
- package/src/mcp-bin.ts +2 -2
- package/src/mcp.ts +130 -57
- package/src/runReport.ts +39 -0
- package/src/schedule.ts +330 -7
- package/src/signals.ts +90 -0
- package/src/spoolFiles.ts +116 -0
- package/src/tam.ts +654 -0
- package/src/types.ts +12 -0
package/dist/tam.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tam` — Total Addressable Market mapping.
|
|
3
|
+
*
|
|
4
|
+
* Market mapping (`market`) answers "who else is in this category and where are
|
|
5
|
+
* the open fronts." TAM mapping answers a different, longer-horizon question:
|
|
6
|
+
* "how big is the reachable market for *my* ICP, and how far along am I in
|
|
7
|
+
* actually putting it in the CRM?"
|
|
8
|
+
*
|
|
9
|
+
* It is deliberately NOT a one-shot "$10B TAM" headline. The flow is iterative:
|
|
10
|
+
* 1. `tam estimate` — derive a defensible universe from the ICP: a real
|
|
11
|
+
* account count (a discovery provider's total-match for the ICP filter, or
|
|
12
|
+
* an explicit assumption), × ACV for the dollar figure, with buyers/account
|
|
13
|
+
* giving the *contact* population target. Citable cross-checks optional.
|
|
14
|
+
* 2. `tam populate` — schedule governed `enrich acquire --save` runs that chip
|
|
15
|
+
* away at the universe (plan-only; apply stays a separate human gate).
|
|
16
|
+
* 3. `tam status` / `tam report` — coverage over time: accounts/contacts/$
|
|
17
|
+
* in the CRM vs. the universe, what THIS campaign added since baseline, and
|
|
18
|
+
* an ETA at the current burn rate — so "it'll take a while" is quantified.
|
|
19
|
+
*
|
|
20
|
+
* This module is the pure, deterministic core (estimation + coverage + ETA math
|
|
21
|
+
* + a profile-scoped store). The CLI wires it to the ICP, the CRM snapshot, the
|
|
22
|
+
* discovery providers, and the scheduler.
|
|
23
|
+
*/
|
|
24
|
+
import type { CanonicalAccount, CanonicalGtmSnapshot } from "./types.ts";
|
|
25
|
+
/**
|
|
26
|
+
* ACV basis. `account` (per logo / contract — the standard TAM basis) makes
|
|
27
|
+
* TAM = accounts × ACV. `buyer` (per seat) makes TAM = contacts × ACV, for
|
|
28
|
+
* genuinely seat-priced products. Buyers/account always drives the *contact*
|
|
29
|
+
* population target regardless of basis.
|
|
30
|
+
*/
|
|
31
|
+
export type AcvBasis = "account" | "buyer";
|
|
32
|
+
/** A citable market-research figure that cross-checks the bottom-up estimate. */
|
|
33
|
+
export type TamCrossCheck = {
|
|
34
|
+
claim: string;
|
|
35
|
+
/** Parsed dollar value, when the claim is a $ figure — enables a direct compare. */
|
|
36
|
+
valueUsd?: number;
|
|
37
|
+
sourceUrl: string;
|
|
38
|
+
/** Verbatim snippet (same evidence posture as market signals). */
|
|
39
|
+
quote: string;
|
|
40
|
+
asOf?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Real CRM counts that "fill" the TAM — accounts with a domain + their contacts. */
|
|
43
|
+
export type TamCoverageCounts = {
|
|
44
|
+
accounts: number;
|
|
45
|
+
contacts: number;
|
|
46
|
+
};
|
|
47
|
+
export type TamUniverse = {
|
|
48
|
+
accounts: number;
|
|
49
|
+
/** Where the account count came from: "provider:explorium" or "assumption". */
|
|
50
|
+
accountsSource: string;
|
|
51
|
+
buyersPerAccount: number;
|
|
52
|
+
/** how buyersPerAccount was set: "crm:avg-contacts-per-account (N)" or "explicit" or "assumption:...". */
|
|
53
|
+
buyersSource: string;
|
|
54
|
+
/** accounts × buyersPerAccount — the contact population target. */
|
|
55
|
+
contacts: number;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The ICP criteria the TAM was estimated against — captured at estimate time so
|
|
59
|
+
* `status` can classify CRM accounts as in/out of THIS TAM. geo/technology aren't
|
|
60
|
+
* on a CanonicalAccount, so only `employeeBands`/`industries` are CRM-checkable;
|
|
61
|
+
* geo + technographic verification needs re-enrichment (`--reverify`, future).
|
|
62
|
+
*/
|
|
63
|
+
export type TamTargeting = {
|
|
64
|
+
geos?: string[];
|
|
65
|
+
employeeBands?: string[];
|
|
66
|
+
industries?: string[];
|
|
67
|
+
technologies?: string[];
|
|
68
|
+
};
|
|
69
|
+
export type TamModel = {
|
|
70
|
+
name: string;
|
|
71
|
+
icpName: string;
|
|
72
|
+
universe: TamUniverse;
|
|
73
|
+
/** the ICP filter this TAM was sized against (for coverage classification). */
|
|
74
|
+
targeting?: TamTargeting;
|
|
75
|
+
/** ACV must be REAL, never a fabricated default: `source` says where it came
|
|
76
|
+
* from ("explicit" from the operator, or "crm:closed-won (N deals, median)"). */
|
|
77
|
+
acv: {
|
|
78
|
+
basis: AcvBasis;
|
|
79
|
+
valueUsd: number;
|
|
80
|
+
source: string;
|
|
81
|
+
};
|
|
82
|
+
/** accounts × ACV (account basis) or contacts × ACV (buyer basis). */
|
|
83
|
+
tamUsd: number;
|
|
84
|
+
crossChecks: TamCrossCheck[];
|
|
85
|
+
/** CRM counts at estimate time, so `status` can attribute what the campaign added. */
|
|
86
|
+
baseline: TamCoverageCounts;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
};
|
|
89
|
+
/** CRM accounts classified against the TAM ICP — only the in-TAM bucket counts. */
|
|
90
|
+
export type TamClassified = {
|
|
91
|
+
/** matches every CRM-checkable criterion (no contradiction) — counts toward coverage. */
|
|
92
|
+
inTam: number;
|
|
93
|
+
/** fails a checkable criterion (wrong size/industry) — NOT in this market. */
|
|
94
|
+
outOfTam: number;
|
|
95
|
+
/** missing the data to classify — surfaced, never silently counted. */
|
|
96
|
+
unknown: number;
|
|
97
|
+
/** all domain-bearing CRM accounts (inTam + outOfTam + unknown). */
|
|
98
|
+
totalCrm: number;
|
|
99
|
+
/** which criteria were CRM-checkable, e.g. ["size","industry"]. */
|
|
100
|
+
criteria: string[];
|
|
101
|
+
};
|
|
102
|
+
export type TamCoverage = {
|
|
103
|
+
at: string;
|
|
104
|
+
universe: {
|
|
105
|
+
accounts: number;
|
|
106
|
+
contacts: number;
|
|
107
|
+
tamUsd: number;
|
|
108
|
+
};
|
|
109
|
+
inCrm: TamCoverageCounts;
|
|
110
|
+
/** current − baseline (floored at 0) — what's been added since the TAM was set. */
|
|
111
|
+
addedSinceBaseline: TamCoverageCounts;
|
|
112
|
+
/** in-TAM (or total when unclassified) / universe, capped at 1. */
|
|
113
|
+
accountCoverage: number;
|
|
114
|
+
contactCoverage: number;
|
|
115
|
+
/** accountCoverage × tamUsd. */
|
|
116
|
+
dollarCovered: number;
|
|
117
|
+
/** present when the model carries targeting — the in/out/unknown breakdown. */
|
|
118
|
+
classified?: TamClassified;
|
|
119
|
+
/** max(estimate, inTam): the bottom-up count is a floor on the real universe. */
|
|
120
|
+
reconciledUniverse?: number;
|
|
121
|
+
/** inTam > the top-down estimate — the estimate was low; revise it UP. */
|
|
122
|
+
bottomUpExceedsEstimate?: boolean;
|
|
123
|
+
};
|
|
124
|
+
/** Linear projection of when the universe is filled, from the coverage timeline. */
|
|
125
|
+
export type TamEta = {
|
|
126
|
+
/** accounts added per day, measured across the timeline window. */
|
|
127
|
+
accountsPerDay: number;
|
|
128
|
+
accountsRemaining: number;
|
|
129
|
+
daysRemaining: number;
|
|
130
|
+
/** ISO date (YYYY-MM-DD) the account universe is projected to be covered. */
|
|
131
|
+
etaDate: string;
|
|
132
|
+
/** how the rate was measured — the timeline span used. */
|
|
133
|
+
basis: string;
|
|
134
|
+
};
|
|
135
|
+
export type EstimateTamInput = {
|
|
136
|
+
name: string;
|
|
137
|
+
icpName: string;
|
|
138
|
+
accounts: number;
|
|
139
|
+
accountsSource: string;
|
|
140
|
+
buyersPerAccount?: number;
|
|
141
|
+
buyersSource?: string;
|
|
142
|
+
targeting?: TamTargeting;
|
|
143
|
+
acv: {
|
|
144
|
+
basis?: AcvBasis;
|
|
145
|
+
valueUsd: number;
|
|
146
|
+
source?: string;
|
|
147
|
+
};
|
|
148
|
+
crossChecks?: TamCrossCheck[];
|
|
149
|
+
baseline: TamCoverageCounts;
|
|
150
|
+
createdAt: string;
|
|
151
|
+
};
|
|
152
|
+
export declare function estimateTam(input: EstimateTamInput): TamModel;
|
|
153
|
+
export type DerivedAcv = {
|
|
154
|
+
valueUsd: number;
|
|
155
|
+
dealCount: number;
|
|
156
|
+
meanUsd: number;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Derive a real ACV from the CRM's closed-won deals (median amount — robust to
|
|
160
|
+
* outliers). Returns null when there are no usable won deals, so the caller can
|
|
161
|
+
* refuse to fabricate a dollar TAM. The median is the headline; the mean is
|
|
162
|
+
* surfaced too so a skewed pipeline is visible.
|
|
163
|
+
*/
|
|
164
|
+
export declare function deriveAcvFromClosedWon(snapshot: CanonicalGtmSnapshot): DerivedAcv | null;
|
|
165
|
+
export type DerivedBuyers = {
|
|
166
|
+
value: number;
|
|
167
|
+
accountsSampled: number;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Derive buyers/account from the CRM: the average number of contacts on accounts
|
|
171
|
+
* that have at least one. A real proxy for the buying group, vs. a hardcoded
|
|
172
|
+
* guess. Returns null when no account has a contact.
|
|
173
|
+
*/
|
|
174
|
+
export declare function deriveBuyersPerAccount(snapshot: CanonicalGtmSnapshot): DerivedBuyers | null;
|
|
175
|
+
/**
|
|
176
|
+
* Count what "fills" the TAM from a CRM snapshot: accounts that carry a domain
|
|
177
|
+
* (real, signal-watchable records — a name-only stub doesn't count) and the
|
|
178
|
+
* contacts at them. Coverage is measured against these, so a TAM you've started
|
|
179
|
+
* filling reads truthfully even for accounts that pre-dated the campaign.
|
|
180
|
+
*/
|
|
181
|
+
export declare function coverageCountsFromSnapshot(snapshot: CanonicalGtmSnapshot): TamCoverageCounts;
|
|
182
|
+
export type AccountTamClass = "in" | "out" | "unknown";
|
|
183
|
+
/** The TAM criteria checkable from a CanonicalAccount — geo/technology are NOT on
|
|
184
|
+
* the record, so they need re-enrichment and are excluded here. */
|
|
185
|
+
export declare function crmCheckableCriteria(t: TamTargeting): string[];
|
|
186
|
+
/**
|
|
187
|
+
* Classify one CRM account against the TAM ICP, using only CRM-checkable criteria
|
|
188
|
+
* (size, industry). "out" if any criterion contradicts (wrong size/industry);
|
|
189
|
+
* "in" if at least one passes and none contradict; "unknown" if nothing could be
|
|
190
|
+
* evaluated (missing fields, or the TAM is defined only by geo/technology). Note:
|
|
191
|
+
* geo + "uses a CRM" can't be confirmed from a CanonicalAccount, so an "in" here
|
|
192
|
+
* means "matches what the CRM lets us check" — not a full technographic match.
|
|
193
|
+
*/
|
|
194
|
+
export declare function classifyAccount(account: CanonicalAccount, t: TamTargeting): AccountTamClass;
|
|
195
|
+
/**
|
|
196
|
+
* Coverage that classifies CRM accounts against THIS TAM's ICP — so accounts
|
|
197
|
+
* loaded from anywhere that DON'T match the target market (wrong size/industry)
|
|
198
|
+
* land in out-of-TAM/unknown and never inflate coverage. Reconciles bottom-up vs
|
|
199
|
+
* top-down: the in-TAM count is a FLOOR on the real universe (you've verified
|
|
200
|
+
* those exist), so when it exceeds the estimate, the estimate was low.
|
|
201
|
+
*/
|
|
202
|
+
export declare function classifyCoverage(model: TamModel, snapshot: CanonicalGtmSnapshot, at: string): TamCoverage;
|
|
203
|
+
/** The covered-account count a coverage reading represents: in-TAM when
|
|
204
|
+
* classified, else the raw CRM count (legacy). Used for ETA + reconciliation. */
|
|
205
|
+
export declare function coveredAccounts(c: TamCoverage): number;
|
|
206
|
+
export declare function computeCoverage(model: TamModel, inCrm: TamCoverageCounts, at: string): TamCoverage;
|
|
207
|
+
/**
|
|
208
|
+
* Project when the account universe is filled, from the coverage timeline. Uses
|
|
209
|
+
* the first and last readings that show real movement. Returns null when there
|
|
210
|
+
* isn't enough signal (fewer than two readings, no elapsed time, or a flat/
|
|
211
|
+
* negative burn rate) — an honest "can't project yet" rather than a fake date.
|
|
212
|
+
*/
|
|
213
|
+
export declare function projectEta(model: TamModel, timeline: TamCoverage[]): TamEta | null;
|
|
214
|
+
/** `$FSGTM_HOME[/profiles/<profile>]/tam/<name>/`. */
|
|
215
|
+
export declare function tamDir(name: string): string;
|
|
216
|
+
export declare function saveTamModel(model: TamModel): string;
|
|
217
|
+
export declare function loadTamModel(name: string): TamModel | null;
|
|
218
|
+
/** Append a coverage reading to the TAM's append-only timeline (0600). */
|
|
219
|
+
export declare function appendCoverage(name: string, coverage: TamCoverage): void;
|
|
220
|
+
/** Read the TAM's coverage timeline; tolerant of partial/corrupt lines. */
|
|
221
|
+
export declare function readCoverageTimeline(name: string): TamCoverage[];
|
|
222
|
+
/** A compact human summary for `tam status`. */
|
|
223
|
+
export declare function coverageToText(model: TamModel, coverage: TamCoverage, eta: TamEta | null): string;
|
|
224
|
+
/** A client-ready markdown deliverable for `tam report`. */
|
|
225
|
+
export declare function tamReportToMarkdown(model: TamModel, timeline: TamCoverage[], eta: TamEta | null): string;
|