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/icp.js
CHANGED
|
@@ -55,46 +55,128 @@ export function icpToExploriumFilters(icp) {
|
|
|
55
55
|
return f;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* Explorium /v1/businesses filters from the ICP — the COMPANY (account) side, for
|
|
59
|
+
* sizing the account universe (TAM). Firmographics only, no persona: the count is
|
|
60
|
+
* of matching companies. Field names differ from /v1/prospects (verified live):
|
|
61
|
+
* `country_code` (not company_country_code), `company_size` (same employee bands),
|
|
62
|
+
* `naics_category`. `/v1/businesses` total_results is a real count, capped at
|
|
63
|
+
* 60,000 (see EXPLORIUM_BUSINESS_COUNT_CAP in the connector).
|
|
61
64
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
export function icpToExploriumBusinessFilters(icp) {
|
|
66
|
+
const f = {};
|
|
67
|
+
if (icp.firmographics.geos?.length)
|
|
68
|
+
f.country_code = { values: icp.firmographics.geos };
|
|
69
|
+
if (icp.firmographics.employeeBands?.length)
|
|
70
|
+
f.company_size = { values: icp.firmographics.employeeBands };
|
|
71
|
+
if (icp.firmographics.naics?.length)
|
|
72
|
+
f.naics_category = { values: icp.firmographics.naics };
|
|
73
|
+
return f;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Collapse provider-agnostic employee bands ("51-200","10001+") into a single
|
|
77
|
+
* {min,max} envelope for APIs that take integer bounds (TheirStack). An open
|
|
78
|
+
* top band ("10001+") leaves max undefined.
|
|
79
|
+
*/
|
|
80
|
+
export function employeeBandsToRange(bands) {
|
|
81
|
+
if (!bands?.length)
|
|
82
|
+
return {};
|
|
83
|
+
let min;
|
|
84
|
+
let max;
|
|
85
|
+
let openTop = false;
|
|
86
|
+
for (const band of bands) {
|
|
87
|
+
const plus = /^(\d+)\+$/.exec(band.trim());
|
|
88
|
+
if (plus) {
|
|
89
|
+
const lo = Number(plus[1]);
|
|
90
|
+
if (min === undefined || lo < min)
|
|
91
|
+
min = lo;
|
|
92
|
+
openTop = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const range = /^(\d+)\s*-\s*(\d+)$/.exec(band.trim());
|
|
96
|
+
if (range) {
|
|
97
|
+
const lo = Number(range[1]);
|
|
98
|
+
const hi = Number(range[2]);
|
|
99
|
+
if (min === undefined || lo < min)
|
|
100
|
+
min = lo;
|
|
101
|
+
if (max === undefined || hi > max)
|
|
102
|
+
max = hi;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return { min, max: openTop ? undefined : max };
|
|
106
|
+
}
|
|
77
107
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
108
|
+
* TheirStack company-search filter from the ICP: the technographic targeting that
|
|
109
|
+
* Explorium can't do. `company_technology_slug_or` is the CRM/MAP buying signal
|
|
110
|
+
* (firmographics.technologies); employee bands become min/max bounds; geos become
|
|
111
|
+
* ISO2 codes (uppercased).
|
|
112
|
+
*/
|
|
113
|
+
export function icpToTheirStackFilters(icp) {
|
|
114
|
+
const f = {};
|
|
115
|
+
if (icp.firmographics.technologies?.length) {
|
|
116
|
+
f.company_technology_slug_or = icp.firmographics.technologies.map((t) => t.trim().toLowerCase());
|
|
117
|
+
}
|
|
118
|
+
if (icp.firmographics.geos?.length) {
|
|
119
|
+
f.company_country_code_or = icp.firmographics.geos.map((g) => g.trim().toUpperCase());
|
|
120
|
+
}
|
|
121
|
+
const range = employeeBandsToRange(icp.firmographics.employeeBands);
|
|
122
|
+
if (range.min !== undefined)
|
|
123
|
+
f.min_employee_count = range.min;
|
|
124
|
+
if (range.max !== undefined)
|
|
125
|
+
f.max_employee_count = range.max;
|
|
126
|
+
return f;
|
|
127
|
+
}
|
|
128
|
+
// NOTE: a CRUSTDATA_SENIORITY vocab table lived here until 2026-07-02 — the
|
|
129
|
+
// `current_seniority_levels` filter is broken upstream (see the live findings
|
|
130
|
+
// on icpToCrustdataFilters below), so job levels are no longer sent to the
|
|
131
|
+
// provider at all. Resurrect the table from git history if pipe0 fixes it.
|
|
132
|
+
/**
|
|
133
|
+
* ICP industry keyword → LinkedIn industry names Crustdata filters on.
|
|
134
|
+
*
|
|
135
|
+
* LinkedIn renamed its industry taxonomy (v1 → v2, ~2022), and data vendors
|
|
136
|
+
* normalize to one generation or the other. Sending ONLY the v2 names risks a
|
|
137
|
+
* zero-match when Crustdata stores v1 (and vice-versa) — observed live: a RevOps
|
|
138
|
+
* ICP whose only firmographic constraint was the v2 names returned 0 even though
|
|
139
|
+
* the title-only search returned plenty. So each cluster sends BOTH generations
|
|
140
|
+
* (OR-matched within the field): v2 ("Software Development", "IT Services and IT
|
|
141
|
+
* Consulting", "Technology, Information and Internet") AND v1 ("Computer
|
|
142
|
+
* Software", "Information Technology & Services", "Internet").
|
|
81
143
|
*/
|
|
82
144
|
const CRUSTDATA_INDUSTRY = {
|
|
83
|
-
software: [
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
software: [
|
|
146
|
+
"Software Development",
|
|
147
|
+
"Computer Software",
|
|
148
|
+
"IT Services and IT Consulting",
|
|
149
|
+
"Information Technology & Services",
|
|
150
|
+
"Information Technology and Services",
|
|
151
|
+
"Technology, Information and Internet",
|
|
152
|
+
"Internet",
|
|
153
|
+
],
|
|
154
|
+
saas: ["Software Development", "Computer Software", "IT Services and IT Consulting", "Information Technology & Services"],
|
|
155
|
+
"information technology & services": ["Information Technology & Services", "IT Services and IT Consulting"],
|
|
156
|
+
"information technology and services": ["Information Technology & Services", "IT Services and IT Consulting"],
|
|
157
|
+
internet: ["Internet", "Technology, Information and Internet"],
|
|
88
158
|
fintech: ["Financial Services"],
|
|
89
159
|
"financial services": ["Financial Services"],
|
|
90
160
|
};
|
|
91
161
|
/**
|
|
92
162
|
* pipe0 Crustdata people-search config.filters from the ICP. `current_job_titles`
|
|
93
163
|
* matches real LinkedIn title strings (case-sensitive), so keywords are Title
|
|
94
|
-
* Cased.
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
164
|
+
* Cased. Industry is mapped to Crustdata's controlled vocab via the table above.
|
|
165
|
+
*
|
|
166
|
+
* LIVE FINDINGS (2026-06-26, re-confirmed 2026-07-02 with fresh credits):
|
|
167
|
+
* `current_job_titles` works; `current_title` is rejected (422). The culprit
|
|
168
|
+
* that zeroed the full ICP filter is `current_seniority_levels`: ANY non-empty
|
|
169
|
+
* `include` returns 0 results through pipe0's people:profiles:crustdata@1 —
|
|
170
|
+
* including Crustdata's own documented values ("CXO", "Director",
|
|
171
|
+
* "Vice President"), lowercase, and SNAKE_CASE variants — while the identical
|
|
172
|
+
* search without it returns results. (An array instead of the
|
|
173
|
+
* {include, exclude} object is a 422, so the shape was right; the filter is
|
|
174
|
+
* broken upstream.) So job levels are NOT sent to the provider: persona
|
|
175
|
+
* seniority is enforced by fit scoring (scoreProspectAgainstIcp weights
|
|
176
|
+
* jobLevels), which was always the precision backstop. `locations` and
|
|
177
|
+
* `current_employers_linkedin_industries` (both-taxonomy hedge) are live-
|
|
178
|
+
* verified working in combination with titles. Note fit-scoring backs up
|
|
179
|
+
* PERSONA precision but NOT industry, so the industry filter is load-bearing.
|
|
98
180
|
*/
|
|
99
181
|
export function icpToCrustdataFilters(icp) {
|
|
100
182
|
const f = {};
|
|
@@ -103,11 +185,6 @@ export function icpToCrustdataFilters(icp) {
|
|
|
103
185
|
if (icp.firmographics.geos?.length) {
|
|
104
186
|
f.locations = icp.firmographics.geos.map((g) => COUNTRY_NAMES[g.toLowerCase()] ?? g);
|
|
105
187
|
}
|
|
106
|
-
if (icp.persona.jobLevels?.length) {
|
|
107
|
-
const include = [...new Set(icp.persona.jobLevels.map((l) => CRUSTDATA_SENIORITY[l.toLowerCase()]).filter(Boolean))];
|
|
108
|
-
if (include.length)
|
|
109
|
-
f.current_seniority_levels = { include, exclude: [] };
|
|
110
|
-
}
|
|
111
188
|
if (icp.firmographics.industries?.length) {
|
|
112
189
|
const inds = [
|
|
113
190
|
...new Set(icp.firmographics.industries.flatMap((i) => CRUSTDATA_INDUSTRY[i.toLowerCase()] ?? [titleCase(i)])),
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { activeProfile, credentialsDir, credentialsPath, DEFAULT_PROFILE, delete
|
|
|
14
14
|
export { generateDemoSnapshot, type DemoSnapshotOptions } from "./demo.ts";
|
|
15
15
|
export { buildEnrichPlan, createFileEnrichRunStore, DEFAULT_STALE_DAYS, ENRICH_CONFIG_FILE_NAME, enrichRunId, inferIngestObjectType, ingestKeyValue, latestStamps, loadEnrichConfig, matchSourceRecord, parseCsv, parseEnrichConfig, resolveCrmField, selectStaleWork, sourceValueAt, stagedSourceRecords, staleDaysFor, type BuildEnrichPlanOptions, type EnrichAmbiguity, type EnrichConfig, type EnrichCounts, type EnrichFieldConfig, type EnrichMatchConfig, type EnrichMode, type EnrichObjectType, type EnrichPlanResult, type EnrichRun, type EnrichRunStore, type EnrichSourceConfig, type EnrichSourceRecord, type EnrichStamp, type EnrichWorkItem, type MatchOutcome, } from "./enrich.ts";
|
|
16
16
|
export { scaffoldWorkspace, starterEnrichConfig, starterIcp, starterPlaybook, type InitProvider, type InitSource, type ScaffoldFile, type ScaffoldOptions, } from "./init.ts";
|
|
17
|
+
export { appendCoverage, classifyAccount, classifyCoverage, computeCoverage, coverageCountsFromSnapshot, coveredAccounts, coverageToText, crmCheckableCriteria, deriveAcvFromClosedWon, deriveBuyersPerAccount, estimateTam, loadTamModel, projectEta, readCoverageTimeline, saveTamModel, tamDir, tamReportToMarkdown, type AccountTamClass, type AcvBasis, type DerivedAcv, type DerivedBuyers, type EstimateTamInput, type TamClassified, type TamCoverage, type TamCoverageCounts, type TamCrossCheck, type TamEta, type TamModel, type TamTargeting, type TamUniverse, } from "./tam.ts";
|
|
17
18
|
export { apolloPullKeysForAppend, apolloPullKeysForRefresh, createApolloClient, pullApolloRecords, type ApolloClient, type ApolloClientOptions, type ApolloPullKey, type ApolloPullResult, } from "./enrichApollo.ts";
|
|
18
19
|
export { diffFindings, diffSnapshots, diffToMarkdown, type CollectionDiff, type FieldChange, type FindingsDrift, type RecordChange, type SnapshotDiff, } from "./diff.ts";
|
|
19
20
|
export { mergeSnapshots, type MergeConflict, type MergeMatch, type MergeReport, type MergeSuggestion, } from "./merge.ts";
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export { activeProfile, credentialsDir, credentialsPath, DEFAULT_PROFILE, delete
|
|
|
14
14
|
export { generateDemoSnapshot } from "./demo.js";
|
|
15
15
|
export { buildEnrichPlan, createFileEnrichRunStore, DEFAULT_STALE_DAYS, ENRICH_CONFIG_FILE_NAME, enrichRunId, inferIngestObjectType, ingestKeyValue, latestStamps, loadEnrichConfig, matchSourceRecord, parseCsv, parseEnrichConfig, resolveCrmField, selectStaleWork, sourceValueAt, stagedSourceRecords, staleDaysFor, } from "./enrich.js";
|
|
16
16
|
export { scaffoldWorkspace, starterEnrichConfig, starterIcp, starterPlaybook, } from "./init.js";
|
|
17
|
+
export { appendCoverage, classifyAccount, classifyCoverage, computeCoverage, coverageCountsFromSnapshot, coveredAccounts, coverageToText, crmCheckableCriteria, deriveAcvFromClosedWon, deriveBuyersPerAccount, estimateTam, loadTamModel, projectEta, readCoverageTimeline, saveTamModel, tamDir, tamReportToMarkdown, } from "./tam.js";
|
|
17
18
|
export { apolloPullKeysForAppend, apolloPullKeysForRefresh, createApolloClient, pullApolloRecords, } from "./enrichApollo.js";
|
|
18
19
|
export { diffFindings, diffSnapshots, diffToMarkdown, } from "./diff.js";
|
|
19
20
|
export { mergeSnapshots, } from "./merge.js";
|
package/dist/init.js
CHANGED
|
@@ -28,6 +28,9 @@ export function starterIcp() {
|
|
|
28
28
|
industries: ["software", "saas"],
|
|
29
29
|
employeeBands: ["51-200", "201-500", "501-1000"],
|
|
30
30
|
geos: ["us"],
|
|
31
|
+
// Technographic targeting (the real RevOps signal): companies that USE a
|
|
32
|
+
// CRM/MAP. Drives `tam estimate|accounts --source theirstack`.
|
|
33
|
+
technologies: ["salesforce", "hubspot", "pipedrive"],
|
|
31
34
|
},
|
|
32
35
|
persona: {
|
|
33
36
|
jobLevels: ["vp", "director", "manager"],
|
package/dist/judge.d.ts
CHANGED
|
@@ -230,6 +230,8 @@ export declare function judgeSignals(opts: {
|
|
|
230
230
|
promptTemplate?: string;
|
|
231
231
|
llm?: LlmCallOptions;
|
|
232
232
|
now?: Date;
|
|
233
|
+
/** Per-account progress (presentation only — a throwing callback never fails the run). */
|
|
234
|
+
onAccount?: (done: number, total: number, domain: string) => void;
|
|
233
235
|
}): Promise<JudgeDecision[]>;
|
|
234
236
|
export declare function judgeDir(baseDir?: string): string;
|
|
235
237
|
export interface JudgeStore {
|
package/dist/judge.js
CHANGED
|
@@ -438,6 +438,12 @@ export async function judgeSignals(opts) {
|
|
|
438
438
|
}
|
|
439
439
|
const decisions = [];
|
|
440
440
|
for (const [domain, signals] of byAccount) {
|
|
441
|
+
try {
|
|
442
|
+
opts.onAccount?.(decisions.length, byAccount.size, domain);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// progress is presentation-only
|
|
446
|
+
}
|
|
441
447
|
// Memory + fit stay gated on --with-history (scoring semantics unchanged).
|
|
442
448
|
const recentlyTouched = opts.withHistory && opts.snapshot ? accountRecentlyTouched(domain, opts.snapshot, now) : false;
|
|
443
449
|
const bestContact = opts.withHistory && opts.icp && opts.snapshot ? bestContactForAccount(domain, opts.snapshot) : undefined;
|
package/dist/judgeEval.d.ts
CHANGED
|
@@ -110,6 +110,13 @@ export declare function defaultJudgeFn(opts?: {
|
|
|
110
110
|
icp?: Icp;
|
|
111
111
|
now?: Date;
|
|
112
112
|
}): EvalJudgeFn;
|
|
113
|
+
/**
|
|
114
|
+
* The default golden set's clock. Every `firstSeen` below is relative to this
|
|
115
|
+
* instant, so grading MUST pin `now: new Date(DEFAULT_GOLDEN_NOW_ISO)` —
|
|
116
|
+
* grading against wall time lets freshness decay rot the "fresh → send" rows,
|
|
117
|
+
* and the gate starts failing on a calendar date instead of a code change.
|
|
118
|
+
*/
|
|
119
|
+
export declare const DEFAULT_GOLDEN_NOW_ISO = "2026-06-23T12:00:00.000Z";
|
|
113
120
|
/**
|
|
114
121
|
* A starter labeled set covering the four rubric corners, so `icp eval --golden
|
|
115
122
|
* default` runs offline and the deterministic baseline passes it (>= the default
|
package/dist/judgeEval.js
CHANGED
|
@@ -85,7 +85,14 @@ export function defaultJudgeFn(opts = {}) {
|
|
|
85
85
|
}
|
|
86
86
|
// ---------------------------------------------------------------------------
|
|
87
87
|
// The default golden set (ships inline so `icp eval --golden default` is free)
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* The default golden set's clock. Every `firstSeen` below is relative to this
|
|
90
|
+
* instant, so grading MUST pin `now: new Date(DEFAULT_GOLDEN_NOW_ISO)` —
|
|
91
|
+
* grading against wall time lets freshness decay rot the "fresh → send" rows,
|
|
92
|
+
* and the gate starts failing on a calendar date instead of a code change.
|
|
93
|
+
*/
|
|
94
|
+
export const DEFAULT_GOLDEN_NOW_ISO = "2026-06-23T12:00:00.000Z";
|
|
95
|
+
const GOLD_NOW_ISO = DEFAULT_GOLDEN_NOW_ISO;
|
|
89
96
|
function goldSignal(over) {
|
|
90
97
|
const domain = normalizeAccountDomain(over.accountDomain);
|
|
91
98
|
return {
|
package/dist/marketClassify.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type ClassifyMarketOptions = {
|
|
|
11
11
|
/** Captures directory override (tests); defaults to the profile market home. */
|
|
12
12
|
capturesDir?: string;
|
|
13
13
|
now?: () => Date;
|
|
14
|
+
/** Per-vendor progress (presentation only — a throwing callback never fails the run). */
|
|
15
|
+
onVendor?: (done: number, total: number, vendorId: string) => void;
|
|
14
16
|
};
|
|
15
17
|
export type ClassifyMarketResult = {
|
|
16
18
|
set: ObservationSet;
|
package/dist/marketClassify.js
CHANGED
|
@@ -92,7 +92,13 @@ export async function classifyMarket(config, options) {
|
|
|
92
92
|
const claimIds = config.claims.map((claim) => claim.id);
|
|
93
93
|
const observations = [];
|
|
94
94
|
const retriedVendorIds = [];
|
|
95
|
-
for (const vendorId of vendorIds) {
|
|
95
|
+
for (const [vendorIndex, vendorId] of vendorIds.entries()) {
|
|
96
|
+
try {
|
|
97
|
+
options.onVendor?.(vendorIndex, vendorIds.length, vendorId);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// progress is presentation-only
|
|
101
|
+
}
|
|
96
102
|
const vendor = config.vendors.find((candidate) => candidate.id === vendorId);
|
|
97
103
|
if (!vendor)
|
|
98
104
|
throw new Error(`Unknown vendor "${vendorId}"`);
|
package/dist/mcp-bin.js
CHANGED
|
@@ -7,8 +7,8 @@ const MISSING_PEER_HELP = `The MCP server needs the optional peer dependencies @
|
|
|
7
7
|
In a project:
|
|
8
8
|
npm install fullstackgtm @modelcontextprotocol/sdk zod
|
|
9
9
|
|
|
10
|
-
Zero-install:
|
|
11
|
-
npx -
|
|
10
|
+
Zero-install (the fullstackgtm-mcp wrapper package bundles the peers):
|
|
11
|
+
npx -y fullstackgtm-mcp`;
|
|
12
12
|
function isMissingPeerError(error) {
|
|
13
13
|
if (!(error instanceof Error))
|
|
14
14
|
return false;
|