fullstackgtm 0.50.0 → 0.50.1
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 +24 -0
- package/README.md +7 -0
- package/dist/cli/audit.js +6 -1
- package/dist/cli/auth.js +25 -0
- package/dist/cli/backfill.js +4 -0
- package/dist/cli/call.js +28 -6
- package/dist/cli/draft.js +11 -1
- package/dist/cli/enrich.js +3 -2
- package/dist/cli/fix.js +6 -3
- package/dist/cli/help.js +22 -22
- package/dist/cli/icp.js +15 -1
- package/dist/cli/market.js +14 -1
- package/dist/cli/planOutput.d.ts +5 -0
- package/dist/cli/planOutput.js +27 -0
- package/dist/cli/plans.js +184 -33
- package/dist/cli/schedule.js +22 -11
- package/dist/cli/signals.js +22 -3
- package/dist/cli/tam.js +10 -1
- package/dist/cli/ui.js +14 -6
- package/dist/connectors/hubspot.d.ts +4 -0
- package/dist/connectors/hubspot.js +36 -18
- package/dist/hostedPatchPlan.js +6 -1
- package/package.json +1 -1
- package/src/cli/audit.ts +5 -1
- package/src/cli/auth.ts +24 -0
- package/src/cli/backfill.ts +3 -0
- package/src/cli/call.ts +25 -6
- package/src/cli/draft.ts +9 -1
- package/src/cli/enrich.ts +3 -2
- package/src/cli/fix.ts +5 -3
- package/src/cli/help.ts +22 -22
- package/src/cli/icp.ts +13 -1
- package/src/cli/market.ts +12 -1
- package/src/cli/planOutput.ts +30 -0
- package/src/cli/plans.ts +174 -29
- package/src/cli/schedule.ts +21 -15
- package/src/cli/signals.ts +22 -3
- package/src/cli/tam.ts +8 -1
- package/src/cli/ui.ts +15 -6
- package/src/connectors/hubspot.ts +41 -17
- package/src/hostedPatchPlan.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ The path to 1.0 is planned in [docs/roadmap-to-1.0.md](https://github.com/fullst
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.50.1] — 2026-07-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Decision-first CLI presentation across plan previews, plan queues, apply
|
|
15
|
+
outcomes, doctor, schedules, signals, ICP, market, TAM, and call analysis.
|
|
16
|
+
Human defaults use compact cards and ranked summaries; `--verbose` restores
|
|
17
|
+
full audit detail and `--json` retains the stable machine contract.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- HubSpot 429/5xx responses now retry with bounded backoff, honor
|
|
22
|
+
`Retry-After`, and report accurate live throttle feedback.
|
|
23
|
+
- Multi-line progress boards no longer scroll intermediate paint frames into
|
|
24
|
+
terminal history.
|
|
25
|
+
- `plans sync` reconciles historical workspaces concurrently with visible
|
|
26
|
+
progress, skips absent legacy mirrors, and avoids echoing hosted-origin
|
|
27
|
+
receipts back to hosted.
|
|
28
|
+
- CLI apply reports only the exact approved receipt subset to hosted while
|
|
29
|
+
retaining excluded rows in the complete local audit trail.
|
|
30
|
+
- Plan and apply output now distinguish approved, applied, excluded, skipped,
|
|
31
|
+
and failed operations without exposing the immutable document's stale
|
|
32
|
+
creation-time status as the current lifecycle state.
|
|
33
|
+
|
|
10
34
|
## [0.50.0] — 2026-07-10
|
|
11
35
|
|
|
12
36
|
### Added
|
package/README.md
CHANGED
|
@@ -243,6 +243,13 @@ replica learns what happened rather than inferring it from plan status. A
|
|
|
243
243
|
conflicting plan body or approval revision is surfaced for review and is never
|
|
244
244
|
merged by expanding authority.
|
|
245
245
|
|
|
246
|
+
Interactive command output is decision-first by default: compact cards show
|
|
247
|
+
status, selected scope, expected effect, per-record outcomes, hosted links, and
|
|
248
|
+
the next safe command. Use `--verbose` on supported human-facing commands for
|
|
249
|
+
the complete findings/evidence/payload document; use `--json` for the stable
|
|
250
|
+
machine-readable contract. Progress and warnings remain on stderr, while JSON
|
|
251
|
+
and requested report data remain clean on stdout.
|
|
252
|
+
|
|
246
253
|
Local signing keys and HMAC digests never upload. Operation before/after values
|
|
247
254
|
do upload to the paired organization because they are required for informed
|
|
248
255
|
review and hosted execution.
|
package/dist/cli/audit.js
CHANGED
|
@@ -11,6 +11,7 @@ import { auditReportToHtml, auditReportToMarkdown } from "../report.js";
|
|
|
11
11
|
import { reportCounts, reportCrm, reportFindings } from "../runReport.js";
|
|
12
12
|
import { connectorFor, numericOption, option, readSnapshot, saveRequested, selectedRules } from "./shared.js";
|
|
13
13
|
import { colorEnabled, createChecklist, formatCount, paint, scoreColor, sparkline, stylizePlanMarkdown, table } from "./ui.js";
|
|
14
|
+
import { compactPlan, verbosePlanRequested } from "./planOutput.js";
|
|
14
15
|
const SEVERITY_RANK = {
|
|
15
16
|
info: 0,
|
|
16
17
|
warning: 1,
|
|
@@ -241,11 +242,15 @@ export async function audit(args) {
|
|
|
241
242
|
if (args.includes("--json")) {
|
|
242
243
|
console.log(JSON.stringify(plan, null, 2));
|
|
243
244
|
}
|
|
245
|
+
else if (!verbosePlanRequested(args)) {
|
|
246
|
+
console.log(compactPlan(plan, { saved: saveRequested(args) }));
|
|
247
|
+
console.error(`\n${auditNextStep(args, plan)}`);
|
|
248
|
+
}
|
|
244
249
|
else {
|
|
245
250
|
// Default to the summary view (rule table + counts); the full per-operation
|
|
246
251
|
// dump is opt-in via --full or, for a deliverable, `report`. (dx-punch-list #3)
|
|
247
252
|
// Interactive terminals get the styled rendering; piped output is unchanged.
|
|
248
|
-
console.log(stylizePlanMarkdown(patchPlanToMarkdown(plan, { summary:
|
|
253
|
+
console.log(stylizePlanMarkdown(patchPlanToMarkdown(plan, { summary: false }), paint(colorEnabled(process.stdout))));
|
|
249
254
|
console.error(`\n${auditNextStep(args, plan)}`);
|
|
250
255
|
}
|
|
251
256
|
if (threshold &&
|
package/dist/cli/auth.js
CHANGED
|
@@ -579,6 +579,31 @@ export async function doctorCommand(args) {
|
|
|
579
579
|
const healthLine = workspace.auditCount === 0
|
|
580
580
|
? p.dim("no saved audits yet (fullstackgtm audit --provider <name> --save starts the timeline)")
|
|
581
581
|
: `${scoreColor(workspace.healthScore ?? 0, p)}/100${delta} — ${workspace.auditCount} audit(s) saved, last ${workspace.lastAuditAt}${healthSparkline(workspace.profile, p.enabled)}`;
|
|
582
|
+
const connectedProviders = Object.entries(report.providers).filter(([, provider]) => provider.source !== "none");
|
|
583
|
+
const blockers = [
|
|
584
|
+
...(!report.node.ok ? [`Node v${report.node.version} is unsupported; install ${report.node.required}.`] : []),
|
|
585
|
+
...(connectedProviders.length === 0 ? ["No CRM connected."] : []),
|
|
586
|
+
...(workspace.pendingPlans.length > 0 ? [`${workspace.pendingPlans.length} plan${workspace.pendingPlans.length === 1 ? "" : "s"} awaiting approval.`] : []),
|
|
587
|
+
];
|
|
588
|
+
if (!args.includes("--verbose")) {
|
|
589
|
+
const ready = report.node.ok && connectedProviders.length > 0;
|
|
590
|
+
const compact = [
|
|
591
|
+
`${ready ? p.green("Ready") : p.yellow("Setup needed")} · ${report.package.name} ${report.package.version} · profile ${report.profile}`,
|
|
592
|
+
`CRM ${connectedProviders.length > 0 ? connectedProviders.map(([name]) => name).join(", ") : "not connected"}`,
|
|
593
|
+
`Health ${healthLine}`,
|
|
594
|
+
`Plans ${workspace.pendingPlans.length === 0 ? "none awaiting approval" : `${workspace.pendingPlans.length} awaiting approval · ${workspace.pendingPlans[0].id}`}`,
|
|
595
|
+
...(blockers.length > 0 ? ["", "Attention", ...blockers.map((blocker) => ` ${blocker}`)] : []),
|
|
596
|
+
"",
|
|
597
|
+
"Next",
|
|
598
|
+
...nextSteps.map((step) => ` ${step}`),
|
|
599
|
+
"",
|
|
600
|
+
"Run `fullstackgtm doctor --verbose` for credential paths and optional tooling checks.",
|
|
601
|
+
];
|
|
602
|
+
console.log(p.enabled ? box(compact, p, "Workspace readiness").join("\n") : compact.join("\n"));
|
|
603
|
+
if (!report.node.ok)
|
|
604
|
+
process.exitCode = 1;
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
582
607
|
const lines = [
|
|
583
608
|
`Package: ${p.bold(`${report.package.name} ${report.package.version}`)}`,
|
|
584
609
|
`Node: v${report.node.version} (${report.node.required} required) ${mark(report.node.ok)}`,
|
package/dist/cli/backfill.js
CHANGED
|
@@ -14,6 +14,7 @@ import { progressReporter } from "../runReport.js";
|
|
|
14
14
|
import { unknownSubcommandError } from "./suggest.js";
|
|
15
15
|
import { option, readSnapshot, saveRequested } from "./shared.js";
|
|
16
16
|
import { colorEnabled, createProgressRenderer, paint, stylizePlanMarkdown, table } from "./ui.js";
|
|
17
|
+
import { compactPlan, verbosePlanRequested } from "./planOutput.js";
|
|
17
18
|
/** STRIPE_SECRET_KEY env ∨ stored `login stripe` credential (same ladder as `--provider stripe`). */
|
|
18
19
|
function resolveStripeKey() {
|
|
19
20
|
const key = process.env.STRIPE_SECRET_KEY ?? getCredential("stripe")?.accessToken;
|
|
@@ -73,6 +74,9 @@ export async function backfillCommand(args) {
|
|
|
73
74
|
if (rest.includes("--json")) {
|
|
74
75
|
console.log(JSON.stringify({ plan: result.plan, counts: result.counts, unmatched: result.unmatched, proposedAccounts: result.proposedAccounts }, null, 2));
|
|
75
76
|
}
|
|
77
|
+
else if (!verbosePlanRequested(rest)) {
|
|
78
|
+
console.log(compactPlan(result.plan, { saved: save && result.plan.operations.length > 0 }));
|
|
79
|
+
}
|
|
76
80
|
else {
|
|
77
81
|
// TTY-only styling; piped output stays byte-identical plain text.
|
|
78
82
|
console.log(stylizePlanMarkdown(patchPlanToMarkdown(result.plan), paint(colorEnabled(process.stdout))));
|
package/dist/cli/call.js
CHANGED
|
@@ -161,11 +161,13 @@ score always needs a key (scoring is LLM work).`);
|
|
|
161
161
|
console.log(JSON.stringify(parsed, null, 2));
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
|
-
console.log(`Call ${parsed.id}${parsed.title ? ` — ${parsed.title}` : ""}
|
|
165
|
-
console.log(`${parsed.segments.length} segments · ${parsed.insights.length} insights
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
console.log(`Call ${parsed.id}${parsed.title ? ` — ${parsed.title}` : ""}`);
|
|
165
|
+
console.log(`${parsed.segments.length} segments · ${parsed.insights.length} insights · ${parsed.summary.highImportance} high priority`);
|
|
166
|
+
const visible = rest.includes("--verbose") ? parsed.insights : parsed.insights.filter((insight) => insight.importance >= 3);
|
|
167
|
+
for (const insight of visible)
|
|
168
|
+
console.log(`\n${insight.type.replaceAll("_", " ")} · priority ${insight.importance}\n ${insight.text.replace(/\s+/g, " ").trim()}`);
|
|
169
|
+
if (!rest.includes("--verbose") && visible.length < parsed.insights.length)
|
|
170
|
+
console.log(`\n${parsed.insights.length - visible.length} lower-priority insight(s) hidden; use --verbose to show all.`);
|
|
169
171
|
return;
|
|
170
172
|
}
|
|
171
173
|
if (subcommand === "link") {
|
|
@@ -284,11 +286,31 @@ score always needs a key (scoring is LLM work).`);
|
|
|
284
286
|
console.log(JSON.stringify(scorecard, null, 2));
|
|
285
287
|
return;
|
|
286
288
|
}
|
|
287
|
-
console.log(renderScorecard(scorecard, title));
|
|
289
|
+
console.log(rest.includes("--verbose") ? renderScorecard(scorecard, title) : renderCompactScorecard(scorecard, title));
|
|
288
290
|
return;
|
|
289
291
|
}
|
|
290
292
|
throw new Error(`call supports: parse, classify, link, plan, score (got ${subcommand ?? "nothing"})`);
|
|
291
293
|
}
|
|
294
|
+
function renderCompactScorecard(scorecard, title) {
|
|
295
|
+
const lines = [
|
|
296
|
+
`${title ?? "Call"} · ${scorecard.overallScore}/${scorecard.scale}${scorecard.band ? ` · ${scorecard.band.label}` : ""}`,
|
|
297
|
+
scorecard.rubricName ? `${scorecard.rubricName}${scorecard.callType ? ` (${scorecard.callType})` : ""}` : "",
|
|
298
|
+
"",
|
|
299
|
+
].filter((line, index) => line || index === 2);
|
|
300
|
+
for (const dimension of scorecard.dimensions) {
|
|
301
|
+
lines.push(`${dimension.score}/${dimension.maxScore} ${dimension.name}`);
|
|
302
|
+
lines.push(` ${dimension.coachingNote.replace(/\s+/g, " ").trim()}`);
|
|
303
|
+
}
|
|
304
|
+
if (scorecard.missedItems.length) {
|
|
305
|
+
lines.push("", "Focus next");
|
|
306
|
+
for (const item of scorecard.missedItems)
|
|
307
|
+
lines.push(` ${item}`);
|
|
308
|
+
}
|
|
309
|
+
if (scorecard.highlights.length)
|
|
310
|
+
lines.push("", `Strengths: ${scorecard.highlights.join(" · ")}`);
|
|
311
|
+
lines.push("", "Use --verbose for the full coaching scorecard.");
|
|
312
|
+
return lines.join("\n");
|
|
313
|
+
}
|
|
292
314
|
function renderScorecard(scorecard, title) {
|
|
293
315
|
const bandText = scorecard.band ? ` — ${scorecard.band.label}` : "";
|
|
294
316
|
const rubricLine = scorecard.rubricName ? `Rubric: ${scorecard.rubricName}${scorecard.callType ? ` (${scorecard.callType})` : ""}` : "";
|
package/dist/cli/draft.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createFileSignalStore } from "../signals.js";
|
|
|
7
7
|
import { createFileJudgeStore } from "../judge.js";
|
|
8
8
|
import { authorOpeners, DEFAULT_DRAFT_PROMPT, DRAFT_CHANNELS, draft } from "../draft.js";
|
|
9
9
|
import { numericOption, option, resolveLlmBaseUrls, saveRequested } from "./shared.js";
|
|
10
|
+
import { compactPlan, verbosePlanRequested } from "./planOutput.js";
|
|
10
11
|
/**
|
|
11
12
|
* `draft` — author ONE trigger-grounded opener per hot judge decision as a
|
|
12
13
|
* governed create_task plan. Structurally a proposal: never sends, never writes
|
|
@@ -68,7 +69,16 @@ LLM key it emits a clearly-labeled stub rather than fake authored copy.`);
|
|
|
68
69
|
channel,
|
|
69
70
|
openers,
|
|
70
71
|
});
|
|
71
|
-
|
|
72
|
+
if (args.includes("--json")) {
|
|
73
|
+
console.log(JSON.stringify({ drafts, rejected }, null, 2));
|
|
74
|
+
}
|
|
75
|
+
else if (verbosePlanRequested(args)) {
|
|
76
|
+
console.log(JSON.stringify({ drafts, rejected }, null, 2));
|
|
77
|
+
console.log(compactPlan(plan, { saved: save }));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log(compactPlan(plan, { saved: save }));
|
|
81
|
+
}
|
|
72
82
|
const stale = drafts.filter((d) => d.staleTrigger);
|
|
73
83
|
console.error(`${drafts.length} opener(s) staged as create_task proposals (channel ${channel}, min score ${minScore})` +
|
|
74
84
|
`${rejected.length ? `; ${rejected.length} rejected (ungrounded first line)` : ""}` +
|
package/dist/cli/enrich.js
CHANGED
|
@@ -22,6 +22,7 @@ import { isOptionValue, loadIcp, numericOption, option, readSnapshot, saveReques
|
|
|
22
22
|
import { providerKey } from "./tam.js";
|
|
23
23
|
import { unknownSubcommandError } from "./suggest.js";
|
|
24
24
|
import { colorEnabled, createProgressRenderer, createStatusLine, formatBar, formatDuration, paint } from "./ui.js";
|
|
25
|
+
import { compactPlan, verbosePlanRequested } from "./planOutput.js";
|
|
25
26
|
/**
|
|
26
27
|
* The enrich layer: governed append/refresh of third-party data (Apollo pull,
|
|
27
28
|
* Clay ingest) into the CRM through the normal dry-run → approval → apply
|
|
@@ -267,7 +268,7 @@ are phase 2. Recurring execution is the scheduler's job; enrich has no cron.`);
|
|
|
267
268
|
console.log(JSON.stringify({ plan: result.plan, counts: result.counts, estCostUsd: result.estCostUsd, meter: headroom }, null, 2));
|
|
268
269
|
}
|
|
269
270
|
else {
|
|
270
|
-
console.log(patchPlanToMarkdown(result.plan));
|
|
271
|
+
console.log(verbosePlanRequested(rest) ? patchPlanToMarkdown(result.plan) : compactPlan(result.plan));
|
|
271
272
|
console.log(meterLine);
|
|
272
273
|
if (gaugeLine)
|
|
273
274
|
console.log(gaugeLine);
|
|
@@ -460,7 +461,7 @@ are phase 2. Recurring execution is the scheduler's job; enrich has no cron.`);
|
|
|
460
461
|
console.log(JSON.stringify(result.plan, null, 2));
|
|
461
462
|
}
|
|
462
463
|
else {
|
|
463
|
-
console.log(patchPlanToMarkdown(result.plan));
|
|
464
|
+
console.log(verbosePlanRequested(rest) ? patchPlanToMarkdown(result.plan) : compactPlan(result.plan));
|
|
464
465
|
console.log(formatEnrichCounts(result.counts, result.ambiguities.length));
|
|
465
466
|
console.log("\nDry run — nothing written. Re-run with --save to persist the plan and the run record.");
|
|
466
467
|
}
|
package/dist/cli/fix.js
CHANGED
|
@@ -20,6 +20,7 @@ import { APPLY_STAGES, composeListeners, createProgressEmitter } from "../progre
|
|
|
20
20
|
import { progressReporter } from "../runReport.js";
|
|
21
21
|
import { confirmRequested, connectorFor, isOptionValue, numericOption, option, readSnapshot, repeatedOption, saveRequested } from "./shared.js";
|
|
22
22
|
import { box, colorEnabled, createProgressRenderer, paint } from "./ui.js";
|
|
23
|
+
import { compactPlan, verbosePlanRequested } from "./planOutput.js";
|
|
23
24
|
/**
|
|
24
25
|
* The resolve gate: exit 0 = safe to create, exit 2 = match found (exists or
|
|
25
26
|
* ambiguous — do NOT blind-create), exit 1 = error. Built for sync jobs and
|
|
@@ -101,9 +102,12 @@ async function emitPlan(plan, args) {
|
|
|
101
102
|
if (args.includes("--json")) {
|
|
102
103
|
console.log(JSON.stringify(plan, null, 2));
|
|
103
104
|
}
|
|
104
|
-
else {
|
|
105
|
+
else if (verbosePlanRequested(args)) {
|
|
105
106
|
console.log(patchPlanToMarkdown(plan));
|
|
106
107
|
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(compactPlan(plan, { saved: saveRequested(args) }));
|
|
110
|
+
}
|
|
107
111
|
}
|
|
108
112
|
/**
|
|
109
113
|
* Governed duplicate cleanup: group by a normalized identity key, propose one
|
|
@@ -170,8 +174,7 @@ export async function reassignCommand(args) {
|
|
|
170
174
|
for (const plan of plans) {
|
|
171
175
|
if (store)
|
|
172
176
|
await store.save(plan);
|
|
173
|
-
console.log(
|
|
174
|
-
console.log(` ${plan.summary}`);
|
|
177
|
+
console.log(verbosePlanRequested(args) ? patchPlanToMarkdown(plan) : compactPlan(plan, { saved: Boolean(store) }));
|
|
175
178
|
}
|
|
176
179
|
if (store) {
|
|
177
180
|
console.log(`\nSaved ${plans.length} plan(s). For each: \`fullstackgtm plans show <id>\`, \`fullstackgtm plans approve <id> --operations <ids|all>\`, then \`fullstackgtm apply --plan-id <id> --provider <name>\`.`);
|
package/dist/cli/help.js
CHANGED
|
@@ -176,11 +176,11 @@ Usage:
|
|
|
176
176
|
fullstackgtm suggest --plan-id <id> | --plan <path> [source options] [--json] [--out <path>]
|
|
177
177
|
derive values for requires_human_* placeholders
|
|
178
178
|
from snapshot evidence, with confidence + reasons
|
|
179
|
-
fullstackgtm plans list [--status <s>] | show <id> | sync | reject <id>
|
|
179
|
+
fullstackgtm plans list [--status <s>] | show <id> [--verbose] | sync | reject <id>
|
|
180
180
|
fullstackgtm plans approve <id> --operations <ids|all> [--value <opId>=<v>]
|
|
181
181
|
fullstackgtm plans approve <id> --values-from <suggestions.json> [--min-confidence high|low] [--include-creates]
|
|
182
182
|
fullstackgtm plans recover <id> --acknowledge-uncertain-writes
|
|
183
|
-
fullstackgtm apply --plan-id <id> --provider <name>
|
|
183
|
+
fullstackgtm apply --plan-id <id> --provider <name> [--verbose|--json]
|
|
184
184
|
fullstackgtm apply --plan-id <id> --channel outbox (render approved openers; transmits nothing)
|
|
185
185
|
fullstackgtm apply --plan <path> --provider <name> --approve <ids|all> [options]
|
|
186
186
|
fullstackgtm audit-log export [--out <path>] | verify --in <path> tamper-evident apply-run record
|
|
@@ -502,7 +502,7 @@ export const HELP = {
|
|
|
502
502
|
summary: "replicated plan lifecycle: list / show / sync / approve / reject",
|
|
503
503
|
phase: "Govern",
|
|
504
504
|
synopsis: [
|
|
505
|
-
"fullstackgtm plans list [--status <s>] | show <id> | sync | reject <id>",
|
|
505
|
+
"fullstackgtm plans list [--status <s>] | show <id> [--verbose] | sync | reject <id>",
|
|
506
506
|
"fullstackgtm plans approve <id> --operations <ids|all> [--value <opId>=<v>]",
|
|
507
507
|
"fullstackgtm plans approve <id> --values-from <suggestions.json> [--min-confidence high|low]",
|
|
508
508
|
"fullstackgtm plans recover <id> --acknowledge-uncertain-writes",
|
|
@@ -514,11 +514,11 @@ export const HELP = {
|
|
|
514
514
|
summary: "write ONLY explicitly approved operations to a provider",
|
|
515
515
|
phase: "Govern / Verify",
|
|
516
516
|
synopsis: [
|
|
517
|
-
"fullstackgtm apply --plan-id <id> --provider <name>",
|
|
517
|
+
"fullstackgtm apply --plan-id <id> --provider <name> [--verbose|--json]",
|
|
518
518
|
"fullstackgtm apply --plan-id <id> --channel outbox (render approved drafted openers to the outbox; transmits nothing)",
|
|
519
519
|
"fullstackgtm apply --plan <path> --provider <name> --approve <ids|all> [--value <opId>=<v>]",
|
|
520
520
|
],
|
|
521
|
-
detail: "The CLI CRM-write verb. Writes only operations approved locally or imported from the shared hosted replica, with an online execution claim when available, stable operation ids, compare-and-set, resolve-before-create, and readback. Hosted may execute the same synchronized plan when it has a compatible CRM connection; its exact operation receipts are imported on the next CLI check-in. Never writes requires_human_* placeholders without a --value override.",
|
|
521
|
+
detail: "The CLI CRM-write verb. Writes only operations approved locally or imported from the shared hosted replica, with an online execution claim when available, stable operation ids, compare-and-set, resolve-before-create, and readback. Default output is a compact outcome card; --verbose prints the full run document and --json preserves structured output. Hosted may execute the same synchronized plan when it has a compatible CRM connection; its exact operation receipts are imported on the next CLI check-in. Never writes requires_human_* placeholders without a --value override.",
|
|
522
522
|
seeAlso: ["plans", "suggest", "audit-log"],
|
|
523
523
|
},
|
|
524
524
|
"audit-log": {
|
|
@@ -597,7 +597,7 @@ export const BESPOKE_HELP = ["init", "call", "market", "tam", "enrich", "schedul
|
|
|
597
597
|
export const GLOBAL_FLAGS = ["--help", "--full"];
|
|
598
598
|
export const GLOBAL_SHORT_FLAGS = ["-h"];
|
|
599
599
|
export const SOURCE_FLAGS = ["--provider", "--token-env", "--input", "--demo", "--sample", "--seed", "--today"];
|
|
600
|
-
export const AUDIT_FLAGS = ["--config", "--allow-plugins", "--no-plugins", "--rules", "--stale-days", "--fail-on", "--save", "--dry-run", "--json", "--out", "--full"];
|
|
600
|
+
export const AUDIT_FLAGS = ["--config", "--allow-plugins", "--no-plugins", "--rules", "--stale-days", "--fail-on", "--save", "--dry-run", "--json", "--out", "--full", "--verbose"];
|
|
601
601
|
// Complete per-command flag registry used by runCli's fail-closed flag
|
|
602
602
|
// validation. Keep this next to HELP so focused help, machine capabilities,
|
|
603
603
|
// and the parser safety gate have one command inventory to reconcile against.
|
|
@@ -605,7 +605,7 @@ export const COMMAND_FLAGS = {
|
|
|
605
605
|
init: ["--source", "--provider", "--out", "--force"],
|
|
606
606
|
login: ["--via", "--hosted", "--private-token", "--token", "--key", "--api-key", "--client-id", "--client-secret", "--scopes", "--port", "--oauth", "--device", "--instance-url", "--login-url", "--no-validate"],
|
|
607
607
|
logout: [],
|
|
608
|
-
doctor: ["--json"],
|
|
608
|
+
doctor: ["--verbose", "--json"],
|
|
609
609
|
capabilities: ["--json"],
|
|
610
610
|
"robot-docs": [],
|
|
611
611
|
profiles: ["--json"],
|
|
@@ -619,24 +619,24 @@ export const COMMAND_FLAGS = {
|
|
|
619
619
|
hierarchy: [...SOURCE_FLAGS, "--json", "--out"],
|
|
620
620
|
relationships: [...SOURCE_FLAGS, "--account-id", "--domain", "--json", "--out"],
|
|
621
621
|
route: [...SOURCE_FLAGS, "--match", "--no-inherit-owner", "--reassign-owned", "--policy", "--reason", "--max-operations", "--save", "--dry-run", "--json", "--out"],
|
|
622
|
-
fix: [...SOURCE_FLAGS, "--config", "--allow-plugins", "--no-plugins", "--rule", "--provider", "--min-confidence", "--include-creates", "--yes", "--confirm", "--dry-run"],
|
|
623
|
-
"bulk-update": [...SOURCE_FLAGS, "--where", "--set", "--guard", "--require", "--create-task", "--archive", "--force-archive-duplicates", "--reason", "--max-operations", "--save", "--dry-run", "--json", "--out"],
|
|
624
|
-
dedupe: [...SOURCE_FLAGS, "--key", "--keep", "--reason", "--max-operations", "--save", "--dry-run", "--json", "--out"],
|
|
625
|
-
reassign: [...SOURCE_FLAGS, "--from", "--assign-unowned", "--to", "--objects", "--where", "--except-deal-stage", "--include-closed-deals", "--reason", "--max-operations", "--save", "--dry-run", "--json", "--out"],
|
|
626
|
-
backfill: [...SOURCE_FLAGS, "--since", "--pipeline", "--match-property", "--skip-unmatched", "--save", "--dry-run", "--json"],
|
|
627
|
-
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--json", "--out"],
|
|
628
|
-
call: [...SOURCE_FLAGS, "--transcript", "--title", "--source", "--model", "--deterministic", "--heuristics", "--llm", "--json", "--ndjson", "--out", "--call", "--call-type", "--rubric", "--list", "--list-rubrics", "--attendees", "--domain", "--deal", "--save", "--dry-run"],
|
|
622
|
+
fix: [...SOURCE_FLAGS, "--config", "--allow-plugins", "--no-plugins", "--rule", "--provider", "--min-confidence", "--include-creates", "--yes", "--confirm", "--dry-run", "--verbose"],
|
|
623
|
+
"bulk-update": [...SOURCE_FLAGS, "--where", "--set", "--guard", "--require", "--create-task", "--archive", "--force-archive-duplicates", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
624
|
+
dedupe: [...SOURCE_FLAGS, "--key", "--keep", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
625
|
+
reassign: [...SOURCE_FLAGS, "--from", "--assign-unowned", "--to", "--objects", "--where", "--except-deal-stage", "--include-closed-deals", "--reason", "--max-operations", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
626
|
+
backfill: [...SOURCE_FLAGS, "--since", "--pipeline", "--match-property", "--skip-unmatched", "--save", "--dry-run", "--verbose", "--json"],
|
|
627
|
+
enrich: [...SOURCE_FLAGS, "--config", "--source", "--icp", "--input", "--provider", "--list", "--stale-days", "--assign-owner", "--objects", "--max", "--scan-limit", "--staged-run", "--run-label", "--label", "--runs", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
628
|
+
call: [...SOURCE_FLAGS, "--transcript", "--title", "--source", "--model", "--deterministic", "--heuristics", "--llm", "--verbose", "--json", "--ndjson", "--out", "--call", "--call-type", "--rubric", "--list", "--list-rubrics", "--attendees", "--domain", "--deal", "--save", "--dry-run"],
|
|
629
629
|
suggest: [...SOURCE_FLAGS, "--plan-id", "--plan", "--json", "--out"],
|
|
630
|
-
plans: ["--status", "--operations", "--value", "--values-from", "--min-confidence", "--include-creates", "--acknowledge-uncertain-writes", "--json"],
|
|
631
|
-
apply: ["--plan", "--plan-id", "--provider", "--channel", "--token-env", "--approve", "--value", "--json", "--config"],
|
|
630
|
+
plans: ["--status", "--operations", "--value", "--values-from", "--min-confidence", "--include-creates", "--acknowledge-uncertain-writes", "--verbose", "--json"],
|
|
631
|
+
apply: ["--plan", "--plan-id", "--provider", "--channel", "--token-env", "--approve", "--value", "--verbose", "--json", "--config"],
|
|
632
632
|
"audit-log": ["--in", "--out", "--json"],
|
|
633
633
|
merge: ["--input", "--out", "--json"],
|
|
634
|
-
market: ["--category", "--config", "--vendor", "--snapshot", "--task-account", "--task-deal", "--capture-run", "--run", "--prior-run", "--diff", "--from", "--calls", "--anchor", "--min-mentions", "--max-claims", "--promote-lift", "--model", "--format", "--auto", "--unverified", "--save", "--dry-run", "--json", "--out"],
|
|
635
|
-
tam: [...SOURCE_FLAGS, "--source", "--name", "--icp", "--accounts", "--acv", "--acv-basis", "--acv-from-crm", "--deal-period", "--buyers-per-account", "--cross-checks", "--max", "--max-credits", "--usd-per-credit", "--dry-run", "--confirm", "--cron", "--label", "--save", "--json", "--out"],
|
|
636
|
-
icp: [...SOURCE_FLAGS, "--name", "--signals-from", "--with-history", "--prompt", "--min-score", "--from-judge", "--golden", "--against-outcomes", "--min-accuracy", "--model", "--label", "--save", "--json", "--out"],
|
|
637
|
-
signals: ["--bucket", "--source", "--connector", "--connector-opt", "--watchlist", "--keywords", "--from", "--label", "--since", "--account", "--contact", "--unjudged", "--touch", "--result", "--save", "--json", "--explain"],
|
|
638
|
-
draft: ["--from-judge", "--min-score", "--prompt", "--model", "--channel", "--save", "--dry-run", "--json", "--out"],
|
|
639
|
-
schedule: ["--cron", "--label", "--provider", "--trigger", "--timer", "--runs", "--json"],
|
|
634
|
+
market: ["--category", "--config", "--vendor", "--snapshot", "--task-account", "--task-deal", "--capture-run", "--run", "--prior-run", "--diff", "--from", "--calls", "--anchor", "--min-mentions", "--max-claims", "--promote-lift", "--model", "--format", "--auto", "--unverified", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
635
|
+
tam: [...SOURCE_FLAGS, "--source", "--name", "--icp", "--accounts", "--acv", "--acv-basis", "--acv-from-crm", "--deal-period", "--buyers-per-account", "--cross-checks", "--max", "--max-credits", "--usd-per-credit", "--dry-run", "--confirm", "--cron", "--label", "--save", "--verbose", "--json", "--out"],
|
|
636
|
+
icp: [...SOURCE_FLAGS, "--name", "--signals-from", "--with-history", "--prompt", "--min-score", "--from-judge", "--golden", "--against-outcomes", "--min-accuracy", "--model", "--label", "--save", "--verbose", "--json", "--out"],
|
|
637
|
+
signals: ["--bucket", "--source", "--connector", "--connector-opt", "--watchlist", "--keywords", "--from", "--label", "--since", "--account", "--contact", "--unjudged", "--touch", "--result", "--save", "--verbose", "--json", "--explain"],
|
|
638
|
+
draft: ["--from-judge", "--min-score", "--prompt", "--model", "--channel", "--save", "--dry-run", "--verbose", "--json", "--out"],
|
|
639
|
+
schedule: ["--cron", "--label", "--provider", "--trigger", "--timer", "--runs", "--verbose", "--json"],
|
|
640
640
|
};
|
|
641
641
|
export const FLAGS_WITH_VALUES = new Set([
|
|
642
642
|
"--account", "--account-id", "--accounts", "--acv", "--acv-basis", "--after", "--anchor", "--api-key", "--approve", "--archive", "--assign-owner", "--attendees", "--before", "--bucket", "--buyers-per-account", "--call", "--call-type", "--calls", "--capture-run", "--category", "--channel", "--client", "--client-id", "--client-secret", "--config", "--connector", "--connector-opt", "--contact", "--cron", "--cross-checks", "--deal", "--deal-period", "--diff", "--domain", "--email", "--except-deal-stage", "--format", "--from", "--from-judge", "--golden", "--guard", "--icp", "--in", "--input", "--instance-url", "--keep", "--key", "--keywords", "--label", "--list", "--login-url", "--match", "--match-property", "--max", "--max-claims", "--max-credits", "--max-examples", "--max-operations", "--min-accuracy", "--min-confidence", "--min-mentions", "--min-score", "--model", "--name", "--objects", "--operations", "--out", "--pipeline", "--plan", "--plan-id", "--policy", "--port", "--prepared-by", "--prior-run", "--profile", "--promote-lift", "--prompt", "--provider", "--reason", "--require", "--result", "--rubric", "--rule", "--rules", "--run", "--run-label", "--runs", "--scan-limit", "--scopes", "--seed", "--set", "--signals-from", "--since", "--snapshot", "--source", "--staged-run", "--stale-days", "--status", "--task-account", "--task-deal", "--timer", "--title", "--to", "--today", "--token", "--token-env", "--touch", "--transcript", "--trigger", "--usd-per-credit", "--value", "--values-from", "--vendor", "--via", "--watchlist", "--where",
|
package/dist/cli/icp.js
CHANGED
|
@@ -9,6 +9,20 @@ import { DEFAULT_GOLDEN_NOW_ISO, DEFAULT_GOLDEN_SET, DEFAULT_MIN_ACCURACY, defau
|
|
|
9
9
|
import { loadIcp, numericOption, option, readSnapshot, resolveLlmBaseUrls, saveRequested } from "./shared.js";
|
|
10
10
|
import { createStatusLine } from "./ui.js";
|
|
11
11
|
import { unknownSubcommandError } from "./suggest.js";
|
|
12
|
+
function renderJudgeDecisions(decisions) {
|
|
13
|
+
if (decisions.length === 0)
|
|
14
|
+
return "No accounts cleared the score threshold.";
|
|
15
|
+
const lines = [`ICP decisions (${decisions.length})`, ""];
|
|
16
|
+
for (const decision of decisions) {
|
|
17
|
+
const target = decision.contact?.email || decision.contact?.title;
|
|
18
|
+
lines.push(`${decision.decision.toUpperCase().padEnd(7)} ${String(decision.score).padStart(3)} ${decision.accountDomain}${target ? ` · ${target}` : ""}`);
|
|
19
|
+
if (decision.whyNow)
|
|
20
|
+
lines.push(` ${decision.whyNow.replace(/\s+/g, " ").trim()}`);
|
|
21
|
+
if (decision.play)
|
|
22
|
+
lines.push(` Next: ${decision.play.replace(/\s+/g, " ").trim()}`);
|
|
23
|
+
}
|
|
24
|
+
return lines.join("\n");
|
|
25
|
+
}
|
|
12
26
|
/**
|
|
13
27
|
* `icp` — develop and inspect the Ideal Customer Profile that targets acquire.
|
|
14
28
|
* The CLI can't run AskUserQuestion itself; `icp interview` emits the question
|
|
@@ -133,7 +147,7 @@ ops. Develop one by interview, then \`enrich acquire\` picks up ./icp.json.
|
|
|
133
147
|
}
|
|
134
148
|
decisions = decisions.filter((d) => d.score >= minScore);
|
|
135
149
|
// 5) Ranked decisions to stdout (JSON), guidance to stderr.
|
|
136
|
-
console.log(JSON.stringify(decisions, null, 2));
|
|
150
|
+
console.log(rest.includes("--json") || rest.includes("--verbose") ? JSON.stringify(decisions, null, 2) : renderJudgeDecisions(decisions));
|
|
137
151
|
console.error(`Judged ${unjudged.length} signal(s) across ${new Set(decisions.map((d) => d.accountDomain)).size} account(s): ` +
|
|
138
152
|
`${decisions.filter((d) => d.decision === "send").length} send, ` +
|
|
139
153
|
`${decisions.filter((d) => d.decision === "nurture").length} nurture, ` +
|
package/dist/cli/market.js
CHANGED
|
@@ -481,9 +481,22 @@ Rules:
|
|
|
481
481
|
writeFileSync(resolve(process.cwd(), outPath), output);
|
|
482
482
|
console.log(`Wrote ${outPath}`);
|
|
483
483
|
}
|
|
484
|
-
else {
|
|
484
|
+
else if (rest.includes("--verbose") || option(rest, "--format")) {
|
|
485
485
|
console.log(output);
|
|
486
486
|
}
|
|
487
|
+
else {
|
|
488
|
+
const fronts = computeFrontStates(config, set);
|
|
489
|
+
const counts = new Map();
|
|
490
|
+
for (const front of fronts)
|
|
491
|
+
counts.set(front.state, (counts.get(front.state) ?? 0) + 1);
|
|
492
|
+
console.log(`${config.category} market · ${set.runLabel}`);
|
|
493
|
+
console.log(`${fronts.length} claim fronts · ${["open", "contested", "owned", "saturated", "vacant"].filter((state) => counts.has(state)).map((state) => `${counts.get(state)} ${state}`).join(" · ")}`);
|
|
494
|
+
for (const front of fronts) {
|
|
495
|
+
const owner = front.loudVendorIds.length ? ` · ${front.loudVendorIds.join(", ")}` : "";
|
|
496
|
+
console.log(`\n${front.state.toUpperCase()} ${front.claimId}${owner}`);
|
|
497
|
+
}
|
|
498
|
+
console.log("\nUse --verbose for the full evidence report, or --out <path> to write it.");
|
|
499
|
+
}
|
|
487
500
|
return;
|
|
488
501
|
}
|
|
489
502
|
if (subcommand === "axes") {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { box, colorEnabled, paint, truncateToWidth } from "./ui.js";
|
|
2
|
+
export function compactPlan(plan, options = {}) {
|
|
3
|
+
const counts = new Map();
|
|
4
|
+
for (const operation of plan.operations) {
|
|
5
|
+
const action = operation.operation || operation.field || "change";
|
|
6
|
+
counts.set(action, (counts.get(action) ?? 0) + 1);
|
|
7
|
+
}
|
|
8
|
+
const mix = [...counts]
|
|
9
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
10
|
+
.slice(0, 4)
|
|
11
|
+
.map(([action, count]) => `${count} ${action}`)
|
|
12
|
+
.join(" · ");
|
|
13
|
+
const lines = [
|
|
14
|
+
truncateToWidth(plan.title, 100),
|
|
15
|
+
`Status ${options.saved ? "SAVED · NEEDS APPROVAL" : "DRY RUN · NOTHING WRITTEN"}`,
|
|
16
|
+
truncateToWidth(`Scope ${plan.operations.length} operation${plan.operations.length === 1 ? "" : "s"}${mix ? ` · ${mix}` : ""}`, 100),
|
|
17
|
+
truncateToWidth(`Effect ${plan.summary}`, 100),
|
|
18
|
+
options.saved ? `Next fullstackgtm plans show ${plan.id}` : "Next re-run with --save to stage this plan for approval",
|
|
19
|
+
];
|
|
20
|
+
return [
|
|
21
|
+
...box(lines, paint(colorEnabled(process.stdout)), "Plan preview"),
|
|
22
|
+
`Details: re-run with --verbose${options.saved ? `; JSON: fullstackgtm plans show ${plan.id} --json` : "; machine output: --json"}`,
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
export function verbosePlanRequested(args) {
|
|
26
|
+
return args.includes("--verbose") || args.includes("--full");
|
|
27
|
+
}
|