@signaliz/cli 1.0.12 → 1.0.14

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.
Files changed (2) hide show
  1. package/dist/bin.js +193 -11
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1071,6 +1071,7 @@ Campaign Builder:
1071
1071
  Merge a private-safe strategy template
1072
1072
  --builtins a,b lead_generation,local_leads,email_finding,email_verification,signals
1073
1073
  --custom-tool ROUTE BYO route: provider:layer:tool[:mcp]
1074
+ campaign-agent doctor Inspect built-in lanes, BYO routes, Memory, Brain, and approvals
1074
1075
  campaign-agent proof Prove memory, plan, approvals, and build dry-run without spend
1075
1076
  campaign-agent commit-plan
1076
1077
  Dry-run or write a reviewed GTM campaign object
@@ -2791,6 +2792,7 @@ var CAMPAIGN_AGENT_HELP = `signaliz campaign-agent <command> [options]
2791
2792
  Strategy-template campaign builder:
2792
2793
  init Emit reusable CampaignBuilderAgentRequest JSON
2793
2794
  plan Compose an approval-gated strategy-template MCP flow
2795
+ doctor Inspect built-in lanes, BYO routes, Memory, Brain, and approvals
2794
2796
  proof Prove memory, plan, approvals, and build dry-run without spend
2795
2797
  commit-plan Dry-run or write a reviewed GTM campaign object
2796
2798
  dry-run Execute build_campaign with dry_run=true
@@ -2803,6 +2805,7 @@ Strategy-template campaign builder:
2803
2805
 
2804
2806
  Examples:
2805
2807
  signaliz campaign-agent init --strategy-template non-medical-home-care --target-count 250 --use-local-leads --json > campaign-request.json
2808
+ signaliz campaign-agent doctor --request-file campaign-request.json --json
2806
2809
  signaliz campaign-agent plan --goal "Build a strategy-template campaign for my ICP" --strategy-template non-medical-home-care --target-count 500 --builtins lead_generation,email_finding,email_verification,signals
2807
2810
  signaliz campaign-agent proof --goal "Build a strategy-template campaign for my ICP" --strategy-template non-medical-home-care --target-count 100 --json
2808
2811
  signaliz campaign-agent plan --request-file campaign-builder-agent-request.json --target-count 250 --json
@@ -2868,7 +2871,7 @@ async function campaignAgent(sub, rest) {
2868
2871
  await campaignAgentReview(sub, rest);
2869
2872
  return;
2870
2873
  }
2871
- if (!["init", "template", "request-template", "new", "plan", "proof", "smoke", "commit-plan", "dry-run", "build-approved", "launch-approved"].includes(sub)) {
2874
+ if (!["init", "template", "request-template", "new", "plan", "doctor", "readiness", "preflight", "proof", "smoke", "commit-plan", "dry-run", "build-approved", "launch-approved"].includes(sub)) {
2872
2875
  process.stdout.write(CAMPAIGN_AGENT_HELP);
2873
2876
  return;
2874
2877
  }
@@ -2886,7 +2889,8 @@ async function campaignAgent(sub, rest) {
2886
2889
  return;
2887
2890
  }
2888
2891
  const requestFromFile = campaignAgentRequestFileFromFlags(flags);
2889
- const goal = commandStringFlag(flags, "goal", "brief") || promptArg(rest) || campaignAgentStringValue(requestFromFile?.goal);
2892
+ const isDoctor = sub === "doctor" || sub === "readiness" || sub === "preflight";
2893
+ const goal = commandStringFlag(flags, "goal", "brief") || promptArg(rest) || campaignAgentStringValue(requestFromFile?.goal) || (isDoctor ? "Build a strategy-template campaign for the target ICP." : void 0);
2890
2894
  if (!goal) die(`campaign-agent ${sub} requires --goal or a request file with goal`);
2891
2895
  const isApprovedBuild = sub === "build-approved" || sub === "launch-approved";
2892
2896
  const isCommitPlan = sub === "commit-plan";
@@ -2905,21 +2909,32 @@ async function campaignAgent(sub, rest) {
2905
2909
  requestFromFile,
2906
2910
  campaignAgentRequestFromFlags(goal, flags, { includeDefaults: !requestFromFile })
2907
2911
  );
2908
- const plan = await agent.createPlan(request, {
2912
+ const planOptions = {
2909
2913
  discoverCapabilities: !commandBooleanFlag(flags, "skip-discovery"),
2910
2914
  scopeCampaign: !commandBooleanFlag(flags, "skip-scope"),
2911
2915
  includeStrategyMemoryStatus: !commandBooleanFlag(flags, "no-strategy-memory"),
2912
2916
  includeKernelPlan: !commandBooleanFlag(flags, "no-kernel-plan"),
2913
2917
  includeDeliveryRisk: !commandBooleanFlag(flags, "no-delivery-risk")
2914
- });
2918
+ };
2919
+ if (isDoctor) {
2920
+ const readiness = await agent.readiness?.(request, planOptions) || createCampaignAgentReadiness(await agent.createPlan(request, planOptions));
2921
+ if (!readiness.summary?.ready) process.exitCode = 1;
2922
+ return output(readiness, () => printCampaignAgentReadiness(readiness));
2923
+ }
2915
2924
  if (sub === "proof" || sub === "smoke") {
2916
- const result = await agent.dryRunPlan(plan, {
2925
+ const proof = typeof agent.proof === "function" ? await agent.proof(request, {
2926
+ ...planOptions,
2917
2927
  idempotencyKey: commandStringFlag(flags, "idempotency-key")
2918
- });
2919
- const proof = createCampaignAgentProofReceipt(plan, result);
2928
+ }) : await (async () => {
2929
+ const plan2 = await agent.createPlan(request, planOptions);
2930
+ return createCampaignAgentProofReceipt(plan2, await agent.dryRunPlan(plan2, {
2931
+ idempotencyKey: commandStringFlag(flags, "idempotency-key")
2932
+ }));
2933
+ })();
2920
2934
  if (!proof.success) process.exitCode = 1;
2921
2935
  return output(proof, () => printCampaignAgentProof(proof));
2922
2936
  }
2937
+ const plan = await agent.createPlan(request, planOptions);
2923
2938
  if (isCommitPlan) {
2924
2939
  const result = await agent.commitPlan(plan, {
2925
2940
  confirm: isConfirmedCommit,
@@ -3334,6 +3349,172 @@ function createCampaignAgentApproval(plan, input) {
3334
3349
  approvedAt: (/* @__PURE__ */ new Date()).toISOString()
3335
3350
  };
3336
3351
  }
3352
+ function createCampaignAgentReadiness(plan) {
3353
+ const lanes = firstArray(plan?.routes).map((route) => {
3354
+ const builtIn = campaignAgentBuiltInForRoute(route);
3355
+ const customRoute = route?.provider !== "signaliz" && route?.provider !== "csv" && !builtIn;
3356
+ const prepareStep = firstArray(plan?.mcpFlow).find(
3357
+ (step) => step?.tool === "gtm_provider_recipe_prepare" && record(step?.arguments).provider_id === route?.provider
3358
+ );
3359
+ const activateStep = firstArray(plan?.mcpFlow).find(
3360
+ (step) => step?.tool === "gtm_provider_route_activate" && record(step?.arguments).provider_id === route?.provider
3361
+ );
3362
+ const activationArgs = record(activateStep?.arguments);
3363
+ const readOnlySetup = !customRoute || prepareStep?.readOnly === true && activateStep?.readOnly === true && activationArgs.dry_run === true && activationArgs.confirm === false;
3364
+ const blockers2 = [
3365
+ customRoute && !prepareStep ? `${route?.label || route?.provider} is missing provider recipe setup.` : void 0,
3366
+ customRoute && !activateStep ? `${route?.label || route?.provider} is missing provider route dry-run setup.` : void 0,
3367
+ customRoute && !readOnlySetup ? `${route?.label || route?.provider} route setup must stay read-only before approval.` : void 0,
3368
+ route?.mode === "required" && !route?.toolName && customRoute ? `${route?.label || route?.provider} is required but has no tool name.` : void 0
3369
+ ].filter(Boolean);
3370
+ return {
3371
+ id: route?.id || `${route?.provider}-${route?.layer}`,
3372
+ label: route?.label || campaignAgentReadinessLaneLabel(route, builtIn),
3373
+ layer: route?.layer,
3374
+ gtmLayers: firstArray(route?.gtmLayers).length ? route.gtmLayers : [route?.gtmLayer].filter(Boolean),
3375
+ provider: route?.provider,
3376
+ toolName: route?.toolName,
3377
+ mode: route?.mode || "if_available",
3378
+ builtIn,
3379
+ approvalRequired: route?.approvalRequired === true || firstArray(plan?.approvals).some((approval) => approval?.routeId === route?.id),
3380
+ customRoute,
3381
+ readOnlySetup,
3382
+ ready: blockers2.length === 0,
3383
+ blockers: blockers2,
3384
+ nextAction: customRoute ? `Review ${route?.provider} route setup before launch approval.` : void 0
3385
+ };
3386
+ });
3387
+ const builtInLanes = lanes.filter((lane) => lane.builtIn);
3388
+ const customRoutes = lanes.filter((lane) => lane.customRoute);
3389
+ const strategyMemory = record(plan?.strategyMemoryStatus);
3390
+ const deliveryRisk = record(record(plan?.buildRequest).deliveryRisk);
3391
+ const risk = record(deliveryRisk.risk);
3392
+ const riskLevel = String(risk.risk_level || risk.riskLevel || "").toLowerCase();
3393
+ const riskBlockers = firstArray(deliveryRisk.blockers);
3394
+ const gates = [
3395
+ {
3396
+ id: "campaign_plan_created",
3397
+ label: "Campaign plan created",
3398
+ passed: Boolean(plan?.planId && firstArray(plan?.mcpFlow).length),
3399
+ detail: plan?.planId ? `Plan ${plan.planId} has ${firstArray(plan?.mcpFlow).length} MCP step(s).` : "No campaign-builder plan was created."
3400
+ },
3401
+ {
3402
+ id: "built_in_lanes_ready",
3403
+ label: "Built-in lanes ready",
3404
+ passed: builtInLanes.length > 0 && builtInLanes.every((lane) => lane.ready),
3405
+ detail: `${builtInLanes.filter((lane) => lane.ready).length}/${builtInLanes.length} Signaliz built-in lane(s) are present.`
3406
+ },
3407
+ {
3408
+ id: "strategy_memory_ready",
3409
+ label: "Strategy memory ready",
3410
+ passed: !plan?.strategyMemoryStatus || strategyMemory.ready !== false,
3411
+ detail: plan?.strategyMemoryStatus ? "Strategy memory readiness was checked." : "Strategy memory readiness was not requested."
3412
+ },
3413
+ {
3414
+ id: "kernel_plan_attached",
3415
+ label: "Kernel plan attached",
3416
+ passed: Object.keys(record(plan?.kernelPlan)).length > 0,
3417
+ detail: Object.keys(record(plan?.kernelPlan)).length > 0 ? "Kernel planner evidence is attached." : "Kernel planner evidence is not attached."
3418
+ },
3419
+ {
3420
+ id: "delivery_risk_clear",
3421
+ label: "Delivery risk clear",
3422
+ passed: Object.keys(deliveryRisk).length === 0 || riskBlockers.length === 0 && !["high", "critical", "blocked"].includes(riskLevel),
3423
+ detail: Object.keys(deliveryRisk).length === 0 ? "Delivery risk preflight was not requested." : `Delivery risk is ${riskLevel || "available"} with ${riskBlockers.length} blocker(s).`
3424
+ },
3425
+ {
3426
+ id: "custom_routes_prepared",
3427
+ label: "Custom routes prepared",
3428
+ passed: customRoutes.every((lane) => lane.ready),
3429
+ detail: customRoutes.length === 0 ? "No BYO routes were requested." : `${customRoutes.filter((lane) => lane.ready).length}/${customRoutes.length} BYO route(s) have read-only setup steps.`
3430
+ },
3431
+ {
3432
+ id: "approval_gates_present",
3433
+ label: "Approval gates present",
3434
+ passed: firstArray(plan?.approvals).length > 0,
3435
+ detail: firstArray(plan?.approvals).length > 0 ? `${firstArray(plan?.approvals).length} approval gate(s) are attached.` : "No approval gates are attached."
3436
+ }
3437
+ ];
3438
+ const blockers = [
3439
+ ...gates.filter((gate) => !gate.passed).map((gate) => gate.detail),
3440
+ ...lanes.flatMap((lane) => lane.blockers || [])
3441
+ ];
3442
+ const ready = blockers.length === 0;
3443
+ return {
3444
+ plan,
3445
+ lanes,
3446
+ gates,
3447
+ summary: {
3448
+ ready,
3449
+ score: Math.round(gates.filter((gate) => gate.passed).length / gates.length * 100),
3450
+ targetCount: plan?.targetCount,
3451
+ estimatedCredits: plan?.estimatedCredits,
3452
+ builtInLanesReady: builtInLanes.filter((lane) => lane.ready).length,
3453
+ builtInLanesTotal: builtInLanes.length,
3454
+ customRoutesReady: customRoutes.filter((lane) => lane.ready).length,
3455
+ customRoutesTotal: customRoutes.length,
3456
+ approvalGateCount: firstArray(plan?.approvals).length,
3457
+ blockers,
3458
+ warnings: firstArray(plan?.warnings)
3459
+ },
3460
+ nextActions: ready ? [
3461
+ "signaliz campaign-agent proof --request-file campaign-request.json --json",
3462
+ "signaliz campaign-agent commit-plan --request-file campaign-request.json --json",
3463
+ `signaliz campaign-agent build-approved --request-file campaign-request.json --confirm-launch --approve-all --approved-by operator@example.com --spend-limit ${plan?.estimatedCredits || 0}`
3464
+ ] : ["signaliz campaign-agent plan --request-file campaign-request.json --json"]
3465
+ };
3466
+ }
3467
+ function campaignAgentBuiltInForRoute(route) {
3468
+ const map = {
3469
+ "signaliz-lead-generation": "lead_generation",
3470
+ "signaliz-local-leads": "local_leads",
3471
+ "signaliz-email-finding": "email_finding",
3472
+ "signaliz-email-verification": "email_verification",
3473
+ "signaliz-signals": "signals"
3474
+ };
3475
+ return map[String(route?.id || "")];
3476
+ }
3477
+ function campaignAgentReadinessLaneLabel(route, builtIn) {
3478
+ const labels = {
3479
+ lead_generation: "Signaliz lead generation",
3480
+ local_leads: "Signaliz local leads",
3481
+ email_finding: "Signaliz email finding",
3482
+ email_verification: "Signaliz email verification",
3483
+ signals: "Signaliz signals"
3484
+ };
3485
+ return builtIn ? labels[builtIn] : `${route?.provider || "custom"} ${route?.layer || "route"}`;
3486
+ }
3487
+ function printCampaignAgentReadiness(readiness) {
3488
+ const summary = record(readiness.summary);
3489
+ const plan = record(readiness.plan);
3490
+ console.log(`Readiness: ${summary.ready === true ? "ready" : "blocked"} (${summary.score || 0}%)`);
3491
+ if (plan.campaignName) console.log(`Campaign: ${plan.campaignName}`);
3492
+ if (summary.targetCount !== void 0) console.log(`Target: ${summary.targetCount}`);
3493
+ if (summary.estimatedCredits !== void 0) console.log(`Estimated credits: ${summary.estimatedCredits}`);
3494
+ console.log(`Built-ins: ${summary.builtInLanesReady || 0}/${summary.builtInLanesTotal || 0}`);
3495
+ console.log(`BYO routes: ${summary.customRoutesReady || 0}/${summary.customRoutesTotal || 0}`);
3496
+ console.log(`Approval gates: ${summary.approvalGateCount || 0}`);
3497
+ const gates = firstArray(readiness.gates);
3498
+ if (gates.length) {
3499
+ console.log("\nGates:");
3500
+ for (const gate of gates) console.log(`- ${gate.passed ? "PASS" : "BLOCKED"} ${gate.label}: ${gate.detail}`);
3501
+ }
3502
+ const lanes = firstArray(readiness.lanes);
3503
+ if (lanes.length) {
3504
+ console.log("\nLanes:");
3505
+ for (const lane of lanes) console.log(`- ${lane.ready ? "READY" : "BLOCKED"} ${lane.label}: ${lane.toolName || lane.provider}`);
3506
+ }
3507
+ const blockers = firstArray(summary.blockers);
3508
+ if (blockers.length) {
3509
+ console.log("\nBlockers:");
3510
+ for (const blocker of blockers.slice(0, 8)) console.log(`- ${blocker}`);
3511
+ }
3512
+ const nextActions = firstArray(readiness.nextActions);
3513
+ if (nextActions.length) {
3514
+ console.log("\nNext:");
3515
+ for (const action of nextActions) console.log(`- ${action}`);
3516
+ }
3517
+ }
3337
3518
  function printCampaignAgentPlan(plan) {
3338
3519
  console.log(`Plan: ${plan.campaignName || plan.goal || plan.planId}`);
3339
3520
  if (plan.planId) console.log(`Plan ID: ${plan.planId}`);
@@ -5570,18 +5751,19 @@ async function simpleJob(cmd, rest) {
5570
5751
  if (isHelpRequest(rest) || !promptArg(rest) && !flagArg("prompt") && !flagArg("brief") && !flagArg("campaign-brief")) {
5571
5752
  process.stdout.write(`signaliz build "campaign brief" [options]
5572
5753
 
5573
- Plan a campaign without spending credits or writing to providers.
5754
+ Run the campaign-agent proof path: strategy memory, Kernel plan, approvals, and
5755
+ a forced build_campaign dry-run without spend or provider writes.
5574
5756
 
5575
5757
  Examples:
5576
5758
  signaliz build "Build a campaign for industrial maintenance buyers" --target-count 50
5577
- signaliz build "Audit prior Instantly performance and build the next campaign" --preferred-providers instantly
5759
+ signaliz build "Build a strategy-template campaign for local operators" --strategy-template non-medical-home-care --use-local-leads --json
5578
5760
 
5579
5761
  Equivalent:
5580
- signaliz gtm plan "campaign brief"
5762
+ signaliz campaign-agent proof "campaign brief"
5581
5763
  `);
5582
5764
  return;
5583
5765
  }
5584
- return gtm("plan", rest);
5766
+ return campaignAgent("proof", rest);
5585
5767
  }
5586
5768
  if (cmd === "connect") {
5587
5769
  if (isHelpRequest(rest)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaliz/cli",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Signaliz CLI — GTM Kernel, Nango routes, MCP, and enrichment operations.",
5
5
  "bin": {
6
6
  "signaliz": "dist/bin.js"