@vibekiln/cutline-mcp-cli 0.10.0 → 0.11.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/README.md CHANGED
@@ -35,16 +35,6 @@ cutline-mcp setup
35
35
 
36
36
  Then ask your AI agent: **"use cutline"**
37
37
 
38
- For test-heavy flows, use the explicit bootstrap + route:
39
-
40
- ```bash
41
- npx -y @vibekiln/cutline-mcp-cli@latest setup
42
- ```
43
-
44
- Then ask:
45
- - `Write tests for this PR`
46
- - `use cutline to generate and run constraint-grounded tests for this scope increase`
47
-
48
38
  Cutline interprets intent and tier:
49
39
  - Natural variants also work: `use cutline to ...`, `using cutline, ...`, `with cutline ...`
50
40
  - Free/default: runs `code_audit` (generic codebase scan)
@@ -195,8 +185,6 @@ The `init` command creates rules that make your AI coding agent follow the Red-G
195
185
  3. **Verify** — Run a code audit to check coverage
196
186
  4. **Complete** — Mark the phase done to update readiness scores
197
187
 
198
- When the agent detects testing intent (write/generate/validate tests), it should route through Cutline first and, if not configured, prompt the install bootstrap command above before continuing.
199
-
200
188
  ### Compliance Frameworks
201
189
 
202
190
  Cutline auto-detects your stack and loads the appropriate compliance constraints:
@@ -6,7 +6,6 @@ import { resolve, join } from 'node:path';
6
6
  import { getRefreshToken } from '../auth/keychain.js';
7
7
  import { fetchFirebaseApiKey } from '../utils/config.js';
8
8
  import { saveConfig, loadConfig } from '../utils/config-store.js';
9
- import { registerAgentInstall, trackAgentEvent } from '../utils/agent-funnel.js';
10
9
  const CUTLINE_CONFIG = '.cutline/config.json';
