fullstackgtm 0.10.0 → 0.11.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 +106 -0
- package/INSTALL_FOR_AGENTS.md +28 -2
- package/README.md +74 -6
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +251 -16
- package/dist/connectors/hubspot.js +36 -11
- package/dist/connectors/hubspotAuth.js +10 -2
- package/dist/connectors/salesforce.js +34 -9
- package/dist/connectors/salesforceAuth.js +17 -3
- package/dist/credentials.d.ts +19 -0
- package/dist/credentials.js +69 -8
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/mcp.js +53 -5
- package/dist/report.d.ts +61 -0
- package/dist/report.js +331 -0
- package/dist/rules.d.ts +1 -0
- package/dist/rules.js +47 -0
- package/dist/suggest.d.ts +31 -0
- package/dist/suggest.js +148 -0
- package/docs/api.md +13 -1
- package/docs/roadmap-to-1.0.md +31 -3
- package/package.json +1 -1
- package/src/cli.ts +271 -14
- package/src/connectors/hubspot.ts +35 -11
- package/src/connectors/hubspotAuth.ts +9 -2
- package/src/connectors/salesforce.ts +35 -9
- package/src/connectors/salesforceAuth.ts +19 -6
- package/src/credentials.ts +71 -6
- package/src/index.ts +7 -0
- package/src/mcp.ts +55 -5
- package/src/report.ts +502 -0
- package/src/rules.ts +50 -0
- package/src/suggest.ts +202 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,112 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
5
5
|
and the project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
The path to 1.0 is planned in [docs/roadmap-to-1.0.md](./docs/roadmap-to-1.0.md).
|
|
7
7
|
|
|
8
|
+
## [0.11.0] — 2026-06-11
|
|
9
|
+
|
|
10
|
+
Canonicalizes the paths discovered dogfooding against a real portal: the
|
|
11
|
+
suggest chain (every `requires_human_account_selection` answer was derivable
|
|
12
|
+
from the snapshot but required ad-hoc scripting), governed record creation,
|
|
13
|
+
client-ready reports, and multi-org profiles.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`fullstackgtm suggest --plan-id <id> | --plan <path> [source options]`**:
|
|
18
|
+
deterministic value suggestions for `requires_human_*` placeholder
|
|
19
|
+
operations, derived from snapshot evidence — account-name matching
|
|
20
|
+
cross-checked against contact→account associations, plus the
|
|
21
|
+
only-active-user case for owner selection. Every suggestion carries a
|
|
22
|
+
confidence level (`high`/`low`/`create`/`none`) and a written reason;
|
|
23
|
+
conflicting or ambiguous evidence yields no suggestion, never a guess.
|
|
24
|
+
`--out` writes a suggestions file; `--json` for agents. Exposed
|
|
25
|
+
programmatically as `suggestValues` and over MCP as `fullstackgtm_suggest`.
|
|
26
|
+
- **`plans approve <id> --values-from <suggestions.json>`**: bulk-approve
|
|
27
|
+
with suggested values — high-confidence only by default,
|
|
28
|
+
`--min-confidence low` and `--include-creates` widen the bar explicitly.
|
|
29
|
+
Explicit `--value` flags still win.
|
|
30
|
+
- **`create:<Name>` link values**: approving a `link_record` operation with
|
|
31
|
+
`--value <op>=create:Acme` creates the company (HubSpot) / account
|
|
32
|
+
(Salesforce) and links to it in one audited operation — record creation
|
|
33
|
+
stays inside the typed, human-approved model instead of a side channel.
|
|
34
|
+
- New builtin rule `duplicate-open-deal` (data-quality): flags multiple open
|
|
35
|
+
deals sharing a normalized name, scoped to the account when linked —
|
|
36
|
+
typically an integration re-creating deals instead of upserting, which
|
|
37
|
+
counts the same revenue several times in pipeline and forecast. Emits one
|
|
38
|
+
finding and one approval-gated merge-review task per duplicate group.
|
|
39
|
+
Found dogfooding: an outreach-tool sync had tripled five open deals in our
|
|
40
|
+
own portal and no existing rule caught it.
|
|
41
|
+
- `fullstackgtm report` — render an audit (or an existing plan via `--plan`)
|
|
42
|
+
as a client-ready deliverable in markdown or self-contained HTML:
|
|
43
|
+
at-a-glance metrics, prose summary, per-rule detail with capped example
|
|
44
|
+
records (`--max-examples`), and recommended next steps. `--client`,
|
|
45
|
+
`--title`, `--prepared-by`, and `--format` customize the output;
|
|
46
|
+
`--out report.html` infers HTML. Exposed programmatically as
|
|
47
|
+
`auditReportToMarkdown` / `auditReportToHtml`.
|
|
48
|
+
- Credential profiles for multi-organization use: the global
|
|
49
|
+
`--profile <name>` flag (or `FULLSTACKGTM_PROFILE`) scopes stored logins
|
|
50
|
+
AND stored plans to `profiles/<name>/` under the fullstackgtm home, so one
|
|
51
|
+
operator can hold several clients' credentials without mixing them — and a
|
|
52
|
+
plan proposed against one org's CRM can never be applied through
|
|
53
|
+
another's. New `profiles` command lists them; `doctor` reports the active
|
|
54
|
+
profile. The default profile keeps the existing flat layout, so current
|
|
55
|
+
installs are unaffected. Exposed programmatically as `setActiveProfile`,
|
|
56
|
+
`activeProfile`, `listProfiles`, and `DEFAULT_PROFILE`.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- `validateHubspotToken` / `validateSalesforceToken` no longer echo the
|
|
61
|
+
provider's raw error body into the login failure message (observed live: a
|
|
62
|
+
HubSpot 401 body printed to the terminal). Status line only, matching the
|
|
63
|
+
no-body-interpolation rule applied elsewhere in 1.0.1.
|
|
64
|
+
- Unreachable hosts during `login salesforce --instance-url` and
|
|
65
|
+
`login --via <url>` now name the target and what to check, completing the
|
|
66
|
+
0.10.1 fix that only covered the audit/connector path.
|
|
67
|
+
|
|
68
|
+
## [0.10.1] — 2026-06-11
|
|
69
|
+
|
|
70
|
+
Fixes from a full fresh-user journey audit (install → demo → MCP → real CRM),
|
|
71
|
+
focused on the first-contact experience.
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- **`fullstackgtm --version`** (also `-v` / `version`) — previously exited 1
|
|
76
|
+
with a usage dump.
|
|
77
|
+
- **README "Connect your CRM" section**: HubSpot private-app walkthrough with
|
|
78
|
+
the exact read scopes the audit needs (`crm.objects.{owners,companies,contacts,deals}.read`)
|
|
79
|
+
and the write scopes `apply` needs; Salesforce Connected App prerequisites
|
|
80
|
+
(admin-created, device flow enabled, `api` + `refresh_token` scopes,
|
|
81
|
+
propagation delay); Stripe restricted-key guidance. Previously the word
|
|
82
|
+
"scope" appeared nowhere in the docs.
|
|
83
|
+
- Roadmap: documented the known real-portal gaps to close before 1.0
|
|
84
|
+
(pipeline-aware closed-deal detection, 429/retry, fetchChanges 10k
|
|
85
|
+
truncation, MCP plan hand-off, scope-complete login validation).
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **`FSGTM_NO_BROWSER=1` suppresses OS browser opens** in all login flows
|
|
90
|
+
(broker pairing, Salesforce device flow, HubSpot OAuth) — for agent
|
|
91
|
+
sandboxes, CI, and headless use; verification URLs are always printed.
|
|
92
|
+
The broker test suite sets it, so running `npm test` no longer pops a
|
|
93
|
+
real browser tab at a mock pairing URL on every run.
|
|
94
|
+
- **MCP server now works from inside existing projects.** When launched via
|
|
95
|
+
`npx -p ... fullstackgtm-mcp` from a directory whose node_modules already
|
|
96
|
+
contains the peers (but not fullstackgtm), npx skips installing them into
|
|
97
|
+
its cache and module resolution failed; the server now falls back to
|
|
98
|
+
resolving the peers from the working directory — peer-dependency semantics.
|
|
99
|
+
Previously this made the README's `claude mcp add` setup produce a dead
|
|
100
|
+
server in most agent-tooling repos.
|
|
101
|
+
- **README auth examples matched the CLI again**: `login salesforce --token ...`
|
|
102
|
+
and `login hubspot --oauth ... --client-secret ...` showed argv-secret forms
|
|
103
|
+
the CLI deliberately rejects; both now use stdin.
|
|
104
|
+
- The no-credentials Stripe error suggested `login stripe --token sk_...`,
|
|
105
|
+
which the CLI itself refuses — it now shows the stdin form and mentions
|
|
106
|
+
restricted keys.
|
|
107
|
+
- Unreachable hosts no longer surface as a bare `fetch failed`: HubSpot and
|
|
108
|
+
Salesforce connection errors name the target URL and (for Salesforce) point
|
|
109
|
+
at SALESFORCE_INSTANCE_URL.
|
|
110
|
+
- Credential errors now point at `fullstackgtm doctor` and the README scope
|
|
111
|
+
guide; the MCP audit tool description now mentions the `demo` source;
|
|
112
|
+
README rule count corrected (11 built-ins, not five).
|
|
113
|
+
|
|
8
114
|
## [0.10.0] — 2026-06-10
|
|
9
115
|
|
|
10
116
|
**Versioning reset to reflect beta status.** The 1.x numbering below
|
package/INSTALL_FOR_AGENTS.md
CHANGED
|
@@ -45,7 +45,15 @@ Credential resolution ladder, first match wins:
|
|
|
45
45
|
4. Broker pairing: `fullstackgtm login --via <hosted url>` (a human approves the pairing code)
|
|
46
46
|
|
|
47
47
|
In an agent sandbox, prefer rung 1 or 2. Never echo tokens into argv —
|
|
48
|
-
`login` reads secrets from stdin only.
|
|
48
|
+
`login` reads secrets from stdin only. Set `FSGTM_NO_BROWSER=1` in headless
|
|
49
|
+
environments — login flows then print verification URLs instead of opening
|
|
50
|
+
the OS browser.
|
|
51
|
+
|
|
52
|
+
Provider prerequisites (what the human must create, and which scopes) are in
|
|
53
|
+
the README's **"Connect your CRM"** section: HubSpot needs a private app with
|
|
54
|
+
four `crm.objects.*.read` scopes (plus write scopes only for `apply`);
|
|
55
|
+
Salesforce needs an admin-created Connected App with device flow enabled;
|
|
56
|
+
Stripe works with a restricted key (Customers + Subscriptions read).
|
|
49
57
|
|
|
50
58
|
```bash
|
|
51
59
|
HUBSPOT_ACCESS_TOKEN=$TOKEN fullstackgtm audit --provider hubspot --json --out plan.json
|
|
@@ -65,15 +73,33 @@ fullstackgtm audit --provider hubspot --save # persists plan to ~/.fullstack
|
|
|
65
73
|
fullstackgtm plans list # a human reviews and approves
|
|
66
74
|
```
|
|
67
75
|
|
|
76
|
+
For `requires_human_*` placeholders, chain the deterministic suggestion engine
|
|
77
|
+
instead of guessing values yourself:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
fullstackgtm suggest --plan-id <id> --provider hubspot --out suggestions.json # read-only
|
|
81
|
+
fullstackgtm plans approve <id> --values-from suggestions.json # high-confidence only
|
|
82
|
+
fullstackgtm apply --plan-id <id> --provider hubspot
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Every suggestion carries a confidence (`high`/`low`/`create`/`none`) and a
|
|
86
|
+
reason derived from snapshot evidence. Surface `low`/`create`/`none` entries
|
|
87
|
+
to your human rather than widening the bar unilaterally — `create:<Name>`
|
|
88
|
+
values create a new CRM record when applied.
|
|
89
|
+
|
|
68
90
|
## 6. MCP server (optional)
|
|
69
91
|
|
|
70
92
|
The MCP entrypoint needs optional peers that plain `npx fullstackgtm-mcp`
|
|
71
93
|
does not install:
|
|
72
94
|
|
|
73
95
|
```bash
|
|
74
|
-
npx -p fullstackgtm -p @modelcontextprotocol/sdk -p zod fullstackgtm-mcp
|
|
96
|
+
npx -y -p fullstackgtm -p @modelcontextprotocol/sdk -p zod fullstackgtm-mcp
|
|
75
97
|
```
|
|
76
98
|
|
|
99
|
+
If the working directory's project already has the peers in its node_modules,
|
|
100
|
+
the server resolves them from there (peer-dependency semantics) — so this
|
|
101
|
+
works from inside existing projects too.
|
|
102
|
+
|
|
77
103
|
Tools exposed over stdio: `fullstackgtm_audit` (read-only),
|
|
78
104
|
`fullstackgtm_rules`, `fullstackgtm_apply` (requires `approvedOperationIds`).
|
|
79
105
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# fullstackgtm
|
|
2
2
|
|
|
3
|
+
[](https://github.com/fullstackgtm/core/actions/workflows/ci.yml) [](https://www.npmjs.com/package/fullstackgtm) [](./LICENSE)
|
|
4
|
+
|
|
3
5
|
**Plan/apply for your GTM stack.** An open-source framework for managing disparate go-to-market data spread across third-party systems: a canonical CRM/GTM data model, deterministic hygiene audits, reviewable dry-run patch plans, and approval-gated write-back to providers.
|
|
4
6
|
|
|
5
7
|
Think `terraform plan` for your CRM: agents and scripts may *read* everything, but every proposed change becomes a typed patch operation — object, field, before, after, reason, risk — that a human approves before any provider write happens.
|
|
@@ -45,6 +47,39 @@ HUBSPOT_ACCESS_TOKEN=pat-... npx fullstackgtm apply \
|
|
|
45
47
|
|
|
46
48
|
Nothing is ever written without an explicit `--approve`. Operations whose value is a human decision (`requires_human_*` placeholders, e.g. which owner to assign) are refused unless you supply a concrete `--value` override.
|
|
47
49
|
|
|
50
|
+
## From findings to fixes: the suggest chain
|
|
51
|
+
|
|
52
|
+
Most placeholder answers are already derivable from your own CRM data. `suggest` computes them deterministically — account-name matching cross-checked against contact associations — with a confidence level and a written reason per operation, so you (or an agent) approve evidence, not guesses:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
fullstackgtm audit --provider hubspot --save # → Saved plan patch_plan_abc123
|
|
56
|
+
fullstackgtm suggest --plan-id patch_plan_abc123 --provider hubspot --out suggestions.json
|
|
57
|
+
# review suggestions.json: every value carries confidence (high/low/create/none) + a reason
|
|
58
|
+
fullstackgtm plans approve patch_plan_abc123 --values-from suggestions.json # high-confidence only by default
|
|
59
|
+
fullstackgtm apply --plan-id patch_plan_abc123 --provider hubspot
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Widen the bar deliberately: `--min-confidence low` accepts single-signal matches; `--include-creates` accepts `create:<Name>` values — approving one **creates the missing company/account record and links to it** in a single audited operation, so even record creation stays inside the typed, human-approved model. Conflicting or ambiguous evidence always yields *no* suggestion with an explanation, never a guess.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# 3. Hand the findings to whoever owns the CRM: a client-ready report
|
|
66
|
+
npx fullstackgtm report --provider hubspot --client "Acme" --out acme-health.html
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`report` renders the same audit as a deliverable — severity counts up front, a prose summary, per-rule detail with example records, and next steps — as markdown or self-contained HTML (printable, emailable, no external assets).
|
|
70
|
+
|
|
71
|
+
### Working across organizations
|
|
72
|
+
|
|
73
|
+
Consultants and fractional operators hold credentials for several CRMs at once. A profile scopes stored logins *and* stored plans to one organization:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
fullstackgtm --profile acme login hubspot
|
|
77
|
+
fullstackgtm --profile acme audit --provider hubspot --save
|
|
78
|
+
fullstackgtm profiles # list profiles, * marks the active one
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Set `FULLSTACKGTM_PROFILE=acme` to pin a shell (or agent sandbox) to one client. Plans saved under a profile are invisible to every other profile, so a patch plan proposed against one client's CRM can never be applied through another client's credentials.
|
|
82
|
+
|
|
48
83
|
## Built for agents (and the RevOps humans they work for)
|
|
49
84
|
|
|
50
85
|
Every command is designed to compose in an agent loop — deterministic output, machine-readable everywhere, meaningful exit codes:
|
|
@@ -93,28 +128,61 @@ fullstackgtm login hubspot
|
|
|
93
128
|
|
|
94
129
|
# HubSpot, bring-your-own-app OAuth. The browser is used exactly once — the
|
|
95
130
|
# consent grant — captured on a 127.0.0.1 loopback (RFC 8252); the CLI
|
|
96
|
-
# exchanges the code itself and refreshes silently from then on.
|
|
97
|
-
|
|
131
|
+
# exchanges the code itself and refreshes silently from then on. The client
|
|
132
|
+
# secret is read from stdin or an interactive prompt — never as a flag.
|
|
133
|
+
echo "$CLIENT_SECRET" | fullstackgtm login hubspot --oauth --client-id <id>
|
|
98
134
|
# (register http://localhost:8763/callback as a redirect URL on your app)
|
|
99
135
|
|
|
100
136
|
# Salesforce: native device flow — confirm a code on any device, no localhost
|
|
101
137
|
# server, no client secret, silent refresh. Needs a Connected App consumer key
|
|
102
|
-
# with device flow enabled.
|
|
138
|
+
# with device flow enabled (see "Connect your CRM" below).
|
|
103
139
|
fullstackgtm login salesforce --device --client-id <consumer key>
|
|
104
|
-
# ...or a session token directly:
|
|
105
|
-
fullstackgtm login salesforce --
|
|
140
|
+
# ...or a session token directly (token on stdin, never as a flag):
|
|
141
|
+
echo "$SF_SESSION_TOKEN" | fullstackgtm login salesforce --instance-url https://yourorg.my.salesforce.com
|
|
106
142
|
|
|
107
143
|
fullstackgtm logout hubspot # or: salesforce | broker
|
|
108
144
|
```
|
|
109
145
|
|
|
110
146
|
A direct `login hubspot` always wins over a broker pairing, so an operator can override the team default. HubSpot does not support the device-authorization grant or secretless public clients, which is why the bring-your-own-app OAuth path requires client credentials; they are stored locally for silent refresh, the same model as `gcloud` and `aws` CLI profiles.
|
|
111
147
|
|
|
148
|
+
## Connect your CRM
|
|
149
|
+
|
|
150
|
+
What each provider actually requires before `audit --provider <name>` works on your data.
|
|
151
|
+
|
|
152
|
+
### HubSpot: create a private app (~2 minutes, needs super-admin)
|
|
153
|
+
|
|
154
|
+
1. In HubSpot: **Settings → Integrations → Private Apps → Create a private app.**
|
|
155
|
+
2. On the **Scopes** tab, grant the read scopes the audit needs:
|
|
156
|
+
- `crm.objects.owners.read`
|
|
157
|
+
- `crm.objects.companies.read`
|
|
158
|
+
- `crm.objects.contacts.read`
|
|
159
|
+
- `crm.objects.deals.read`
|
|
160
|
+
3. If you plan to **apply** approved operations (not just audit), also grant write scopes for the objects you'll let it touch: `crm.objects.deals.write` (covers `deal.next_step` and other deal fields), plus `crm.objects.contacts.write` / `crm.objects.companies.write` for contact/company patches, and the **Tasks** write scope for `create_task` operations (search "tasks" in the scope picker; naming varies by portal).
|
|
161
|
+
4. Create the app, copy the token (`pat-...`), then: `echo "$TOKEN" | fullstackgtm login hubspot`.
|
|
162
|
+
|
|
163
|
+
If a scope is missing you'll see a `403` mid-run whose body names the exact missing scope (`requiredGranularScopes`) — add it to the private app and re-run. Note that `login` only validates the token itself; it can't tell whether every scope you'll need is granted.
|
|
164
|
+
|
|
165
|
+
### Salesforce: a Connected App (one-time, usually needs an admin)
|
|
166
|
+
|
|
167
|
+
Device-flow login requires a Connected App in your org — if you're not an admin, this is the step to ask one for:
|
|
168
|
+
|
|
169
|
+
1. **Setup → App Manager → New Connected App**, enable OAuth settings.
|
|
170
|
+
2. Check **Enable Device Flow**.
|
|
171
|
+
3. OAuth scopes: **Manage user data via APIs (`api`)** and **Perform requests at any time (`refresh_token`)** — the CLI requests exactly these.
|
|
172
|
+
4. Save (Salesforce can take ~2–10 minutes to propagate a new Connected App), copy the **Consumer Key**, then: `fullstackgtm login salesforce --device --client-id <consumer key>`.
|
|
173
|
+
|
|
174
|
+
Writeback needs no extra OAuth scope — applies are gated by the logged-in user's normal object/field permissions.
|
|
175
|
+
|
|
176
|
+
### Stripe: a restricted key is enough (read-only connector)
|
|
177
|
+
|
|
178
|
+
The Stripe connector only reads customers and subscriptions, and `apply` is read-only by construction. Create a **restricted key** with just **Customers: Read** and **Subscriptions: Read** (Developers → API keys → Create restricted key) instead of pasting a full-access secret key: `echo "$KEY" | fullstackgtm login stripe`.
|
|
179
|
+
|
|
112
180
|
## Concepts
|
|
113
181
|
|
|
114
182
|
| Concept | What it is |
|
|
115
183
|
|---|---|
|
|
116
184
|
| **Canonical snapshot** | Provider-independent view of users, accounts, contacts, deals, activities. Records carry `identities` — `(provider, externalId)` claims — so the same real-world entity can be tracked across several systems. |
|
|
117
|
-
| **Audit rule** | A deterministic function `(context) => { findings, operations }`.
|
|
185
|
+
| **Audit rule** | A deterministic function `(context) => { findings, operations }`. Eleven built-ins cover orphan accounts, ownerless/unlinked/amount-less deals, past close dates, stale pipeline, duplicates, and more — `fullstackgtm rules` lists them all. Write your own in ~10 lines. |
|
|
118
186
|
| **Patch plan** | The dry-run output of an audit: findings plus typed patch operations with before/after values, reasons, risk levels, and approval flags. Always a proposal, never a mutation. |
|
|
119
187
|
| **Connector** | A provider adapter: `fetchSnapshot()` for reads, optional `applyOperation()` for writes. HubSpot and Salesforce reference connectors ship in the package; connectors never drop records they can't fully resolve — the audit flags them instead. |
|
|
120
188
|
| **Patch plan run** | The audit record of one apply attempt: per-operation applied/failed/skipped results. |
|