fullstackgtm 0.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/CHANGELOG.md +381 -0
- package/INSTALL_FOR_AGENTS.md +87 -0
- package/LICENSE +202 -0
- package/README.md +230 -0
- package/dist/audit.d.ts +7 -0
- package/dist/audit.js +202 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +6 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.js +915 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +85 -0
- package/dist/connector.d.ts +30 -0
- package/dist/connector.js +94 -0
- package/dist/connectors/hubspot.d.ts +20 -0
- package/dist/connectors/hubspot.js +409 -0
- package/dist/connectors/hubspotAuth.d.ts +42 -0
- package/dist/connectors/hubspotAuth.js +189 -0
- package/dist/connectors/salesforce.d.ts +26 -0
- package/dist/connectors/salesforce.js +318 -0
- package/dist/connectors/salesforceAuth.d.ts +44 -0
- package/dist/connectors/salesforceAuth.js +120 -0
- package/dist/connectors/stripe.d.ts +27 -0
- package/dist/connectors/stripe.js +176 -0
- package/dist/credentials.d.ts +75 -0
- package/dist/credentials.js +197 -0
- package/dist/demo.d.ts +20 -0
- package/dist/demo.js +169 -0
- package/dist/diff.d.ts +46 -0
- package/dist/diff.js +107 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +109 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +17 -0
- package/dist/mappings.d.ts +8 -0
- package/dist/mappings.js +123 -0
- package/dist/mcp-bin.d.ts +2 -0
- package/dist/mcp-bin.js +33 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +140 -0
- package/dist/merge.d.ts +48 -0
- package/dist/merge.js +145 -0
- package/dist/planStore.d.ts +31 -0
- package/dist/planStore.js +116 -0
- package/dist/rules.d.ts +24 -0
- package/dist/rules.js +512 -0
- package/dist/sampleData.d.ts +2 -0
- package/dist/sampleData.js +115 -0
- package/dist/types.d.ts +294 -0
- package/dist/types.js +8 -0
- package/docs/api.md +72 -0
- package/docs/roadmap-to-1.0.md +121 -0
- package/llms.txt +25 -0
- package/package.json +76 -0
- package/src/audit.ts +242 -0
- package/src/bin.ts +7 -0
- package/src/cli.ts +1042 -0
- package/src/config.ts +113 -0
- package/src/connector.ts +140 -0
- package/src/connectors/hubspot.ts +528 -0
- package/src/connectors/hubspotAuth.ts +246 -0
- package/src/connectors/salesforce.ts +420 -0
- package/src/connectors/salesforceAuth.ts +167 -0
- package/src/connectors/stripe.ts +215 -0
- package/src/credentials.ts +282 -0
- package/src/demo.ts +200 -0
- package/src/diff.ts +158 -0
- package/src/format.ts +162 -0
- package/src/index.ts +129 -0
- package/src/mappings.ts +157 -0
- package/src/mcp-bin.ts +32 -0
- package/src/mcp.ts +185 -0
- package/src/merge.ts +235 -0
- package/src/planStore.ts +155 -0
- package/src/rules.ts +539 -0
- package/src/sampleData.ts +117 -0
- package/src/types.ts +372 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical GTM data model.
|
|
3
|
+
*
|
|
4
|
+
* Amounts are expressed in major currency units (e.g. dollars), matching what
|
|
5
|
+
* CRM providers return natively. Adapters that store minor units (cents) must
|
|
6
|
+
* convert at their own boundary.
|
|
7
|
+
*/
|
|
8
|
+
export type CrmProvider = "salesforce" | "hubspot" | "mock" | "unknown" | (string & {});
|
|
9
|
+
export type RiskLevel = "low" | "medium" | "high";
|
|
10
|
+
export type ApprovalStatus = "draft" | "needs_approval" | "approved" | "rejected" | "applied";
|
|
11
|
+
export type GtmObjectType = "account" | "contact" | "deal" | "user" | "activity";
|
|
12
|
+
export type GtmEvidenceSourceSystem = "salesforce" | "hubspot" | "gong" | "chorus" | "fathom" | "manual" | "csv" | "mock" | "unknown";
|
|
13
|
+
export type PatchOperationType = "set_field" | "clear_field" | "link_record" | "archive_record" | "create_task";
|
|
14
|
+
export type AuditFindingSeverity = "info" | "warning" | "critical";
|
|
15
|
+
/**
|
|
16
|
+
* One claim that a canonical record exists in an external system. A record
|
|
17
|
+
* merged from several providers carries one identity per provider.
|
|
18
|
+
*/
|
|
19
|
+
export type ProviderIdentity = {
|
|
20
|
+
provider: CrmProvider;
|
|
21
|
+
externalId: string;
|
|
22
|
+
};
|
|
23
|
+
export type PipelineFindingType = "call_next_step_not_reflected_in_crm" | "deal_missing_next_step" | "deal_stale_activity" | "deal_past_close_date" | "deal_missing_owner" | "deal_missing_account";
|
|
24
|
+
export type PipelineFindingStatus = "open" | "planned" | "approved" | "applied" | "verified" | "dismissed" | "stale";
|
|
25
|
+
export type SourceFreshness = {
|
|
26
|
+
state: "fresh" | "stale" | "unknown";
|
|
27
|
+
checkedAt: string;
|
|
28
|
+
sourceUpdatedAt?: string;
|
|
29
|
+
ageDays?: number;
|
|
30
|
+
};
|
|
31
|
+
export type GtmEvidence = {
|
|
32
|
+
id: string;
|
|
33
|
+
sourceSystem: GtmEvidenceSourceSystem;
|
|
34
|
+
sourceObjectType: string;
|
|
35
|
+
sourceObjectId: string;
|
|
36
|
+
objectType?: GtmObjectType;
|
|
37
|
+
objectId?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
text: string;
|
|
40
|
+
observedAt?: string;
|
|
41
|
+
capturedAt?: string;
|
|
42
|
+
freshness?: SourceFreshness;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
export type PipelineFinding = {
|
|
46
|
+
id: string;
|
|
47
|
+
type: PipelineFindingType;
|
|
48
|
+
objectType: GtmObjectType;
|
|
49
|
+
objectId: string;
|
|
50
|
+
severity: AuditFindingSeverity;
|
|
51
|
+
status: PipelineFindingStatus;
|
|
52
|
+
title: string;
|
|
53
|
+
summary: string;
|
|
54
|
+
recommendation: string;
|
|
55
|
+
evidenceIds: string[];
|
|
56
|
+
currentCrmValue?: unknown;
|
|
57
|
+
proposedValue?: unknown;
|
|
58
|
+
freshness: SourceFreshness;
|
|
59
|
+
patchEligibility: {
|
|
60
|
+
eligible: boolean;
|
|
61
|
+
operation?: PatchOperationType | "none";
|
|
62
|
+
field?: string;
|
|
63
|
+
reason: string;
|
|
64
|
+
approvalRequired: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
export type PatchVerification = {
|
|
68
|
+
status: "not_started" | "pending" | "verified" | "failed" | "skipped";
|
|
69
|
+
checkedAt?: string;
|
|
70
|
+
sourceSystem?: GtmEvidenceSourceSystem;
|
|
71
|
+
expectedValue?: unknown;
|
|
72
|
+
observedValue?: unknown;
|
|
73
|
+
providerResult?: unknown;
|
|
74
|
+
auditText?: string;
|
|
75
|
+
};
|
|
76
|
+
export type CanonicalUser = {
|
|
77
|
+
id: string;
|
|
78
|
+
provider?: CrmProvider;
|
|
79
|
+
crmId?: string;
|
|
80
|
+
identities?: ProviderIdentity[];
|
|
81
|
+
name: string;
|
|
82
|
+
email?: string;
|
|
83
|
+
title?: string;
|
|
84
|
+
active?: boolean;
|
|
85
|
+
};
|
|
86
|
+
export type CanonicalAccount = {
|
|
87
|
+
id: string;
|
|
88
|
+
provider?: CrmProvider;
|
|
89
|
+
crmId?: string;
|
|
90
|
+
identities?: ProviderIdentity[];
|
|
91
|
+
name: string;
|
|
92
|
+
domain?: string;
|
|
93
|
+
industry?: string;
|
|
94
|
+
ownerId?: string;
|
|
95
|
+
employeeCount?: number;
|
|
96
|
+
annualRevenue?: number;
|
|
97
|
+
lastActivityAt?: string;
|
|
98
|
+
lastSyncAt?: string;
|
|
99
|
+
/** Provider-native payload escape hatch. Never read by audit rules. */
|
|
100
|
+
raw?: unknown;
|
|
101
|
+
};
|
|
102
|
+
export type CanonicalContact = {
|
|
103
|
+
id: string;
|
|
104
|
+
provider?: CrmProvider;
|
|
105
|
+
crmId?: string;
|
|
106
|
+
identities?: ProviderIdentity[];
|
|
107
|
+
accountId?: string;
|
|
108
|
+
firstName?: string;
|
|
109
|
+
lastName?: string;
|
|
110
|
+
email?: string;
|
|
111
|
+
phone?: string;
|
|
112
|
+
title?: string;
|
|
113
|
+
ownerId?: string;
|
|
114
|
+
lastActivityAt?: string;
|
|
115
|
+
lastSyncAt?: string;
|
|
116
|
+
/** Provider-native payload escape hatch. Never read by audit rules. */
|
|
117
|
+
raw?: unknown;
|
|
118
|
+
};
|
|
119
|
+
export type CanonicalDeal = {
|
|
120
|
+
id: string;
|
|
121
|
+
provider?: CrmProvider;
|
|
122
|
+
crmId?: string;
|
|
123
|
+
identities?: ProviderIdentity[];
|
|
124
|
+
accountId?: string;
|
|
125
|
+
ownerId?: string;
|
|
126
|
+
name: string;
|
|
127
|
+
amount?: number;
|
|
128
|
+
currency?: string;
|
|
129
|
+
stage?: string;
|
|
130
|
+
closeDate?: string;
|
|
131
|
+
/** Provider-native deal type string (e.g. HubSpot `dealtype`), unmapped. */
|
|
132
|
+
dealType?: string;
|
|
133
|
+
forecastCategory?: string;
|
|
134
|
+
nextStep?: string;
|
|
135
|
+
probability?: number;
|
|
136
|
+
isClosed?: boolean;
|
|
137
|
+
isWon?: boolean;
|
|
138
|
+
lastActivityAt?: string;
|
|
139
|
+
lastSyncAt?: string;
|
|
140
|
+
/** Provider-native payload escape hatch. Never read by audit rules. */
|
|
141
|
+
raw?: unknown;
|
|
142
|
+
};
|
|
143
|
+
export type CanonicalActivity = {
|
|
144
|
+
id: string;
|
|
145
|
+
provider?: CrmProvider;
|
|
146
|
+
crmId?: string;
|
|
147
|
+
identities?: ProviderIdentity[];
|
|
148
|
+
accountId?: string;
|
|
149
|
+
contactId?: string;
|
|
150
|
+
dealId?: string;
|
|
151
|
+
ownerId?: string;
|
|
152
|
+
type: "call" | "email" | "meeting" | "note" | "task" | "other";
|
|
153
|
+
occurredAt: string;
|
|
154
|
+
subject?: string;
|
|
155
|
+
};
|
|
156
|
+
export type CanonicalGtmSnapshot = {
|
|
157
|
+
generatedAt: string;
|
|
158
|
+
provider: CrmProvider;
|
|
159
|
+
users: CanonicalUser[];
|
|
160
|
+
accounts: CanonicalAccount[];
|
|
161
|
+
contacts: CanonicalContact[];
|
|
162
|
+
deals: CanonicalDeal[];
|
|
163
|
+
activities: CanonicalActivity[];
|
|
164
|
+
};
|
|
165
|
+
export type GtmPolicy = {
|
|
166
|
+
staleDealDays: number;
|
|
167
|
+
requireDealOwner: boolean;
|
|
168
|
+
requireAccountForDeal: boolean;
|
|
169
|
+
today: string;
|
|
170
|
+
/** Flag open deals without an amount (default true). */
|
|
171
|
+
requireDealAmount?: boolean;
|
|
172
|
+
/** Window ahead of today that counts as "closing soon" (default 14 days). */
|
|
173
|
+
closingSoonDays?: number;
|
|
174
|
+
/** Idle days that make a closing-soon deal a finding (default 7). */
|
|
175
|
+
closingSoonIdleDays?: number;
|
|
176
|
+
};
|
|
177
|
+
export type AuditFinding = {
|
|
178
|
+
id: string;
|
|
179
|
+
objectType: GtmObjectType;
|
|
180
|
+
objectId: string;
|
|
181
|
+
ruleId: string;
|
|
182
|
+
title: string;
|
|
183
|
+
severity: AuditFindingSeverity;
|
|
184
|
+
summary: string;
|
|
185
|
+
recommendation: string;
|
|
186
|
+
type?: PipelineFindingType;
|
|
187
|
+
evidenceIds?: string[];
|
|
188
|
+
currentCrmValue?: unknown;
|
|
189
|
+
proposedValue?: unknown;
|
|
190
|
+
freshness?: SourceFreshness;
|
|
191
|
+
patchEligible?: boolean;
|
|
192
|
+
};
|
|
193
|
+
export type PatchOperation = {
|
|
194
|
+
id: string;
|
|
195
|
+
objectType: GtmObjectType;
|
|
196
|
+
objectId: string;
|
|
197
|
+
operation: PatchOperationType;
|
|
198
|
+
field?: string;
|
|
199
|
+
beforeValue?: unknown;
|
|
200
|
+
afterValue?: unknown;
|
|
201
|
+
reason: string;
|
|
202
|
+
riskLevel: RiskLevel;
|
|
203
|
+
approvalRequired: boolean;
|
|
204
|
+
sourceRuleOrPolicy?: string;
|
|
205
|
+
rollback?: string;
|
|
206
|
+
evidenceIds?: string[];
|
|
207
|
+
findingIds?: string[];
|
|
208
|
+
verification?: PatchVerification;
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* A patch plan is always a dry-run proposal. Applying a plan never mutates
|
|
212
|
+
* the plan itself; the outcome is recorded separately as a `PatchPlanRun`.
|
|
213
|
+
*/
|
|
214
|
+
export type PatchPlan = {
|
|
215
|
+
id: string;
|
|
216
|
+
title: string;
|
|
217
|
+
createdAt: string;
|
|
218
|
+
status: ApprovalStatus;
|
|
219
|
+
dryRun: true;
|
|
220
|
+
summary: string;
|
|
221
|
+
findings: AuditFinding[];
|
|
222
|
+
pipelineFindings?: PipelineFinding[];
|
|
223
|
+
evidence?: GtmEvidence[];
|
|
224
|
+
operations: PatchOperation[];
|
|
225
|
+
};
|
|
226
|
+
/** Pre-computed lookups shared by all rules so each rule stays O(n). */
|
|
227
|
+
export type GtmSnapshotIndex = {
|
|
228
|
+
usersById: Map<string, CanonicalUser>;
|
|
229
|
+
accountsById: Map<string, CanonicalAccount>;
|
|
230
|
+
contactsByAccountId: Map<string, CanonicalContact[]>;
|
|
231
|
+
dealsByAccountId: Map<string, CanonicalDeal[]>;
|
|
232
|
+
activitiesByDealId: Map<string, CanonicalActivity[]>;
|
|
233
|
+
};
|
|
234
|
+
export type GtmRuleContext = {
|
|
235
|
+
snapshot: CanonicalGtmSnapshot;
|
|
236
|
+
policy: GtmPolicy;
|
|
237
|
+
index: GtmSnapshotIndex;
|
|
238
|
+
};
|
|
239
|
+
export type GtmRuleResult = {
|
|
240
|
+
findings: AuditFinding[];
|
|
241
|
+
operations: PatchOperation[];
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* A deterministic audit rule. Rules read the snapshot through the context and
|
|
245
|
+
* return findings plus dry-run patch operations; they never perform writes.
|
|
246
|
+
*/
|
|
247
|
+
export type GtmAuditRule = {
|
|
248
|
+
id: string;
|
|
249
|
+
title: string;
|
|
250
|
+
description: string;
|
|
251
|
+
/** Grouping for docs and discovery, e.g. "hygiene", "forecast", "coverage". */
|
|
252
|
+
category?: string;
|
|
253
|
+
evaluate: (context: GtmRuleContext) => GtmRuleResult;
|
|
254
|
+
};
|
|
255
|
+
export type PatchOperationResult = {
|
|
256
|
+
operationId: string;
|
|
257
|
+
/** `conflict`: the provider value drifted since the plan was proposed; nothing was written. */
|
|
258
|
+
status: "applied" | "failed" | "skipped" | "conflict";
|
|
259
|
+
detail?: string;
|
|
260
|
+
providerData?: unknown;
|
|
261
|
+
};
|
|
262
|
+
export type PatchPlanRunStatus = "applied" | "partial" | "failed" | "rejected";
|
|
263
|
+
/** The record of one attempt to apply an approved patch plan. */
|
|
264
|
+
export type PatchPlanRun = {
|
|
265
|
+
planId: string;
|
|
266
|
+
provider: CrmProvider;
|
|
267
|
+
startedAt: string;
|
|
268
|
+
finishedAt: string;
|
|
269
|
+
status: PatchPlanRunStatus;
|
|
270
|
+
results: PatchOperationResult[];
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* The provider contract. Reads produce a canonical snapshot; writes accept a
|
|
274
|
+
* single patch operation and report the outcome. Connectors without
|
|
275
|
+
* `applyOperation` are read-only.
|
|
276
|
+
*/
|
|
277
|
+
export type GtmConnector = {
|
|
278
|
+
provider: CrmProvider;
|
|
279
|
+
fetchSnapshot: () => Promise<CanonicalGtmSnapshot>;
|
|
280
|
+
applyOperation?: (operation: PatchOperation) => Promise<PatchOperationResult>;
|
|
281
|
+
/**
|
|
282
|
+
* Read the live value of one canonical field, used for compare-and-set:
|
|
283
|
+
* apply orchestration refuses to write over values that drifted since the
|
|
284
|
+
* plan was proposed.
|
|
285
|
+
*/
|
|
286
|
+
readField?: (objectType: GtmObjectType, objectId: string, field: string) => Promise<unknown>;
|
|
287
|
+
/**
|
|
288
|
+
* Records modified since the given ISO timestamp, as a partial snapshot —
|
|
289
|
+
* the incremental alternative to `fetchSnapshot` for frequent syncs.
|
|
290
|
+
* Provider change feeds may omit cross-record associations; consumers
|
|
291
|
+
* merging deltas must preserve associations from the last full snapshot.
|
|
292
|
+
*/
|
|
293
|
+
fetchChanges?: (sinceIso: string) => Promise<CanonicalGtmSnapshot>;
|
|
294
|
+
};
|
package/dist/types.js
ADDED
package/docs/api.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# fullstackgtm — API surface (1.0 contract candidate)
|
|
2
|
+
|
|
3
|
+
Everything listed here is the intended 1.0 contract. While the package is in
|
|
4
|
+
beta (0.x), these surfaces are settling and breaking changes may still land
|
|
5
|
+
in minor releases — each one is called out in the CHANGELOG. At 1.0 they
|
|
6
|
+
freeze: breaking changes will require a major version. Anything *not* listed
|
|
7
|
+
(internal helpers, file layouts, exact message strings) may change in any
|
|
8
|
+
release.
|
|
9
|
+
|
|
10
|
+
## Canonical data model (`types.ts`)
|
|
11
|
+
|
|
12
|
+
- `CanonicalGtmSnapshot` — `{ generatedAt, provider, users, accounts, contacts, deals, activities }`
|
|
13
|
+
- `CanonicalUser`, `CanonicalAccount`, `CanonicalContact`, `CanonicalDeal`, `CanonicalActivity`
|
|
14
|
+
- Amounts are **major currency units**; adapters storing minor units convert at their boundary.
|
|
15
|
+
- `identities: ProviderIdentity[]` — `(provider, externalId)` claims; one per source system.
|
|
16
|
+
- `raw` — provider payload escape hatch; never read by audit rules.
|
|
17
|
+
- `GtmPolicy` — thresholds with optional extensions (`requireDealAmount`, `closingSoonDays`, `closingSoonIdleDays`).
|
|
18
|
+
|
|
19
|
+
## Audit engine
|
|
20
|
+
|
|
21
|
+
- `GtmAuditRule` — `{ id, title, description, category?, evaluate(context) }`; the public extension point.
|
|
22
|
+
- `GtmRuleContext` — `{ snapshot, policy, index }` with the prebuilt O(n) `GtmSnapshotIndex`.
|
|
23
|
+
- `auditSnapshot(snapshot, policy?, rules?)` → `PatchPlan`.
|
|
24
|
+
- `builtinAuditRules` (11 rules) plus each rule exported individually.
|
|
25
|
+
- **Determinism guarantee**: identical inputs produce identical findings and operations with identical ids (`auditFindingId`, `patchOperationId` are stable hashes of rule + record).
|
|
26
|
+
|
|
27
|
+
## Patch plans and application
|
|
28
|
+
|
|
29
|
+
- `PatchPlan` — immutable dry-run proposal (`dryRun: true` always).
|
|
30
|
+
- `PatchOperation` — typed write: objectType, objectId, operation kind, field, before/after, reason, riskLevel, approvalRequired.
|
|
31
|
+
- `applyPatchPlan(connector, plan, options)` → `PatchPlanRun`. The safety contract:
|
|
32
|
+
1. only operation ids in `approvedOperationIds` are written;
|
|
33
|
+
2. `requires_human_*` placeholder values are never written without a `valueOverrides` entry;
|
|
34
|
+
3. compare-and-set: with `readField` support, drifted values produce `conflict` results instead of writes (`checkConflicts: false` opts out);
|
|
35
|
+
4. every operation yields a `PatchOperationResult` (`applied | failed | skipped | conflict`).
|
|
36
|
+
- `PlanStore` / `createFilePlanStore` — durable lifecycle: save → approve operations (+ value overrides) → record runs.
|
|
37
|
+
|
|
38
|
+
## Connectors
|
|
39
|
+
|
|
40
|
+
- `GtmConnector` — `{ provider, fetchSnapshot(), applyOperation?, readField?, fetchChanges? }`.
|
|
41
|
+
- Connectors never silently drop unresolvable records; audits surface them.
|
|
42
|
+
- `fetchChanges(sinceIso)` returns a partial snapshot; change feeds may omit associations.
|
|
43
|
+
- `createHubspotConnector(options)` — read/write/readField/fetchChanges. `applyOperation` implements every `PatchOperationType`: `set_field`, `clear_field`, `link_record`, `create_task`, `archive_record`.
|
|
44
|
+
- `createSalesforceConnector(options)` — read/write/readField/fetchChanges; probabilities normalized to 0..1; `applyOperation` implements every operation type.
|
|
45
|
+
- `createStripeConnector(options)` — read-only billing by design (`applyOperation` returns `skipped`); email domains are the cross-system merge keys. Implements `fetchChanges` (incremental via `created[gte]`).
|
|
46
|
+
|
|
47
|
+
## Multi-system operations
|
|
48
|
+
|
|
49
|
+
- `mergeSnapshots(snapshots)` → `{ snapshot, report }` — entity resolution on email/domain/name keys; identities accumulate; first source wins on conflicts and every conflict is reported.
|
|
50
|
+
- `diffSnapshots(before, after)` → record/field-level changes; `diffFindings(beforePlan, afterPlan)` → hygiene drift on stable ids.
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
- `fullstackgtm.config.json`: `{ policy?, rules?: { enabled?, disabled? }, rulePackages? }`.
|
|
55
|
+
- Rule packages export `rules: GtmAuditRule[]`.
|
|
56
|
+
- Precedence: CLI flags > config file > defaults.
|
|
57
|
+
|
|
58
|
+
## CLI
|
|
59
|
+
|
|
60
|
+
Commands: `login` / `logout`, `snapshot`, `audit`, `diff`, `merge`, `plans`, `apply`, `rules`.
|
|
61
|
+
Exit codes: `0` success · `1` error · `2` findings/regressions at the requested gate
|
|
62
|
+
(`--fail-on`, `--fail-on-new-findings`). `--json` everywhere; JSON output shapes are stable.
|
|
63
|
+
|
|
64
|
+
Credential resolution ladder: explicit `--token-env` → ambient env
|
|
65
|
+
(`HUBSPOT_ACCESS_TOKEN`, `SALESFORCE_ACCESS_TOKEN`+`SALESFORCE_INSTANCE_URL`,
|
|
66
|
+
`STRIPE_SECRET_KEY`) → stored login (`~/.fullstackgtm`, `FSGTM_HOME` override)
|
|
67
|
+
→ broker pairing (`login --via`).
|
|
68
|
+
|
|
69
|
+
## MCP
|
|
70
|
+
|
|
71
|
+
Tools: `fullstackgtm_audit`, `fullstackgtm_rules`, `fullstackgtm_apply`
|
|
72
|
+
(requires explicit `approvedOperationIds`). Input schemas are stable.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# fullstackgtm: 0.1 → 1.0
|
|
2
|
+
|
|
3
|
+
The planned, versioned path from first publish to a stable 1.0. Each milestone
|
|
4
|
+
is shippable on its own, ordered so that every step consolidates the
|
|
5
|
+
architecture before the next step expands it. The invariants that hold at
|
|
6
|
+
every version:
|
|
7
|
+
|
|
8
|
+
1. **The package is the product; the app is an adapter.** Anything the app
|
|
9
|
+
needs from a provider goes through the package connector. New capability
|
|
10
|
+
lands in the package first.
|
|
11
|
+
2. **Reads are free, writes are patch plans.** No code path may mutate a
|
|
12
|
+
provider except `applyPatchPlan` / `applyOperation` with explicit approval.
|
|
13
|
+
3. **Deterministic and diffable.** Same inputs produce the same findings with
|
|
14
|
+
the same ids. Golden tests over the demo dataset enforce this.
|
|
15
|
+
4. **Zero-dependency core.** Connectors use `fetch`; optional integrations
|
|
16
|
+
(MCP) stay optional peers.
|
|
17
|
+
|
|
18
|
+
## 0.1.0 — The loop exists (done)
|
|
19
|
+
|
|
20
|
+
Canonical model, pluggable rule engine, patch plans, safe apply, HubSpot
|
|
21
|
+
connector, CLI/MCP, demo dataset, CLI auth (private app token, loopback
|
|
22
|
+
OAuth), broker pairing against a hosted deployment, CI + tag-publish
|
|
23
|
+
automation.
|
|
24
|
+
|
|
25
|
+
## 0.2.0 — Provider parity and operational trust (done)
|
|
26
|
+
|
|
27
|
+
- **Salesforce connector** behind the same `GtmConnector` contract: SOQL with
|
|
28
|
+
cursor pagination, PATCH write-back, probabilities normalized to canonical
|
|
29
|
+
0..1, never drops unresolvable records.
|
|
30
|
+
- **Salesforce CLI auth**: native device flow (code on any device, no client
|
|
31
|
+
secret, silent refresh) plus manual token + instance URL.
|
|
32
|
+
- **Broker mint for Salesforce**: paired CLIs receive `accessToken +
|
|
33
|
+
instanceUrl + fieldMappings` from the org's stored credentials.
|
|
34
|
+
- **CLI token observability**: paired-CLI list with last-used timestamps and
|
|
35
|
+
one-click revoke in the dashboard.
|
|
36
|
+
- App consolidation: `convex/salesforceActions.ts` collapses onto the package
|
|
37
|
+
connector like the HubSpot path did.
|
|
38
|
+
|
|
39
|
+
## 0.3.0 — One patch-plan vocabulary (done)
|
|
40
|
+
|
|
41
|
+
The last internal duplication: the app's durable `patchPlans` tables speak
|
|
42
|
+
`verb`/`operation` strings while the package speaks typed `PatchOperation`s.
|
|
43
|
+
|
|
44
|
+
- Package: add a `PlanStore` interface (persist plan → approve operations →
|
|
45
|
+
record runs) so durable workflows are a first-class framework concern, not
|
|
46
|
+
an app concern.
|
|
47
|
+
- App: migrate `patchPlans`/`patchOperations` rows to the package types
|
|
48
|
+
(schema migration with a compatibility window), route
|
|
49
|
+
`patchPlanActions.apply` through `applyPatchPlan`.
|
|
50
|
+
- Exit criterion: the dashboard review queue and `fullstackgtm apply` operate
|
|
51
|
+
on byte-identical plan documents.
|
|
52
|
+
|
|
53
|
+
## 0.4.0 — Policy as config, rules as packages (done)
|
|
54
|
+
|
|
55
|
+
Make the rule engine the community surface.
|
|
56
|
+
|
|
57
|
+
- `fullstackgtm.config.json` (or `.ts`): policy thresholds, enabled rules,
|
|
58
|
+
per-rule severity overrides, custom rule module paths.
|
|
59
|
+
- Rule metadata: category, default severity, docs URL; `fullstackgtm rules`
|
|
60
|
+
becomes generated documentation.
|
|
61
|
+
- 10–15 additional built-in rules drawn from real RevOps pain: duplicate
|
|
62
|
+
accounts by domain, contacts without owners, deals closing this quarter
|
|
63
|
+
with no activity in 14 days, stage/probability drift, currency mismatches.
|
|
64
|
+
- Exit criterion: a third party can publish an npm package of rules and a
|
|
65
|
+
user can enable it from config without forking.
|
|
66
|
+
|
|
67
|
+
## 0.5.0 — Time: snapshot history and drift (done)
|
|
68
|
+
|
|
69
|
+
- Snapshot archive conventions (local directory or any mounted/S3-backed
|
|
70
|
+
path) with `fullstackgtm snapshot --archive`.
|
|
71
|
+
- `fullstackgtm diff <a> <b>`: what changed between snapshots — records,
|
|
72
|
+
fields, and *findings* (hygiene drift), built on the stable-id property.
|
|
73
|
+
- Audit trends in the hosted app (the observability layer earns its keep).
|
|
74
|
+
- Exit criterion: a nightly CI job can fail on hygiene regression, not just
|
|
75
|
+
hygiene presence.
|
|
76
|
+
|
|
77
|
+
## 0.6.0 — Many systems, one entity (done — Stripe is the first non-CRM connector)
|
|
78
|
+
|
|
79
|
+
The original thesis: GTM data disagrees across systems.
|
|
80
|
+
|
|
81
|
+
- Entity resolution over `identities` claims: merge canonical records from
|
|
82
|
+
multiple snapshots (CRM + marketing + billing) by domain/email/external-id
|
|
83
|
+
heuristics, with explicit confidence and human-reviewable merge plans (the
|
|
84
|
+
patch-plan safety model applied to identity).
|
|
85
|
+
- Cross-system audit rules: "account exists in billing but not CRM",
|
|
86
|
+
"marketing-qualified contact has no CRM owner".
|
|
87
|
+
- First non-CRM connector (likely billing — Stripe — or marketing) to prove
|
|
88
|
+
the contract generalizes.
|
|
89
|
+
- Exit criterion: one audit over a merged snapshot from two live systems.
|
|
90
|
+
|
|
91
|
+
## 0.7.0 — Sync maturity (done)
|
|
92
|
+
|
|
93
|
+
- Incremental fetch: provider cursors (HubSpot `updatedAfter` search,
|
|
94
|
+
Salesforce `SystemModstamp` filters) behind an optional
|
|
95
|
+
`fetchChanges(since)` connector capability.
|
|
96
|
+
- Conflict surfacing: when a provider value changed under an unapplied patch
|
|
97
|
+
operation, the plan flags it instead of writing blind (compare-and-set).
|
|
98
|
+
- Exit criterion: hourly sync of a 100k-record org without full re-pulls.
|
|
99
|
+
|
|
100
|
+
## 0.8–0.9 — Hardening and freeze (done)
|
|
101
|
+
|
|
102
|
+
- API review and freeze of `types.ts`, connector contract, CLI flags, MCP
|
|
103
|
+
schemas; deprecations resolved.
|
|
104
|
+
- Security review of the auth surfaces (credential store, broker endpoints,
|
|
105
|
+
token lifecycle); rate limiting on the device-flow endpoints.
|
|
106
|
+
- Docs site with the operating-model registry as browsable reference.
|
|
107
|
+
- Performance pass: streaming snapshots for very large orgs.
|
|
108
|
+
|
|
109
|
+
## 1.0.0 — The contract (reached)
|
|
110
|
+
|
|
111
|
+
Semver stability commitment on the canonical model, rule interface, connector
|
|
112
|
+
contract, plan format, CLI, and MCP tools. From here, new providers and new
|
|
113
|
+
rules are minor releases; the model only breaks at 2.0.
|
|
114
|
+
|
|
115
|
+
## Deliberately out of scope until after 1.0
|
|
116
|
+
|
|
117
|
+
- Hosted multi-org analytics across customers
|
|
118
|
+
- Non-deterministic (LLM-scored) rules — they can *propose*, but the
|
|
119
|
+
deterministic engine stays the system of record for findings
|
|
120
|
+
- Workflow orchestration (sequences, cadences) — adjacent product, not
|
|
121
|
+
framework
|
package/llms.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# fullstackgtm
|
|
2
|
+
|
|
3
|
+
> Plan/apply for your GTM stack: canonical CRM/GTM data model, deterministic
|
|
4
|
+
> hygiene audits, reviewable dry-run patch plans, and approval-gated
|
|
5
|
+
> write-back to HubSpot, Salesforce, and Stripe (read-only). Think
|
|
6
|
+
> `terraform plan` for your CRM. Apache-2.0.
|
|
7
|
+
|
|
8
|
+
CLI quick check: `fullstackgtm doctor --json`. Zero-credential demo:
|
|
9
|
+
`fullstackgtm audit --demo --json`. Audits are read-only; `apply` writes only
|
|
10
|
+
explicitly approved operation ids. Exit codes: 0 success, 1 error, 2 findings
|
|
11
|
+
at/above `--fail-on`.
|
|
12
|
+
|
|
13
|
+
## Docs
|
|
14
|
+
|
|
15
|
+
- [README](https://github.com/fullstackgtm/core/blob/main/README.md): install, five-minute loop, auth ladder, MCP setup, programmatic use
|
|
16
|
+
- [INSTALL_FOR_AGENTS](https://github.com/fullstackgtm/core/blob/main/INSTALL_FOR_AGENTS.md): deterministic install-and-verify steps with expected outputs
|
|
17
|
+
- [API reference](https://github.com/fullstackgtm/core/blob/main/docs/api.md): semver-covered surfaces — canonical model, rule interface, plan/apply contract, connector contract, config, CLI, MCP tools
|
|
18
|
+
- [CHANGELOG](https://github.com/fullstackgtm/core/blob/main/CHANGELOG.md): release history
|
|
19
|
+
|
|
20
|
+
## Key invariants
|
|
21
|
+
|
|
22
|
+
- Reads are safe by default; nothing is written without explicit `--approve`
|
|
23
|
+
- `requires_human_*` placeholder values are refused without a `--value` override
|
|
24
|
+
- Finding/operation ids are stable hashes of rule + record (diffable across runs)
|
|
25
|
+
- Secrets are never accepted as argv flags; stdin or env only
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fullstackgtm",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Open-source agentic GTM ops framework: canonical GTM data model, pluggable deterministic audits, reviewable dry-run patch plans, approval-gated write-back with conflict detection, and cross-system entity resolution. HubSpot, Salesforce, and Stripe connectors included.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Full Stack GTM",
|
|
7
|
+
"homepage": "https://github.com/fullstackgtm/core#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/fullstackgtm/core/issues"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"fullstackgtm": "dist/bin.js",
|
|
23
|
+
"fullstackgtm-mcp": "dist/mcp-bin.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"docs",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"INSTALL_FOR_AGENTS.md",
|
|
32
|
+
"llms.txt",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"typescript": "^5.9.0",
|
|
44
|
+
"zod": "^4.4.2"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"gtm",
|
|
48
|
+
"revops",
|
|
49
|
+
"crm",
|
|
50
|
+
"salesforce",
|
|
51
|
+
"hubspot",
|
|
52
|
+
"audit",
|
|
53
|
+
"agent",
|
|
54
|
+
"mcp",
|
|
55
|
+
"patch-plan"
|
|
56
|
+
],
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/fullstackgtm/core.git"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
66
|
+
"zod": "^4.4.2"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@modelcontextprotocol/sdk": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"zod": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|