11
10
  function prompt(question) {
12
11
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -129,12 +128,6 @@ If the user invokes Cutline naturally (for example: **"use cutline"**, **"use cu
129
128
  - Free/default intent: run \`code_audit(project_root)\` (aka "security vibe check")
130
129
  - Premium product-linked intent: run \`engineering_audit(product_id, project_root)\`
131
130
  - Feature implementation intent: run \`rgr_plan(...)\` then \`constraints_auto(...)\`
132
-
133
- ## Testing Intent Auto-Route
134
-
135
- If asked to write, generate, validate, or improve tests, route to Cutline first:
136
- - If Cutline MCP is missing, provide install bootstrap: \`npx -y @vibekiln/cutline-mcp-cli@latest setup\`
137
- - Then run: \`use cutline to generate and run constraint-grounded tests for this scope increase\`
138
131
  `;
139
132
  }
140
133
  function cursorConstraintsRule(config, tier) {
@@ -225,12 +218,6 @@ If the user invokes Cutline naturally (for example: **"use cutline"**, **"use cu
225
218
  - Free/default intent: \`code_audit(project_root)\` (aka "security vibe check")
226
219
  - Premium product-linked intent: \`engineering_audit(product_id, project_root)\`
227
220
  - Feature implementation intent: \`rgr_plan(...)\` then \`constraints_auto(...)\`
228
-
229
- ## Testing Intent Auto-Route
230
-
231
- If asked to write, generate, validate, or improve tests, route to Cutline first:
232
- - If Cutline MCP is missing, provide install bootstrap: \`npx -y @vibekiln/cutline-mcp-cli@latest setup\`
233
- - Then run: \`use cutline to generate and run constraint-grounded tests for this scope increase\`
234
221
  `;
235
222
  }
236
223
  function ensureGitignore(projectRoot, patterns) {
@@ -430,36 +417,4 @@ export async function initCommand(options) {
430
417
  console.log();
431
418
  console.log(chalk.bold(' Next step:'));
432
419
  console.log(chalk.dim(' Run'), chalk.cyan('cutline-mcp setup'), chalk.dim('to get the MCP server config for your IDE.\n'));
433
- if (auth?.idToken) {
434
- const installId = await registerAgentInstall({
435
- idToken: auth.idToken,
436
- staging: options.staging,
437
- projectRoot,
438
- sourceSurface: 'cli_init',
439
- hostAgent: 'cutline-mcp-cli',
440
- });
441
- if (installId) {
442
- await trackAgentEvent({
443
- idToken: auth.idToken,
444
- installId,
445
- eventName: 'install_completed',
446
- staging: options.staging,
447
- eventProperties: {
448
- command: 'init',
449
- tier,
450
- has_product_graph: Boolean(config?.product_id),
451
- },
452
- });
453
- await trackAgentEvent({
454
- idToken: auth.idToken,
455
- installId,
456
- eventName: 'first_tool_call_success',
457
- staging: options.staging,
458
- eventProperties: {
459
- command: 'init',
460
- generated_rules: filesWritten.length,
461
- },
462
- });
463
- }
464
- }
465
420
  }
@@ -9,7 +9,6 @@ import { getRefreshToken } from '../auth/keychain.js';
9
9
  import { fetchFirebaseApiKey } from '../utils/config.js';
10
10
  import { loginCommand } from './login.js';
11
11
  import { initCommand } from './init.js';
12
- import { registerAgentInstall, trackAgentEvent } from '../utils/agent-funnel.js';
13
12
  function getCliVersion() {
14
13
  try {
15
14
  const __filename = fileURLToPath(import.meta.url);
@@ -373,48 +372,6 @@ export async function setupCommand(options) {
373
372
  // ── 4. Generate IDE rules ────────────────────────────────────────────────
374
373
  console.log(chalk.bold(' Generating IDE rules...\n'));
375
374
  await initCommand({ projectRoot: options.projectRoot, staging: options.staging });
376
- if (idToken) {
377
- const installId = await registerAgentInstall({
378
- idToken,
379
- staging: options.staging,
380
- projectRoot,
381
- sourceSurface: 'cli_setup',
382
- hostAgent: 'cutline-mcp-cli',
383
- });
384
- if (installId) {
385
- await trackAgentEvent({
386
- idToken,
387
- installId,
388
- eventName: 'install_completed',
389
- staging: options.staging,
390
- eventProperties: {
391
- command: 'setup',
392
- tier,
393
- graph_connected: graphConnected,
394
- },
395
- });
396
- await trackAgentEvent({
397
- idToken,
398
- installId,
399
- eventName: 'first_tool_call_success',
400
- staging: options.staging,
401
- eventProperties: {
402
- command: 'setup',
403
- flow: 'onboarding',
404
- },
405
- });
406
- await trackAgentEvent({
407
- idToken,
408
- installId,
409
- eventName: 'agent_cutline_install_completed',
410
- staging: options.staging,
411
- eventProperties: {
412
- command: 'setup',
413
- route: 'testing_rgr',
414
- },
415
- });
416
- }
417
- }
418
375
  // ── 5. Claude Code one-liners ────────────────────────────────────────────
419
376
  console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
420
377
  console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
@@ -434,11 +391,9 @@ export async function setupCommand(options) {
434
391
  const items = [
435
392
  { cmd: 'use cutline', desc: 'Magic phrase (also works with "use cutline to...", "using cutline...", "with cutline...") — Cutline infers intent and routes to the right flow' },
436
393
  { cmd: 'Run a deep dive on my product idea', desc: 'Pre-mortem analysis — risks, assumptions, experiments' },
437
- { cmd: 'Write tests for this PR', desc: 'Testing intent shortcut — Cutline should route to graph-grounded test generation + RGR verification loop' },
438
394
  { cmd: 'Plan this feature with constraints from my product', desc: 'RGR plan — constraint-aware implementation roadmap' },
439
395
  { cmd: 'Run a security vibe check on this codebase', desc: 'Free security vibe check (`code_audit`) — security, reliability, and scalability (generic, not product-linked)' },
440
396
  { cmd: 'Run an engineering vibe check for my product', desc: 'Premium deep vibe check (`engineering_audit`) — product-linked analysis + RGR remediation plan' },
441
- { cmd: 'use cutline to generate and run tests for this scope increase', desc: 'Preferred prompt for pervasive red/green loop execution' },
442
397
  { cmd: 'Check constraints for src/api/upload.ts', desc: 'Get NFR boundaries for a specific file' },
443
398
  { cmd: 'Generate .cutline.md for my product', desc: 'Write the constraint routing engine' },
444
399
  { cmd: 'What does my persona think about X?', desc: 'AI persona feedback on features' },
@@ -453,8 +408,6 @@ export async function setupCommand(options) {
453
408
  const items = [
454
409
  { cmd: 'use cutline', desc: 'Magic phrase (also works with "use cutline to...", "using cutline...", "with cutline...") — Cutline routes to the highest-value free flow for your intent' },
455
410
  { cmd: 'Run a security vibe check on this codebase', desc: 'Free security vibe check (`code_audit`) — security, reliability, and scalability scan (3/month free)' },
456
- { cmd: 'Write tests for this PR', desc: 'Testing intent shortcut — prompts install/setup guidance if Cutline MCP is missing' },
457
- { cmd: 'use cutline to generate tests for this scope increase', desc: 'Runs free-tier test-oriented routing and verification guidance where available' },
458
411
  { cmd: 'Explore a product idea', desc: 'Free 6-act discovery flow to identify pain points and opportunities' },
459
412
  { cmd: 'Continue my exploration session', desc: 'Resume and refine an existing free exploration conversation' },
460
413
  ];
@@ -468,7 +421,5 @@ export async function setupCommand(options) {
468
421
  }
469
422
  console.log();
470
423
  console.log(chalk.dim(` cutline-mcp v${version} · docs: https://thecutline.ai/docs/setup`));
471
- console.log(chalk.dim(' Testing bootstrap:'), chalk.cyan('npx -y @vibekiln/cutline-mcp-cli@latest setup'));
472
- console.log(chalk.dim(' Optional repo policy contract:'), chalk.cyan('cutline-mcp policy-init'));
473
424
  console.log();
474
425
  }
package/dist/index.js CHANGED
@@ -10,7 +10,6 @@ import { upgradeCommand } from './commands/upgrade.js';
10
10
  import { serveCommand } from './commands/serve.js';
11
11
  import { setupCommand } from './commands/setup.js';
12
12
  import { initCommand } from './commands/init.js';
13
- import { policyInitCommand } from './commands/policy-init.js';
14
13
  const __filename = fileURLToPath(import.meta.url);
15
14
  const __dirname = dirname(__filename);
16
15
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
@@ -79,19 +78,4 @@ program
79
78
  .option('--project-root <path>', 'Project root directory (default: cwd)')
80
79
  .option('--staging', 'Use staging environment')
81
80
  .action((opts) => initCommand({ projectRoot: opts.projectRoot, staging: opts.staging }));
82
- program
83
- .command('policy-init')
84
- .description('Generate repository cutline.json policy manifest for deterministic safety verification')
85
- .option('--project-root <path>', 'Project root directory (default: cwd)')
86
- .option('--force', 'Overwrite existing cutline.json if present')
87
- .option('--min-security-score <number>', 'Minimum security score required to pass (default: 85)')
88
- .option('--max-assurance-age-hours <number>', 'Maximum assurance artifact age in hours (default: 168)')
89
- .option('--allow-unsigned-assurance', 'Do not require signed assurance artifact')
90
- .action((opts) => policyInitCommand({
91
- projectRoot: opts.projectRoot,
92
- force: Boolean(opts.force),
93
- minSecurityScore: opts.minSecurityScore,
94
- maxAssuranceAgeHours: opts.maxAssuranceAgeHours,
95
- allowUnsignedAssurance: Boolean(opts.allowUnsignedAssurance),
96
- }));
97
81
  program.parse();
@@ -305,20 +305,6 @@ function getHiddenAuditDimensions() {
305
305
  const normalized = [...new Set(hidden.map((d) => String(d).trim().toLowerCase()).filter((d) => allowed.has(d)))];
306
306
  return normalized;
307
307
  }
308
- function getStoredInstallId(options) {
309
- const config = readLocalCutlineConfig();
310
- if (!config)
311
- return null;
312
- const preferred = options?.environment === "staging" ? config.agentInstallIdStaging : config.agentInstallId;
313
- if (typeof preferred === "string" && preferred.trim()) {
314
- return preferred.trim();
315
- }
316
- const fallback = options?.environment === "staging" ? config.agentInstallId : config.agentInstallIdStaging;
317
- if (typeof fallback === "string" && fallback.trim()) {
318
- return fallback.trim();
319
- }
320
- return null;
321
- }
322
308
  function getStoredApiKey(options) {
323
309
  const includeConfig = options?.includeConfig ?? true;
324
310
  if (process.env.CUTLINE_API_KEY) {
@@ -1024,7 +1010,6 @@ export {
1024
1010
  validateAuth,
1025
1011
  resolveAuthContext,
1026
1012
  getHiddenAuditDimensions,
1027
- getStoredInstallId,
1028
1013
  requirePremiumWithAutoAuth,
1029
1014
  resolveAuthContextFree,
1030
1015
  getPublicSiteUrlForCurrentAuth,
@@ -52,7 +52,6 @@ import {
52
52
  getPremortem,
53
53
  getPublicSiteUrlForCurrentAuth,
54
54
  getScanRateLimit,
55
- getStoredInstallId,
56
55
  getTemplate,
57
56
  getTestCasesForEntity,
58
57
  hasConstraints,
@@ -76,7 +75,7 @@ import {
76
75
  upsertEntities,
77
76
  upsertNodes,
78
77
  validateRequestSize
79
- } from "./chunk-RUCYK3TR.js";
78
+ } from "./chunk-X2B5QUWO.js";
80
79
  import {
81
80
  GraphTraverser,
82
81
  computeGenericGraphMetrics,
@@ -6596,11 +6595,6 @@ async function handleCodeAudit(args, deps) {
6596
6595
  },
6597
6596
  sensitiveDataCount: scanResult.sensitive_data.fields.length,
6598
6597
  rgrPlan,
6599
- rgrAutoTrigger: {
6600
- enabled: true,
6601
- onScopeIncrease: true,
6602
- executionMode: "local_vitest"
6603
- },
6604
6598
  securityGaps: graphAnalysis.securityGaps
6605
6599
  }
6606
6600
  };
@@ -7492,132 +7486,6 @@ async function withLlmMonitor(model, fn) {
7492
7486
  }
7493
7487
  }
7494
7488
 
7495
- // ../mcp/dist/mcp/src/shared/repo-policy.js
7496
- function buildRepoPolicyRequirements(manifest) {
7497
- return {
7498
- require_security_scan: Boolean(manifest?.verification_requirements?.require_security_scan ?? true),
7499
- fail_on_critical: Boolean(manifest?.verification_requirements?.fail_on_critical ?? true),
7500
- min_security_score: Number(manifest?.verification_requirements?.min_security_score ?? 85),
7501
- require_assurance_manifest: Boolean(manifest?.verification_requirements?.require_assurance_manifest ?? true),
7502
- require_signed_assurance: Boolean(manifest?.verification_requirements?.require_signed_assurance ?? true),
7503
- max_assurance_age_hours: Number(manifest?.verification_requirements?.max_assurance_age_hours ?? 168)
7504
- };
7505
- }
7506
- function buildRepoPolicyObserved(observed) {
7507
- return {
7508
- security_score: Number(observed?.security_score ?? Number.NaN),
7509
- critical_findings_count: Number(observed?.critical_findings_count ?? Number.NaN),
7510
- assurance_available: observed?.assurance_available,
7511
- assurance_signed: observed?.assurance_signed,
7512
- assurance_age_hours: Number(observed?.assurance_age_hours ?? Number.NaN)
7513
- };
7514
- }
7515
- function evaluateRepoPolicy(requirements, observed) {
7516
- const checks = [];
7517
- const blockingReasons = [];
7518
- const requiredActions = [];
7519
- const evaluate = (id, pass, passMessage, failMessage, unknownMessage) => {
7520
- if (pass === null) {
7521
- checks.push({ id, status: "unknown", message: unknownMessage });
7522
- return;
7523
- }
7524
- if (pass) {
7525
- checks.push({ id, status: "pass", message: passMessage });
7526
- } else {
7527
- checks.push({ id, status: "fail", message: failMessage });
7528
- blockingReasons.push(failMessage);
7529
- }
7530
- };
7531
- if (requirements.require_security_scan) {
7532
- const hasScore = Number.isFinite(observed.security_score);
7533
- evaluate("security_score_minimum", hasScore ? observed.security_score >= requirements.min_security_score : null, `Security score ${observed.security_score} meets minimum ${requirements.min_security_score}.`, `Security score ${hasScore ? observed.security_score : "unknown"} below minimum ${requirements.min_security_score}.`, "Security score not provided in observed evidence.");
7534
- if (!hasScore) {
7535
- requiredActions.push("Provide observed.security_score from the latest audit.");
7536
- }
7537
- }
7538
- if (requirements.fail_on_critical) {
7539
- const hasCritical = Number.isFinite(observed.critical_findings_count);
7540
- evaluate("critical_findings_zero", hasCritical ? observed.critical_findings_count <= 0 : null, "No unresolved critical/high findings.", `${hasCritical ? observed.critical_findings_count : "Unknown"} unresolved critical/high findings detected.`, "Critical findings count not provided in observed evidence.");
7541
- if (!hasCritical) {
7542
- requiredActions.push("Provide observed.critical_findings_count from the latest audit.");
7543
- }
7544
- }
7545
- if (requirements.require_assurance_manifest) {
7546
- const available = typeof observed.assurance_available === "boolean" ? observed.assurance_available : null;
7547
- evaluate("assurance_available", available, "Assurance manifest is available.", "Assurance manifest is required but unavailable.", "Assurance availability not provided in observed evidence.");
7548
- if (available === null) {
7549
- requiredActions.push("Provide observed.assurance_available from assurance retrieval.");
7550
- }
7551
- }
7552
- if (requirements.require_signed_assurance) {
7553
- const signed = typeof observed.assurance_signed === "boolean" ? observed.assurance_signed : null;
7554
- evaluate("assurance_signed", signed, "Assurance manifest signature is present/valid.", "Policy requires signed assurance artifact.", "Assurance signature status not provided in observed evidence.");
7555
- if (signed === null) {
7556
- requiredActions.push("Provide observed.assurance_signed after verification.");
7557
- }
7558
- }
7559
- if (requirements.max_assurance_age_hours > 0) {
7560
- const hasAge = Number.isFinite(observed.assurance_age_hours);
7561
- evaluate("assurance_freshness", hasAge ? observed.assurance_age_hours <= requirements.max_assurance_age_hours : null, `Assurance age ${observed.assurance_age_hours}h within ${requirements.max_assurance_age_hours}h threshold.`, `Assurance age ${hasAge ? observed.assurance_age_hours : "unknown"}h exceeds ${requirements.max_assurance_age_hours}h threshold.`, "Assurance age not provided in observed evidence.");
7562
- if (!hasAge) {
7563
- requiredActions.push("Provide observed.assurance_age_hours.");
7564
- }
7565
- }
7566
- const failedChecks = checks.filter((c) => c.status === "fail").length;
7567
- const unknownChecks = checks.filter((c) => c.status === "unknown").length;
7568
- const status = failedChecks > 0 ? "blocked" : unknownChecks > 0 ? "insufficient_evidence" : "verified";
7569
- if (status !== "verified" && requiredActions.length === 0) {
7570
- requiredActions.push("Re-run validate_repo_policy with complete observed evidence fields.");
7571
- }
7572
- return {
7573
- status,
7574
- checks,
7575
- blocking_reasons: blockingReasons,
7576
- required_actions: [...new Set(requiredActions)],
7577
- verification_requirements: requirements
7578
- };
7579
- }
7580
-
7581
- // ../mcp/dist/mcp/src/shared/repo-policy-response.js
7582
- function resolveTelemetryAttribution(input) {
7583
- if (input.providedInstallId) {
7584
- return "provided";
7585
- }
7586
- if (input.resolvedInstallId) {
7587
- return "auto_resolved";
7588
- }
7589
- return "missing";
7590
- }
7591
- function buildInvalidPolicyResponse(input) {
7592
- return {
7593
- status: "invalid_policy",
7594
- certification_id: `policy_${(input.now || /* @__PURE__ */ new Date()).getTime()}`,
7595
- blocking_reasons: [`Policy manifest not found at ${input.manifestPath}`],
7596
- required_actions: [
7597
- "Create policy manifest: cutline-mcp policy-init",
7598
- "Commit cutline.json and re-run validate_repo_policy"
7599
- ],
7600
- telemetry_attribution: input.telemetryAttribution,
7601
- generated_at: (input.now || /* @__PURE__ */ new Date()).toISOString()
7602
- };
7603
- }
7604
- function buildPolicyVerdictResponse(input) {
7605
- return {
7606
- status: input.verdict.status,
7607
- certification_id: `policy_${(input.now || /* @__PURE__ */ new Date()).getTime()}`,
7608
- manifest_path: input.manifestPath,
7609
- policy_name: input.policyName || "cutline-policy",
7610
- schema_version: input.schemaVersion || "unknown",
7611
- verification_requirements: input.verdict.verification_requirements,
7612
- observed_evidence: input.observedEvidence,
7613
- telemetry_attribution: input.telemetryAttribution,
7614
- checks: input.verdict.checks,
7615
- blocking_reasons: input.verdict.blocking_reasons,
7616
- required_actions: input.verdict.required_actions,
7617
- generated_at: (input.now || /* @__PURE__ */ new Date()).toISOString()
7618
- };
7619
- }
7620
-
7621
7489
  // ../mcp/dist/mcp/src/cutline-server.js
7622
7490
  function mcpAudit(entry) {
7623
7491
  console.error(JSON.stringify({
@@ -7627,70 +7495,6 @@ function mcpAudit(entry) {
7627
7495
  ...entry
7628
7496
  }));
7629
7497
  }
7630
- async function emitPolicyGateEvent(input) {
7631
- if (!input.authToken || !input.installId)
7632
- return;
7633
- const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
7634
- const eventName = input.status === "verified" ? "policy_gate_passed" : "policy_gate_blocked";
7635
- try {
7636
- await fetch(`${siteUrl}/api/agent/event`, {
7637
- method: "POST",
7638
- headers: {
7639
- "Content-Type": "application/json",
7640
- Authorization: `Bearer ${input.authToken}`
7641
- },
7642
- body: JSON.stringify({
7643
- install_id: input.installId,
7644
- event_name: eventName,
7645
- event_properties: {
7646
- status: input.status,
7647
- policy_name: input.policyName || "cutline-policy",
7648
- failed_checks_count: input.failedChecks,
7649
- unknown_checks_count: input.unknownChecks,
7650
- blocking_reasons_count: input.blockingReasonsCount
7651
- }
7652
- })
7653
- });
7654
- } catch {
7655
- }
7656
- }
7657
- async function emitAgentEvent(input) {
7658
- if (!input.authToken || !input.installId)
7659
- return;
7660
- const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
7661
- try {
7662
- await fetch(`${siteUrl}/api/agent/event`, {
7663
- method: "POST",
7664
- headers: {
7665
- "Content-Type": "application/json",
7666
- Authorization: `Bearer ${input.authToken}`
7667
- },
7668
- body: JSON.stringify({
7669
- install_id: input.installId,
7670
- event_name: input.eventName,
7671
- event_properties: input.eventProperties || {}
7672
- })
7673
- });
7674
- } catch {
7675
- }
7676
- }
7677
- function isTestingIntentPrompt(prompt) {
7678
- return /\b(test|tests|testing|vitest|jest|coverage|unit test|integration test|e2e|spec file|assertion)\b/i.test(prompt);
7679
- }
7680
- function buildTestingIntentHintResponse(prompt) {
7681
- return {
7682
- detected: true,
7683
- intent: "testing",
7684
- route: "cutline_testing_rgr",
7685
- prompts: [
7686
- "use cutline to generate tests for this scope increase",
7687
- "use cutline to run the RGR loop for this change",
7688
- "use cutline to evaluate merge gates for this test run"
7689
- ],
7690
- bootstrap_if_missing: "npx -y @vibekiln/cutline-mcp-cli@latest setup",
7691
- source_prompt_excerpt: prompt.slice(0, 180)
7692
- };
7693
- }
7694
7498
  var DEFAULT_MODEL = process.env.MODEL_ID || "gemini-2.5-pro";
7695
7499
  var GOVERNANCE_ENFORCEMENT = (process.env.CUTLINE_GOVERNANCE_ENFORCEMENT || "advisory").toLowerCase() === "enforced";
7696
7500
  function buildGovernanceEnvelope(input) {
@@ -7719,26 +7523,21 @@ var ACT_NAMES = {
7719
7523
  };
7720
7524
  function assessScopeExpansionIntent(params) {
7721
7525
  const task = (params.task_description || "").trim();
7722
- const codeSnippet = (params.code_snippet || "").trim();
7723
- const intentText = `${task}
7724
- ${codeSnippet}`.trim();
7725
7526
  const filePaths = params.file_paths || [];
7726
7527
  const reasons = [];
7727
7528
  let confidence = 0;
7728
7529
  const explicitScopePatterns = [
7729
7530
  /\b(scope increase|expand(?:ing)? scope|broaden scope|new scope)\b/i,
7730
7531
  /\b(seed|ingest|add)\b.{0,24}\b(graph|constraint|entity|entities)\b/i,
7731
- /\b(new feature|new module|new domain|new subsystem)\b/i,
7732
- /\b(expand|increase|broaden)\b.{0,24}\b(coverage|surface area|scope)\b/i,
7733
- /\b(add|introduce|build|create)\b.{0,24}\b(endpoint|route|api|service|workflow)\b/i
7532
+ /\b(new feature|new module|new domain|new subsystem)\b/i
7734
7533
  ];
7735
- const hasExplicitScopeIntent = explicitScopePatterns.some((rx) => rx.test(intentText));
7534
+ const hasExplicitScopeIntent = explicitScopePatterns.some((rx) => rx.test(task));
7736
7535
  if (hasExplicitScopeIntent) {
7737
7536
  confidence += 0.55;
7738
7537
  reasons.push("Task description includes explicit scope-expansion language.");
7739
7538
  }
7740
7539
  const hintedName = params.hinted_entity_name?.trim();
7741
- const inferredName = hintedName || inferEntityNameFromTask(intentText);
7540
+ const inferredName = hintedName || inferEntityNameFromTask(task);
7742
7541
  if (inferredName) {
7743
7542
  confidence += 0.12;
7744
7543
  reasons.push("Potential new entity name detected from task context.");
@@ -8198,9 +7997,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
8198
7997
  type: "object",
8199
7998
  properties: {
8200
7999
  prompt: { type: "string" },
8201
- wikiMarkdown: { type: "string" },
8202
- auth_token: { type: "string", description: "Optional auth token for telemetry attribution" },
8203
- install_id: { type: "string", description: "Optional install ID for telemetry attribution" }
8000
+ wikiMarkdown: { type: "string" }
8204
8001
  },
8205
8002
  required: ["prompt"]
8206
8003
  }
@@ -8229,9 +8026,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
8229
8026
  inputSchema: {
8230
8027
  type: "object",
8231
8028
  properties: {
8232
- prompt: { type: "string" },
8233
- auth_token: { type: "string", description: "Optional auth token for telemetry attribution" },
8234
- install_id: { type: "string", description: "Optional install ID for telemetry attribution" }
8029
+ prompt: { type: "string" }
8235
8030
  },
8236
8031
  required: ["prompt"]
8237
8032
  }
@@ -8665,40 +8460,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
8665
8460
  required: ["product_id"]
8666
8461
  }
8667
8462
  },
8668
- {
8669
- name: "get_assurance_manifest",
8670
- description: `\u{1F513} AUTHENTICATED-FREE - Fetch signed assurance proof for "prove it's safe", "production-ready", and audit verification prompts.`,
8671
- inputSchema: {
8672
- type: "object",
8673
- properties: {
8674
- url: { type: "string", description: "Optional assurance URL override (default: NEXT_PUBLIC_SITE_URL/.well-known/cutline-assurance.json). Use target app domain when verifying deployed builds." }
8675
- }
8676
- }
8677
- },
8678
- {
8679
- name: "validate_repo_policy",
8680
- description: "\u{1F513} AUTHENTICATED-FREE - Run deterministic policy validation for secure/deploy-ready/audit prompts and return a machine-readable verdict object.",
8681
- inputSchema: {
8682
- type: "object",
8683
- properties: {
8684
- project_root: { type: "string", description: "Absolute workspace root containing cutline.json" },
8685
- manifest_path: { type: "string", description: "Optional explicit path to cutline.json (overrides project_root)" },
8686
- install_id: { type: "string", description: "Optional agent install_id for funnel attribution when emitting policy gate events (auto-resolved from local ~/.cutline-mcp/config.json when omitted)" },
8687
- observed: {
8688
- type: "object",
8689
- description: "Observed evidence from scans/assurance checks to evaluate against policy thresholds",
8690
- properties: {
8691
- security_score: { type: "number", description: "Observed security score (0-100)" },
8692
- critical_findings_count: { type: "number", description: "Observed unresolved critical/high findings count" },
8693
- assurance_available: { type: "boolean", description: "Whether assurance artifact is available" },
8694
- assurance_signed: { type: "boolean", description: "Whether assurance artifact signature is valid/present" },
8695
- assurance_age_hours: { type: "number", description: "Age of assurance evidence in hours" }
8696
- }
8697
- }
8698
- },
8699
- required: ["project_root"]
8700
- }
8701
- },
8702
8463
  {
8703
8464
  name: "code_audit",
8704
8465
  description: "\u{1F513} FREE - Security vibe check (code audit). Evaluates your codebase against a stack-aware constraint graph covering security, reliability, and scalability. No deep dive or product_id required \u2014 just point at your codebase. Shows aggregate readiness scores and top critical findings; detailed analysis and remediation require Premium. Requires a Cutline account (free). 3 scans/month.",
@@ -9008,125 +8769,9 @@ Why AI: ${idea.whyAI}`
9008
8769
  };
9009
8770
  }
9010
8771
  if (name2 === "trial_generate") {
9011
- const trialArgs = args;
9012
- const { prompt } = trialArgs;
8772
+ const { prompt } = args;
9013
8773
  const text = await cfGenerateTrialRun(prompt);
9014
- const isTestingIntent = isTestingIntentPrompt(prompt);
9015
- const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
9016
- const resolvedInstallId = trialArgs.install_id || getStoredInstallId({ environment: env });
9017
- if (isTestingIntent) {
9018
- await emitAgentEvent({
9019
- authToken: trialArgs.auth_token,
9020
- installId: resolvedInstallId || void 0,
9021
- eventName: "agent_test_intent_detected",
9022
- eventProperties: {
9023
- tool_name: "trial_generate",
9024
- route: "cutline_testing_rgr"
9025
- }
9026
- });
9027
- }
9028
- return {
9029
- content: [{
9030
- type: "text",
9031
- text: JSON.stringify({
9032
- text,
9033
- ...isTestingIntent ? { cutline_testing_route: buildTestingIntentHintResponse(prompt) } : {}
9034
- })
9035
- }]
9036
- };
9037
- }
9038
- if (name2 === "get_assurance_manifest") {
9039
- const { url } = args;
9040
- await resolveAuthContextFree(args.auth_token);
9041
- const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
9042
- const assuranceUrl = url || `${siteUrl}/.well-known/cutline-assurance.json`;
9043
- const response = await fetch(assuranceUrl, {
9044
- method: "GET",
9045
- headers: { "Accept": "application/json" }
9046
- });
9047
- if (!response.ok) {
9048
- throw new McpError(ErrorCode.InternalError, `Failed to fetch assurance manifest (${response.status}) from ${assuranceUrl}`);
9049
- }
9050
- const manifest = await response.json();
9051
- return {
9052
- content: [{
9053
- type: "text",
9054
- text: JSON.stringify({
9055
- source_url: assuranceUrl,
9056
- fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
9057
- manifest
9058
- }, null, 2)
9059
- }]
9060
- };
9061
- }
9062
- if (name2 === "validate_repo_policy") {
9063
- const policyArgs = args;
9064
- if (!policyArgs.project_root) {
9065
- throw new McpError(ErrorCode.InvalidParams, "project_root is required");
9066
- }
9067
- await resolveAuthContextFree(policyArgs.auth_token);
9068
- const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
9069
- const resolvedInstallId = policyArgs.install_id || getStoredInstallId({ environment: env });
9070
- const telemetryAttribution = resolveTelemetryAttribution({
9071
- providedInstallId: policyArgs.install_id,
9072
- resolvedInstallId: resolvedInstallId || void 0
9073
- });
9074
- const { existsSync, readFileSync } = await import("node:fs");
9075
- const { resolve, join: join4 } = await import("node:path");
9076
- const manifestPath = resolve(policyArgs.manifest_path || join4(policyArgs.project_root, "cutline.json"));
9077
- if (!existsSync(manifestPath)) {
9078
- await emitPolicyGateEvent({
9079
- authToken: policyArgs.auth_token,
9080
- installId: resolvedInstallId || void 0,
9081
- status: "invalid_policy",
9082
- policyName: "cutline-policy",
9083
- failedChecks: 1,
9084
- unknownChecks: 0,
9085
- blockingReasonsCount: 1
9086
- });
9087
- return {
9088
- content: [{
9089
- type: "text",
9090
- text: JSON.stringify(buildInvalidPolicyResponse({
9091
- manifestPath,
9092
- telemetryAttribution
9093
- }), null, 2)
9094
- }]
9095
- };
9096
- }
9097
- let manifest;
9098
- try {
9099
- manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
9100
- } catch (e) {
9101
- throw new McpError(ErrorCode.InvalidParams, `Invalid cutline.json: ${e?.message || String(e)}`);
9102
- }
9103
- const requirements = buildRepoPolicyRequirements(manifest);
9104
- const observed = buildRepoPolicyObserved(policyArgs.observed);
9105
- const verdict = evaluateRepoPolicy(requirements, observed);
9106
- const failedChecks = verdict.checks.filter((c) => c.status === "fail").length;
9107
- const unknownChecks = verdict.checks.filter((c) => c.status === "unknown").length;
9108
- await emitPolicyGateEvent({
9109
- authToken: policyArgs.auth_token,
9110
- installId: resolvedInstallId || void 0,
9111
- status: verdict.status,
9112
- policyName: manifest?.policy_name || "cutline-policy",
9113
- failedChecks,
9114
- unknownChecks,
9115
- blockingReasonsCount: verdict.blocking_reasons.length
9116
- });
9117
- return {
9118
- content: [{
9119
- type: "text",
9120
- text: JSON.stringify(buildPolicyVerdictResponse({
9121
- verdict,
9122
- manifestPath,
9123
- policyName: manifest?.policy_name,
9124
- schemaVersion: manifest?.schema_version,
9125
- observedEvidence: policyArgs.observed || {},
9126
- telemetryAttribution
9127
- }), null, 2)
9128
- }]
9129
- };
8774
+ return { content: [{ type: "text", text: JSON.stringify({ text }) }] };
9130
8775
  }
9131
8776
  if (name2 === "code_audit") {
9132
8777
  const scanArgs = args;
@@ -9427,32 +9072,9 @@ Competitive threats: ${competitors}` : ""
9427
9072
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
9428
9073
  }
9429
9074
  case "agent_chat": {
9430
- const chatArgs = args;
9431
- const { prompt, wikiMarkdown } = chatArgs;
9075
+ const { prompt, wikiMarkdown } = args;
9432
9076
  const text = await cfGenerateChatSuggestion(prompt, wikiMarkdown);
9433
- const isTestingIntent = isTestingIntentPrompt(prompt);
9434
- const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
9435
- const resolvedInstallId = chatArgs.install_id || getStoredInstallId({ environment: env });
9436
- if (isTestingIntent) {
9437
- await emitAgentEvent({
9438
- authToken: chatArgs.auth_token,
9439
- installId: resolvedInstallId || void 0,
9440
- eventName: "agent_test_intent_detected",
9441
- eventProperties: {
9442
- tool_name: "agent_chat",
9443
- route: "cutline_testing_rgr"
9444
- }
9445
- });
9446
- }
9447
- return {
9448
- content: [{
9449
- type: "text",
9450
- text: JSON.stringify({
9451
- text,
9452
- ...isTestingIntent ? { cutline_testing_route: buildTestingIntentHintResponse(prompt) } : {}
9453
- })
9454
- }]
9455
- };
9077
+ return { content: [{ type: "text", text: JSON.stringify({ text }) }] };
9456
9078
  }
9457
9079
  // Integrations
9458
9080
  case "integrations_create_issues": {
@@ -9810,7 +9432,7 @@ Meta: ${JSON.stringify(output.meta)}` }
9810
9432
  }
9811
9433
  const analysis = analyzeFileContext(fileContext);
9812
9434
  let knownEntityNames = [];
9813
- if (task_description || code_snippet || scope_entity_name) {
9435
+ if (task_description || scope_entity_name) {
9814
9436
  try {
9815
9437
  const entities = await getAllEntities(product_id);
9816
9438
  knownEntityNames = entities.map((e) => e.name);
@@ -9819,7 +9441,6 @@ Meta: ${JSON.stringify(output.meta)}` }
9819
9441
  }
9820
9442
  const scopeExpansion = assessScopeExpansionIntent({
9821
9443
  task_description,
9822
- code_snippet,
9823
9444
  file_paths,
9824
9445
  known_entity_names: knownEntityNames,
9825
9446
  hinted_entity_name: scope_entity_name
@@ -9948,8 +9569,9 @@ Meta: ${JSON.stringify(output.meta)}` }
9948
9569
  } catch (e) {
9949
9570
  }
9950
9571
  }
9951
- const autoRgrPlan = planRgrPhases(finalResults);
9572
+ let autoRgrPlan = void 0;
9952
9573
  if (autoPhase === "auto") {
9574
+ autoRgrPlan = planRgrPhases(finalResults);
9953
9575
  if (autoRgrPlan.strategy === "phased") {
9954
9576
  finalResults = finalResults.filter((c) => {
9955
9577
  const phased = filterByPhase([c], "test_spec");
@@ -11021,11 +10643,6 @@ ${JSON.stringify(metrics, null, 2)}` }
11021
10643
  ...plan,
11022
10644
  entity: rgrMatched[0].name,
11023
10645
  complexity,
11024
- auto_execution: {
11025
- scope_increase_triggers_rgr: true,
11026
- mode: "pervasive",
11027
- expected_runner: "local_vitest"
11028
- },
11029
10646
  governance
11030
10647
  }, null, 2)
11031
10648
  }]
@@ -11552,7 +11169,7 @@ Meta: ${JSON.stringify({
11552
11169
  mode: "product",
11553
11170
  codeContext: payload.codeContext || null
11554
11171
  });
11555
- runInput.productId = String(auditArgs.product_id || newJobId);
11172
+ runInput.productId = newJobId;
11556
11173
  await updatePremortem(newJobId, { payload: runInput });
11557
11174
  return { jobId: newJobId };
11558
11175
  }
@@ -78,7 +78,7 @@ import {
78
78
  upsertEdges,
79
79
  upsertEntities,
80
80
  upsertNodes
81
- } from "./chunk-RUCYK3TR.js";
81
+ } from "./chunk-X2B5QUWO.js";
82
82
  export {
83
83
  addEdges,
84
84
  addEntity,
@@ -14,7 +14,7 @@ import {
14
14
  requirePremiumWithAutoAuth,
15
15
  updateExplorationSession,
16
16
  validateRequestSize
17
- } from "./chunk-RUCYK3TR.js";
17
+ } from "./chunk-X2B5QUWO.js";
18
18
 
19
19
  // ../mcp/dist/mcp/src/exploration-server.js
20
20
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -13,7 +13,7 @@ import {
13
13
  requirePremiumWithAutoAuth,
14
14
  validateAuth,
15
15
  validateRequestSize
16
- } from "./chunk-RUCYK3TR.js";
16
+ } from "./chunk-X2B5QUWO.js";
17
17
 
18
18
  // ../mcp/dist/mcp/src/integrations-server.js
19
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -13,7 +13,7 @@ import {
13
13
  mapErrorToMcp,
14
14
  requirePremiumWithAutoAuth,
15
15
  validateRequestSize
16
- } from "./chunk-RUCYK3TR.js";
16
+ } from "./chunk-X2B5QUWO.js";
17
17
 
18
18
  // ../mcp/dist/mcp/src/output-server.js
19
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -27,7 +27,7 @@ import {
27
27
  updatePremortem,
28
28
  validateAuth,
29
29
  validateRequestSize
30
- } from "./chunk-RUCYK3TR.js";
30
+ } from "./chunk-X2B5QUWO.js";
31
31
 
32
32
  // ../mcp/dist/mcp/src/premortem-server.js
33
33
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -21,7 +21,7 @@ import {
21
21
  requirePremiumWithAutoAuth,
22
22
  validateAuth,
23
23
  validateRequestSize
24
- } from "./chunk-RUCYK3TR.js";
24
+ } from "./chunk-X2B5QUWO.js";
25
25
 
26
26
  // ../mcp/dist/mcp/src/tools-server.js
27
27
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -2,8 +2,6 @@ export interface McpConfig {
2
2
  refreshToken?: string;
3
3
  environment?: 'production' | 'staging';
4
4
  apiKey?: string;
5
- agentInstallId?: string;
6
- agentInstallIdStaging?: string;
7
5
  }
8
6
  export declare function saveConfig(config: McpConfig): void;
9
7
  export declare function loadConfig(): McpConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibekiln/cutline-mcp-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "CLI and MCP servers for Cutline — authenticate, then run constraint-aware MCP servers in Cursor or any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,9 +0,0 @@
1
- interface PolicyInitOptions {
2
- projectRoot?: string;
3
- force?: boolean;
4
- minSecurityScore?: string;
5
- maxAssuranceAgeHours?: string;
6
- allowUnsignedAssurance?: boolean;
7
- }
8
- export declare function policyInitCommand(options: PolicyInitOptions): Promise<void>;
9
- export {};
@@ -1,51 +0,0 @@
1
- import chalk from 'chalk';
2
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
- import { join, resolve } from 'node:path';
4
- function parseNumber(raw, fallback, min, max) {
5
- const value = Number(raw);
6
- if (!Number.isFinite(value))
7
- return fallback;
8
- return Math.max(min, Math.min(max, Math.floor(value)));
9
- }
10
- export async function policyInitCommand(options) {
11
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
12
- const policyPath = join(projectRoot, 'cutline.json');
13
- const minSecurityScore = parseNumber(options.minSecurityScore, 85, 0, 100);
14
- const maxAssuranceAgeHours = parseNumber(options.maxAssuranceAgeHours, 168, 1, 24 * 365);
15
- if (existsSync(policyPath) && !options.force) {
16
- console.log(chalk.yellow('cutline.json already exists.'));
17
- console.log(chalk.dim('Use --force to overwrite.'));
18
- console.log(chalk.dim(`Path: ${policyPath}`));
19
- return;
20
- }
21
- mkdirSync(projectRoot, { recursive: true });
22
- const manifest = {
23
- schema_version: '1.0.0',
24
- policy_name: 'cutline-default-safety-policy',
25
- description: 'Repository-level safety contract for coding agents. Use with validate_repo_policy before claiming production readiness.',
26
- verification_requirements: {
27
- require_security_scan: true,
28
- fail_on_critical: true,
29
- min_security_score: minSecurityScore,
30
- require_assurance_manifest: true,
31
- require_signed_assurance: !options.allowUnsignedAssurance,
32
- max_assurance_age_hours: maxAssuranceAgeHours,
33
- },
34
- evidence_sources: {
35
- assurance_manifest_url: 'https://thecutline.ai/.well-known/cutline-assurance.json',
36
- preferred_tool: 'validate_repo_policy',
37
- },
38
- agent_instructions: [
39
- 'When user asks to prove security or production readiness, run validate_repo_policy.',
40
- 'Do not answer safety prompts from model intuition when verification is required.',
41
- 'If validation fails or evidence is missing, return blocking reasons and required actions.',
42
- ],
43
- };
44
- writeFileSync(policyPath, JSON.stringify(manifest, null, 2) + '\n');
45
- console.log(chalk.green('✓ Generated cutline.json policy manifest'));
46
- console.log(chalk.dim(` ${policyPath}`));
47
- console.log();
48
- console.log(chalk.bold('Suggested next prompt in your coding agent:'));
49
- console.log(chalk.cyan(' "Validate this repo against cutline.json and prove it is safe to deploy."'));
50
- console.log();
51
- }
@@ -1,16 +0,0 @@
1
- type AgentEventName = 'install_completed' | 'first_tool_call_success' | 'tool_call_failed' | 'heartbeat' | 'policy_gate_passed' | 'policy_gate_blocked' | 'upgrade_clicked' | 'upgrade_completed' | 'agent_test_intent_detected' | 'agent_cutline_install_prompted' | 'agent_cutline_install_completed' | 'agent_first_rgr_test_call_success';
2
- export declare function registerAgentInstall(input: {
3
- idToken: string;
4
- staging?: boolean;
5
- projectRoot: string;
6
- sourceSurface: string;
7
- hostAgent?: string;
8
- }): Promise<string | null>;
9
- export declare function trackAgentEvent(input: {
10
- idToken: string;
11
- installId: string;
12
- eventName: AgentEventName;
13
- eventProperties?: Record<string, unknown>;
14
- staging?: boolean;
15
- }): Promise<void>;
16
- export {};
@@ -1,64 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { getConfig } from './config.js';
3
- import { loadConfig, saveConfig } from './config-store.js';
4
- function installIdKey(staging) {
5
- return staging ? 'agentInstallIdStaging' : 'agentInstallId';
6
- }
7
- export async function registerAgentInstall(input) {
8
- const key = installIdKey(input.staging);
9
- const existing = loadConfig();
10
- const persistedInstallId = typeof existing[key] === 'string' ? existing[key] : null;
11
- if (persistedInstallId)
12
- return persistedInstallId;
13
- const { BASE_URL } = getConfig({ staging: input.staging });
14
- const installationKey = createHash('sha256')
15
- .update(`${input.projectRoot}:${input.staging ? 'staging' : 'production'}`)
16
- .digest('hex')
17
- .slice(0, 32);
18
- const workspaceId = createHash('sha256').update(input.projectRoot).digest('hex').slice(0, 24);
19
- try {
20
- const response = await fetch(`${BASE_URL}/api/agent/register`, {
21
- method: 'POST',
22
- headers: {
23
- 'Content-Type': 'application/json',
24
- Authorization: `Bearer ${input.idToken}`,
25
- },
26
- body: JSON.stringify({
27
- installation_key: installationKey,
28
- source_surface: input.sourceSurface,
29
- host_agent: input.hostAgent || 'cutline-mcp-cli',
30
- workspace_id: workspaceId,
31
- }),
32
- });
33
- if (!response.ok)
34
- return null;
35
- const payload = await response.json();
36
- if (!payload.install_id)
37
- return null;
38
- saveConfig({ [key]: payload.install_id });
39
- return payload.install_id;
40
- }
41
- catch {
42
- return null;
43
- }
44
- }
45
- export async function trackAgentEvent(input) {
46
- const { BASE_URL } = getConfig({ staging: input.staging });
47
- try {
48
- await fetch(`${BASE_URL}/api/agent/event`, {
49
- method: 'POST',
50
- headers: {
51
- 'Content-Type': 'application/json',
52
- Authorization: `Bearer ${input.idToken}`,
53
- },
54
- body: JSON.stringify({
55
- install_id: input.installId,
56
- event_name: input.eventName,
57
- event_properties: input.eventProperties || {},
58
- }),
59
- });
60
- }
61
- catch {
62
- // Best effort telemetry; never block CLI command success.
63
- }
64
- }