@vibekiln/cutline-mcp-cli 0.11.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 +0 -12
- package/dist/commands/init.d.ts +0 -4
- package/dist/commands/init.js +0 -62
- package/dist/commands/setup.d.ts +0 -4
- package/dist/commands/setup.js +1 -74
- package/dist/index.js +1 -36
- package/dist/servers/{chunk-RUCYK3TR.js → chunk-X2B5QUWO.js} +0 -15
- package/dist/servers/cutline-server.js +14 -489
- package/dist/servers/{data-client-RY2DCLME.js → data-client-AQ5DGSAR.js} +1 -1
- package/dist/servers/exploration-server.js +1 -1
- package/dist/servers/integrations-server.js +1 -1
- package/dist/servers/output-server.js +1 -1
- package/dist/servers/premortem-server.js +1 -1
- package/dist/servers/tools-server.js +1 -1
- package/dist/utils/config-store.d.ts +0 -2
- package/package.json +1 -1
- package/dist/commands/policy-init.d.ts +0 -9
- package/dist/commands/policy-init.js +0 -51
- package/dist/utils/agent-funnel.d.ts +0 -19
- package/dist/utils/agent-funnel.js +0 -67
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:
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -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,53 +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: options.sourceSurface || 'cli_init',
|
|
439
|
-
hostAgent: options.hostAgent || 'cutline-mcp-cli',
|
|
440
|
-
campaign: options.campaign,
|
|
441
|
-
metadata: options.queryCluster
|
|
442
|
-
? { query_cluster: options.queryCluster, discovery_flow: 'token_limit' }
|
|
443
|
-
: undefined,
|
|
444
|
-
});
|
|
445
|
-
if (installId) {
|
|
446
|
-
await trackAgentEvent({
|
|
447
|
-
idToken: auth.idToken,
|
|
448
|
-
installId,
|
|
449
|
-
eventName: 'install_completed',
|
|
450
|
-
staging: options.staging,
|
|
451
|
-
eventProperties: {
|
|
452
|
-
command: 'init',
|
|
453
|
-
tier,
|
|
454
|
-
has_product_graph: Boolean(config?.product_id),
|
|
455
|
-
},
|
|
456
|
-
});
|
|
457
|
-
await trackAgentEvent({
|
|
458
|
-
idToken: auth.idToken,
|
|
459
|
-
installId,
|
|
460
|
-
eventName: 'first_tool_call_success',
|
|
461
|
-
staging: options.staging,
|
|
462
|
-
eventProperties: {
|
|
463
|
-
command: 'init',
|
|
464
|
-
generated_rules: filesWritten.length,
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
if (options.queryCluster) {
|
|
468
|
-
await trackAgentEvent({
|
|
469
|
-
idToken: auth.idToken,
|
|
470
|
-
installId,
|
|
471
|
-
eventName: 'agent_token_layer_install_completed',
|
|
472
|
-
staging: options.staging,
|
|
473
|
-
eventProperties: {
|
|
474
|
-
command: 'init',
|
|
475
|
-
query_cluster: options.queryCluster,
|
|
476
|
-
route: 'cutline_token_layer_install',
|
|
477
|
-
},
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
420
|
}
|
package/dist/commands/setup.d.ts
CHANGED
package/dist/commands/setup.js
CHANGED
|
@@ -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);
|
|
@@ -372,73 +371,7 @@ export async function setupCommand(options) {
|
|
|
372
371
|
}
|
|
373
372
|
// ── 4. Generate IDE rules ────────────────────────────────────────────────
|
|
374
373
|
console.log(chalk.bold(' Generating IDE rules...\n'));
|
|
375
|
-
await initCommand({
|
|
376
|
-
projectRoot: options.projectRoot,
|
|
377
|
-
staging: options.staging,
|
|
378
|
-
sourceSurface: options.sourceSurface,
|
|
379
|
-
campaign: options.campaign,
|
|
380
|
-
queryCluster: options.queryCluster,
|
|
381
|
-
hostAgent: options.hostAgent,
|
|
382
|
-
});
|
|
383
|
-
if (idToken) {
|
|
384
|
-
const installId = await registerAgentInstall({
|
|
385
|
-
idToken,
|
|
386
|
-
staging: options.staging,
|
|
387
|
-
projectRoot,
|
|
388
|
-
sourceSurface: options.sourceSurface || 'cli_setup',
|
|
389
|
-
hostAgent: options.hostAgent || 'cutline-mcp-cli',
|
|
390
|
-
campaign: options.campaign,
|
|
391
|
-
metadata: options.queryCluster
|
|
392
|
-
? { query_cluster: options.queryCluster, discovery_flow: 'token_limit' }
|
|
393
|
-
: undefined,
|
|
394
|
-
});
|
|
395
|
-
if (installId) {
|
|
396
|
-
await trackAgentEvent({
|
|
397
|
-
idToken,
|
|
398
|
-
installId,
|
|
399
|
-
eventName: 'install_completed',
|
|
400
|
-
staging: options.staging,
|
|
401
|
-
eventProperties: {
|
|
402
|
-
command: 'setup',
|
|
403
|
-
tier,
|
|
404
|
-
graph_connected: graphConnected,
|
|
405
|
-
},
|
|
406
|
-
});
|
|
407
|
-
await trackAgentEvent({
|
|
408
|
-
idToken,
|
|
409
|
-
installId,
|
|
410
|
-
eventName: 'first_tool_call_success',
|
|
411
|
-
staging: options.staging,
|
|
412
|
-
eventProperties: {
|
|
413
|
-
command: 'setup',
|
|
414
|
-
flow: 'onboarding',
|
|
415
|
-
},
|
|
416
|
-
});
|
|
417
|
-
await trackAgentEvent({
|
|
418
|
-
idToken,
|
|
419
|
-
installId,
|
|
420
|
-
eventName: 'agent_cutline_install_completed',
|
|
421
|
-
staging: options.staging,
|
|
422
|
-
eventProperties: {
|
|
423
|
-
command: 'setup',
|
|
424
|
-
route: 'testing_rgr',
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
if (options.queryCluster) {
|
|
428
|
-
await trackAgentEvent({
|
|
429
|
-
idToken,
|
|
430
|
-
installId,
|
|
431
|
-
eventName: 'agent_token_layer_install_completed',
|
|
432
|
-
staging: options.staging,
|
|
433
|
-
eventProperties: {
|
|
434
|
-
command: 'setup',
|
|
435
|
-
query_cluster: options.queryCluster,
|
|
436
|
-
route: 'cutline_token_layer_install',
|
|
437
|
-
},
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
374
|
+
await initCommand({ projectRoot: options.projectRoot, staging: options.staging });
|
|
442
375
|
// ── 5. Claude Code one-liners ────────────────────────────────────────────
|
|
443
376
|
console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
|
|
444
377
|
console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
|
|
@@ -458,11 +391,9 @@ export async function setupCommand(options) {
|
|
|
458
391
|
const items = [
|
|
459
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' },
|
|
460
393
|
{ cmd: 'Run a deep dive on my product idea', desc: 'Pre-mortem analysis — risks, assumptions, experiments' },
|
|
461
|
-
{ cmd: 'Write tests for this PR', desc: 'Testing intent shortcut — Cutline should route to graph-grounded test generation + RGR verification loop' },
|
|
462
394
|
{ cmd: 'Plan this feature with constraints from my product', desc: 'RGR plan — constraint-aware implementation roadmap' },
|
|
463
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)' },
|
|
464
396
|
{ cmd: 'Run an engineering vibe check for my product', desc: 'Premium deep vibe check (`engineering_audit`) — product-linked analysis + RGR remediation plan' },
|
|
465
|
-
{ cmd: 'use cutline to generate and run tests for this scope increase', desc: 'Preferred prompt for pervasive red/green loop execution' },
|
|
466
397
|
{ cmd: 'Check constraints for src/api/upload.ts', desc: 'Get NFR boundaries for a specific file' },
|
|
467
398
|
{ cmd: 'Generate .cutline.md for my product', desc: 'Write the constraint routing engine' },
|
|
468
399
|
{ cmd: 'What does my persona think about X?', desc: 'AI persona feedback on features' },
|
|
@@ -477,8 +408,6 @@ export async function setupCommand(options) {
|
|
|
477
408
|
const items = [
|
|
478
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' },
|
|
479
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)' },
|
|
480
|
-
{ cmd: 'Write tests for this PR', desc: 'Testing intent shortcut — prompts install/setup guidance if Cutline MCP is missing' },
|
|
481
|
-
{ cmd: 'use cutline to generate tests for this scope increase', desc: 'Runs free-tier test-oriented routing and verification guidance where available' },
|
|
482
411
|
{ cmd: 'Explore a product idea', desc: 'Free 6-act discovery flow to identify pain points and opportunities' },
|
|
483
412
|
{ cmd: 'Continue my exploration session', desc: 'Resume and refine an existing free exploration conversation' },
|
|
484
413
|
];
|
|
@@ -492,7 +421,5 @@ export async function setupCommand(options) {
|
|
|
492
421
|
}
|
|
493
422
|
console.log();
|
|
494
423
|
console.log(chalk.dim(` cutline-mcp v${version} · docs: https://thecutline.ai/docs/setup`));
|
|
495
|
-
console.log(chalk.dim(' Testing bootstrap:'), chalk.cyan('npx -y @vibekiln/cutline-mcp-cli@latest setup'));
|
|
496
|
-
console.log(chalk.dim(' Optional repo policy contract:'), chalk.cyan('cutline-mcp policy-init'));
|
|
497
424
|
console.log();
|
|
498
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'));
|
|
@@ -64,10 +63,6 @@ program
|
|
|
64
63
|
.option('--staging', 'Use staging environment')
|
|
65
64
|
.option('--skip-login', 'Skip authentication (use existing credentials)')
|
|
66
65
|
.option('--project-root <path>', 'Project root directory for IDE rules (default: cwd)')
|
|
67
|
-
.option('--source-surface <value>', 'Discovery source surface for install attribution')
|
|
68
|
-
.option('--campaign <value>', 'Campaign label for install attribution')
|
|
69
|
-
.option('--query-cluster <value>', 'Query intent cluster (e.g. token_limit_context_overflow)')
|
|
70
|
-
.option('--host-agent <value>', 'Host agent name for install attribution (default: cutline-mcp-cli)')
|
|
71
66
|
.option('--hide-audit-dimension <name>', 'Hide one audit dimension in surfaced code audit output (repeatable)', (value, prev) => [...prev, value], [])
|
|
72
67
|
.option('--hide-audit-dimensions <csv>', 'Hide multiple audit dimensions (comma-separated: engineering,security,reliability,scalability,compliance)')
|
|
73
68
|
.action((opts) => setupCommand({
|
|
@@ -76,41 +71,11 @@ program
|
|
|
76
71
|
projectRoot: opts.projectRoot,
|
|
77
72
|
hideAuditDimension: opts.hideAuditDimension,
|
|
78
73
|
hideAuditDimensions: opts.hideAuditDimensions,
|
|
79
|
-
sourceSurface: opts.sourceSurface,
|
|
80
|
-
campaign: opts.campaign,
|
|
81
|
-
queryCluster: opts.queryCluster,
|
|
82
|
-
hostAgent: opts.hostAgent,
|
|
83
74
|
}));
|
|
84
75
|
program
|
|
85
76
|
.command('init')
|
|
86
77
|
.description('Generate IDE rules only (setup runs this automatically)')
|
|
87
78
|
.option('--project-root <path>', 'Project root directory (default: cwd)')
|
|
88
79
|
.option('--staging', 'Use staging environment')
|
|
89
|
-
.
|
|
90
|
-
.option('--campaign <value>', 'Campaign label for install attribution')
|
|
91
|
-
.option('--query-cluster <value>', 'Query intent cluster (e.g. token_limit_context_overflow)')
|
|
92
|
-
.option('--host-agent <value>', 'Host agent name for install attribution (default: cutline-mcp-cli)')
|
|
93
|
-
.action((opts) => initCommand({
|
|
94
|
-
projectRoot: opts.projectRoot,
|
|
95
|
-
staging: opts.staging,
|
|
96
|
-
sourceSurface: opts.sourceSurface,
|
|
97
|
-
campaign: opts.campaign,
|
|
98
|
-
queryCluster: opts.queryCluster,
|
|
99
|
-
hostAgent: opts.hostAgent,
|
|
100
|
-
}));
|
|
101
|
-
program
|
|
102
|
-
.command('policy-init')
|
|
103
|
-
.description('Generate repository cutline.json policy manifest for deterministic safety verification')
|
|
104
|
-
.option('--project-root <path>', 'Project root directory (default: cwd)')
|
|
105
|
-
.option('--force', 'Overwrite existing cutline.json if present')
|
|
106
|
-
.option('--min-security-score <number>', 'Minimum security score required to pass (default: 85)')
|
|
107
|
-
.option('--max-assurance-age-hours <number>', 'Maximum assurance artifact age in hours (default: 168)')
|
|
108
|
-
.option('--allow-unsigned-assurance', 'Do not require signed assurance artifact')
|
|
109
|
-
.action((opts) => policyInitCommand({
|
|
110
|
-
projectRoot: opts.projectRoot,
|
|
111
|
-
force: Boolean(opts.force),
|
|
112
|
-
minSecurityScore: opts.minSecurityScore,
|
|
113
|
-
maxAssuranceAgeHours: opts.maxAssuranceAgeHours,
|
|
114
|
-
allowUnsignedAssurance: Boolean(opts.allowUnsignedAssurance),
|
|
115
|
-
}));
|
|
80
|
+
.action((opts) => initCommand({ projectRoot: opts.projectRoot, staging: opts.staging }));
|
|
116
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-
|
|
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,192 +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
|
-
// ../mcp/dist/mcp/src/shared/discovery-intent.js
|
|
7622
|
-
var TESTING_INTENT_REGEX = /\b(test|tests|testing|vitest|jest|coverage|unit test|integration test|e2e|spec file|assertion)\b/i;
|
|
7623
|
-
var TOKEN_INTENT_PATTERNS = [
|
|
7624
|
-
{ regex: /\b(rate limit|request limit|429|quota exceeded)\b/i, cluster: "rate_limit" },
|
|
7625
|
-
{
|
|
7626
|
-
regex: /\b(prompt (is )?too long|context window|context overflow|too many tokens|token limit)\b/i,
|
|
7627
|
-
cluster: "context_overflow"
|
|
7628
|
-
},
|
|
7629
|
-
{
|
|
7630
|
-
regex: /\b(token burn|burning credits|too expensive|reduce tokens|save tokens|token usage)\b/i,
|
|
7631
|
-
cluster: "token_cost"
|
|
7632
|
-
}
|
|
7633
|
-
];
|
|
7634
|
-
function isTestingIntentPrompt(prompt) {
|
|
7635
|
-
return TESTING_INTENT_REGEX.test(prompt);
|
|
7636
|
-
}
|
|
7637
|
-
function detectTokenLimitIntentPrompt(prompt) {
|
|
7638
|
-
for (const pattern of TOKEN_INTENT_PATTERNS) {
|
|
7639
|
-
const match = prompt.match(pattern.regex);
|
|
7640
|
-
if (match) {
|
|
7641
|
-
return {
|
|
7642
|
-
detected: true,
|
|
7643
|
-
cluster: pattern.cluster,
|
|
7644
|
-
matched_pattern: match[0]
|
|
7645
|
-
};
|
|
7646
|
-
}
|
|
7647
|
-
}
|
|
7648
|
-
return { detected: false };
|
|
7649
|
-
}
|
|
7650
|
-
function buildTestingIntentHintResponse(prompt) {
|
|
7651
|
-
return {
|
|
7652
|
-
detected: true,
|
|
7653
|
-
intent: "testing",
|
|
7654
|
-
route: "cutline_testing_rgr",
|
|
7655
|
-
prompts: [
|
|
7656
|
-
"use cutline to generate tests for this scope increase",
|
|
7657
|
-
"use cutline to run the RGR loop for this change",
|
|
7658
|
-
"use cutline to evaluate merge gates for this test run"
|
|
7659
|
-
],
|
|
7660
|
-
bootstrap_if_missing: "npx -y @vibekiln/cutline-mcp-cli@latest setup",
|
|
7661
|
-
source_prompt_excerpt: prompt.slice(0, 180)
|
|
7662
|
-
};
|
|
7663
|
-
}
|
|
7664
|
-
function buildTokenLimitIntentHintResponse(prompt, match) {
|
|
7665
|
-
return {
|
|
7666
|
-
detected: true,
|
|
7667
|
-
intent: "token_limit",
|
|
7668
|
-
route: "cutline_token_layer_install",
|
|
7669
|
-
query_cluster: match.cluster || "context_overflow",
|
|
7670
|
-
matched_pattern: match.matched_pattern || null,
|
|
7671
|
-
prompts: [
|
|
7672
|
-
"use cutline to reduce context window pressure before tool calls",
|
|
7673
|
-
"use cutline to apply token-saving policy and retry this flow",
|
|
7674
|
-
"use cutline to install deterministic context guardrails for this repo"
|
|
7675
|
-
],
|
|
7676
|
-
bootstrap_if_missing: "npx -y @vibekiln/cutline-mcp-cli@latest setup",
|
|
7677
|
-
source_prompt_excerpt: prompt.slice(0, 180)
|
|
7678
|
-
};
|
|
7679
|
-
}
|
|
7680
|
-
|
|
7681
7489
|
// ../mcp/dist/mcp/src/cutline-server.js
|
|
7682
7490
|
function mcpAudit(entry) {
|
|
7683
7491
|
console.error(JSON.stringify({
|
|
@@ -7687,56 +7495,8 @@ function mcpAudit(entry) {
|
|
|
7687
7495
|
...entry
|
|
7688
7496
|
}));
|
|
7689
7497
|
}
|
|
7690
|
-
async function emitPolicyGateEvent(input) {
|
|
7691
|
-
if (!input.authToken || !input.installId)
|
|
7692
|
-
return;
|
|
7693
|
-
const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
|
|
7694
|
-
const eventName = input.status === "verified" ? "policy_gate_passed" : "policy_gate_blocked";
|
|
7695
|
-
try {
|
|
7696
|
-
await fetch(`${siteUrl}/api/agent/event`, {
|
|
7697
|
-
method: "POST",
|
|
7698
|
-
headers: {
|
|
7699
|
-
"Content-Type": "application/json",
|
|
7700
|
-
Authorization: `Bearer ${input.authToken}`
|
|
7701
|
-
},
|
|
7702
|
-
body: JSON.stringify({
|
|
7703
|
-
install_id: input.installId,
|
|
7704
|
-
event_name: eventName,
|
|
7705
|
-
event_properties: {
|
|
7706
|
-
status: input.status,
|
|
7707
|
-
policy_name: input.policyName || "cutline-policy",
|
|
7708
|
-
failed_checks_count: input.failedChecks,
|
|
7709
|
-
unknown_checks_count: input.unknownChecks,
|
|
7710
|
-
blocking_reasons_count: input.blockingReasonsCount
|
|
7711
|
-
}
|
|
7712
|
-
})
|
|
7713
|
-
});
|
|
7714
|
-
} catch {
|
|
7715
|
-
}
|
|
7716
|
-
}
|
|
7717
|
-
async function emitAgentEvent(input) {
|
|
7718
|
-
if (!input.authToken || !input.installId)
|
|
7719
|
-
return;
|
|
7720
|
-
const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
|
|
7721
|
-
try {
|
|
7722
|
-
await fetch(`${siteUrl}/api/agent/event`, {
|
|
7723
|
-
method: "POST",
|
|
7724
|
-
headers: {
|
|
7725
|
-
"Content-Type": "application/json",
|
|
7726
|
-
Authorization: `Bearer ${input.authToken}`
|
|
7727
|
-
},
|
|
7728
|
-
body: JSON.stringify({
|
|
7729
|
-
install_id: input.installId,
|
|
7730
|
-
event_name: input.eventName,
|
|
7731
|
-
event_properties: input.eventProperties || {}
|
|
7732
|
-
})
|
|
7733
|
-
});
|
|
7734
|
-
} catch {
|
|
7735
|
-
}
|
|
7736
|
-
}
|
|
7737
7498
|
var DEFAULT_MODEL = process.env.MODEL_ID || "gemini-2.5-pro";
|
|
7738
7499
|
var GOVERNANCE_ENFORCEMENT = (process.env.CUTLINE_GOVERNANCE_ENFORCEMENT || "advisory").toLowerCase() === "enforced";
|
|
7739
|
-
var TOKEN_INTENT_HINT_ENABLED = (process.env.CUTLINE_TOKEN_INTENT_HINT_ENABLED || "false").toLowerCase() === "true";
|
|
7740
7500
|
function buildGovernanceEnvelope(input) {
|
|
7741
7501
|
return {
|
|
7742
7502
|
decision: input.decision,
|
|
@@ -7763,26 +7523,21 @@ var ACT_NAMES = {
|
|
|
7763
7523
|
};
|
|
7764
7524
|
function assessScopeExpansionIntent(params) {
|
|
7765
7525
|
const task = (params.task_description || "").trim();
|
|
7766
|
-
const codeSnippet = (params.code_snippet || "").trim();
|
|
7767
|
-
const intentText = `${task}
|
|
7768
|
-
${codeSnippet}`.trim();
|
|
7769
7526
|
const filePaths = params.file_paths || [];
|
|
7770
7527
|
const reasons = [];
|
|
7771
7528
|
let confidence = 0;
|
|
7772
7529
|
const explicitScopePatterns = [
|
|
7773
7530
|
/\b(scope increase|expand(?:ing)? scope|broaden scope|new scope)\b/i,
|
|
7774
7531
|
/\b(seed|ingest|add)\b.{0,24}\b(graph|constraint|entity|entities)\b/i,
|
|
7775
|
-
/\b(new feature|new module|new domain|new subsystem)\b/i
|
|
7776
|
-
/\b(expand|increase|broaden)\b.{0,24}\b(coverage|surface area|scope)\b/i,
|
|
7777
|
-
/\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
|
|
7778
7533
|
];
|
|
7779
|
-
const hasExplicitScopeIntent = explicitScopePatterns.some((rx) => rx.test(
|
|
7534
|
+
const hasExplicitScopeIntent = explicitScopePatterns.some((rx) => rx.test(task));
|
|
7780
7535
|
if (hasExplicitScopeIntent) {
|
|
7781
7536
|
confidence += 0.55;
|
|
7782
7537
|
reasons.push("Task description includes explicit scope-expansion language.");
|
|
7783
7538
|
}
|
|
7784
7539
|
const hintedName = params.hinted_entity_name?.trim();
|
|
7785
|
-
const inferredName = hintedName || inferEntityNameFromTask(
|
|
7540
|
+
const inferredName = hintedName || inferEntityNameFromTask(task);
|
|
7786
7541
|
if (inferredName) {
|
|
7787
7542
|
confidence += 0.12;
|
|
7788
7543
|
reasons.push("Potential new entity name detected from task context.");
|
|
@@ -8242,9 +7997,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
8242
7997
|
type: "object",
|
|
8243
7998
|
properties: {
|
|
8244
7999
|
prompt: { type: "string" },
|
|
8245
|
-
wikiMarkdown: { type: "string" }
|
|
8246
|
-
auth_token: { type: "string", description: "Optional auth token for telemetry attribution" },
|
|
8247
|
-
install_id: { type: "string", description: "Optional install ID for telemetry attribution" }
|
|
8000
|
+
wikiMarkdown: { type: "string" }
|
|
8248
8001
|
},
|
|
8249
8002
|
required: ["prompt"]
|
|
8250
8003
|
}
|
|
@@ -8273,9 +8026,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
8273
8026
|
inputSchema: {
|
|
8274
8027
|
type: "object",
|
|
8275
8028
|
properties: {
|
|
8276
|
-
prompt: { type: "string" }
|
|
8277
|
-
auth_token: { type: "string", description: "Optional auth token for telemetry attribution" },
|
|
8278
|
-
install_id: { type: "string", description: "Optional install ID for telemetry attribution" }
|
|
8029
|
+
prompt: { type: "string" }
|
|
8279
8030
|
},
|
|
8280
8031
|
required: ["prompt"]
|
|
8281
8032
|
}
|
|
@@ -8709,40 +8460,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
8709
8460
|
required: ["product_id"]
|
|
8710
8461
|
}
|
|
8711
8462
|
},
|
|
8712
|
-
{
|
|
8713
|
-
name: "get_assurance_manifest",
|
|
8714
|
-
description: `\u{1F513} AUTHENTICATED-FREE - Fetch signed assurance proof for "prove it's safe", "production-ready", and audit verification prompts.`,
|
|
8715
|
-
inputSchema: {
|
|
8716
|
-
type: "object",
|
|
8717
|
-
properties: {
|
|
8718
|
-
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." }
|
|
8719
|
-
}
|
|
8720
|
-
}
|
|
8721
|
-
},
|
|
8722
|
-
{
|
|
8723
|
-
name: "validate_repo_policy",
|
|
8724
|
-
description: "\u{1F513} AUTHENTICATED-FREE - Run deterministic policy validation for secure/deploy-ready/audit prompts and return a machine-readable verdict object.",
|
|
8725
|
-
inputSchema: {
|
|
8726
|
-
type: "object",
|
|
8727
|
-
properties: {
|
|
8728
|
-
project_root: { type: "string", description: "Absolute workspace root containing cutline.json" },
|
|
8729
|
-
manifest_path: { type: "string", description: "Optional explicit path to cutline.json (overrides project_root)" },
|
|
8730
|
-
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)" },
|
|
8731
|
-
observed: {
|
|
8732
|
-
type: "object",
|
|
8733
|
-
description: "Observed evidence from scans/assurance checks to evaluate against policy thresholds",
|
|
8734
|
-
properties: {
|
|
8735
|
-
security_score: { type: "number", description: "Observed security score (0-100)" },
|
|
8736
|
-
critical_findings_count: { type: "number", description: "Observed unresolved critical/high findings count" },
|
|
8737
|
-
assurance_available: { type: "boolean", description: "Whether assurance artifact is available" },
|
|
8738
|
-
assurance_signed: { type: "boolean", description: "Whether assurance artifact signature is valid/present" },
|
|
8739
|
-
assurance_age_hours: { type: "number", description: "Age of assurance evidence in hours" }
|
|
8740
|
-
}
|
|
8741
|
-
}
|
|
8742
|
-
},
|
|
8743
|
-
required: ["project_root"]
|
|
8744
|
-
}
|
|
8745
|
-
},
|
|
8746
8463
|
{
|
|
8747
8464
|
name: "code_audit",
|
|
8748
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.",
|
|
@@ -9052,149 +8769,9 @@ Why AI: ${idea.whyAI}`
|
|
|
9052
8769
|
};
|
|
9053
8770
|
}
|
|
9054
8771
|
if (name2 === "trial_generate") {
|
|
9055
|
-
const
|
|
9056
|
-
const { prompt } = trialArgs;
|
|
8772
|
+
const { prompt } = args;
|
|
9057
8773
|
const text = await cfGenerateTrialRun(prompt);
|
|
9058
|
-
|
|
9059
|
-
const tokenIntent = TOKEN_INTENT_HINT_ENABLED ? detectTokenLimitIntentPrompt(prompt) : { detected: false };
|
|
9060
|
-
const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
|
|
9061
|
-
const resolvedInstallId = trialArgs.install_id || getStoredInstallId({ environment: env });
|
|
9062
|
-
if (isTestingIntent) {
|
|
9063
|
-
await emitAgentEvent({
|
|
9064
|
-
authToken: trialArgs.auth_token,
|
|
9065
|
-
installId: resolvedInstallId || void 0,
|
|
9066
|
-
eventName: "agent_test_intent_detected",
|
|
9067
|
-
eventProperties: {
|
|
9068
|
-
tool_name: "trial_generate",
|
|
9069
|
-
route: "cutline_testing_rgr"
|
|
9070
|
-
}
|
|
9071
|
-
});
|
|
9072
|
-
}
|
|
9073
|
-
if (tokenIntent.detected) {
|
|
9074
|
-
await emitAgentEvent({
|
|
9075
|
-
authToken: trialArgs.auth_token,
|
|
9076
|
-
installId: resolvedInstallId || void 0,
|
|
9077
|
-
eventName: "agent_token_limit_intent_detected",
|
|
9078
|
-
eventProperties: {
|
|
9079
|
-
tool_name: "trial_generate",
|
|
9080
|
-
route: "cutline_token_layer_install",
|
|
9081
|
-
query_cluster: tokenIntent.cluster
|
|
9082
|
-
}
|
|
9083
|
-
});
|
|
9084
|
-
await emitAgentEvent({
|
|
9085
|
-
authToken: trialArgs.auth_token,
|
|
9086
|
-
installId: resolvedInstallId || void 0,
|
|
9087
|
-
eventName: "agent_token_layer_install_prompted",
|
|
9088
|
-
eventProperties: {
|
|
9089
|
-
tool_name: "trial_generate",
|
|
9090
|
-
route: "cutline_token_layer_install",
|
|
9091
|
-
query_cluster: tokenIntent.cluster
|
|
9092
|
-
}
|
|
9093
|
-
});
|
|
9094
|
-
}
|
|
9095
|
-
return {
|
|
9096
|
-
content: [{
|
|
9097
|
-
type: "text",
|
|
9098
|
-
text: JSON.stringify({
|
|
9099
|
-
text,
|
|
9100
|
-
...isTestingIntent ? { cutline_testing_route: buildTestingIntentHintResponse(prompt) } : {},
|
|
9101
|
-
...tokenIntent.detected ? { cutline_token_route: buildTokenLimitIntentHintResponse(prompt, tokenIntent) } : {}
|
|
9102
|
-
})
|
|
9103
|
-
}]
|
|
9104
|
-
};
|
|
9105
|
-
}
|
|
9106
|
-
if (name2 === "get_assurance_manifest") {
|
|
9107
|
-
const { url } = args;
|
|
9108
|
-
await resolveAuthContextFree(args.auth_token);
|
|
9109
|
-
const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || "https://thecutline.ai").replace(/\/$/, "");
|
|
9110
|
-
const assuranceUrl = url || `${siteUrl}/.well-known/cutline-assurance.json`;
|
|
9111
|
-
const response = await fetch(assuranceUrl, {
|
|
9112
|
-
method: "GET",
|
|
9113
|
-
headers: { "Accept": "application/json" }
|
|
9114
|
-
});
|
|
9115
|
-
if (!response.ok) {
|
|
9116
|
-
throw new McpError(ErrorCode.InternalError, `Failed to fetch assurance manifest (${response.status}) from ${assuranceUrl}`);
|
|
9117
|
-
}
|
|
9118
|
-
const manifest = await response.json();
|
|
9119
|
-
return {
|
|
9120
|
-
content: [{
|
|
9121
|
-
type: "text",
|
|
9122
|
-
text: JSON.stringify({
|
|
9123
|
-
source_url: assuranceUrl,
|
|
9124
|
-
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9125
|
-
manifest
|
|
9126
|
-
}, null, 2)
|
|
9127
|
-
}]
|
|
9128
|
-
};
|
|
9129
|
-
}
|
|
9130
|
-
if (name2 === "validate_repo_policy") {
|
|
9131
|
-
const policyArgs = args;
|
|
9132
|
-
if (!policyArgs.project_root) {
|
|
9133
|
-
throw new McpError(ErrorCode.InvalidParams, "project_root is required");
|
|
9134
|
-
}
|
|
9135
|
-
await resolveAuthContextFree(policyArgs.auth_token);
|
|
9136
|
-
const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
|
|
9137
|
-
const resolvedInstallId = policyArgs.install_id || getStoredInstallId({ environment: env });
|
|
9138
|
-
const telemetryAttribution = resolveTelemetryAttribution({
|
|
9139
|
-
providedInstallId: policyArgs.install_id,
|
|
9140
|
-
resolvedInstallId: resolvedInstallId || void 0
|
|
9141
|
-
});
|
|
9142
|
-
const { existsSync, readFileSync } = await import("node:fs");
|
|
9143
|
-
const { resolve, join: join4 } = await import("node:path");
|
|
9144
|
-
const manifestPath = resolve(policyArgs.manifest_path || join4(policyArgs.project_root, "cutline.json"));
|
|
9145
|
-
if (!existsSync(manifestPath)) {
|
|
9146
|
-
await emitPolicyGateEvent({
|
|
9147
|
-
authToken: policyArgs.auth_token,
|
|
9148
|
-
installId: resolvedInstallId || void 0,
|
|
9149
|
-
status: "invalid_policy",
|
|
9150
|
-
policyName: "cutline-policy",
|
|
9151
|
-
failedChecks: 1,
|
|
9152
|
-
unknownChecks: 0,
|
|
9153
|
-
blockingReasonsCount: 1
|
|
9154
|
-
});
|
|
9155
|
-
return {
|
|
9156
|
-
content: [{
|
|
9157
|
-
type: "text",
|
|
9158
|
-
text: JSON.stringify(buildInvalidPolicyResponse({
|
|
9159
|
-
manifestPath,
|
|
9160
|
-
telemetryAttribution
|
|
9161
|
-
}), null, 2)
|
|
9162
|
-
}]
|
|
9163
|
-
};
|
|
9164
|
-
}
|
|
9165
|
-
let manifest;
|
|
9166
|
-
try {
|
|
9167
|
-
manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
9168
|
-
} catch (e) {
|
|
9169
|
-
throw new McpError(ErrorCode.InvalidParams, `Invalid cutline.json: ${e?.message || String(e)}`);
|
|
9170
|
-
}
|
|
9171
|
-
const requirements = buildRepoPolicyRequirements(manifest);
|
|
9172
|
-
const observed = buildRepoPolicyObserved(policyArgs.observed);
|
|
9173
|
-
const verdict = evaluateRepoPolicy(requirements, observed);
|
|
9174
|
-
const failedChecks = verdict.checks.filter((c) => c.status === "fail").length;
|
|
9175
|
-
const unknownChecks = verdict.checks.filter((c) => c.status === "unknown").length;
|
|
9176
|
-
await emitPolicyGateEvent({
|
|
9177
|
-
authToken: policyArgs.auth_token,
|
|
9178
|
-
installId: resolvedInstallId || void 0,
|
|
9179
|
-
status: verdict.status,
|
|
9180
|
-
policyName: manifest?.policy_name || "cutline-policy",
|
|
9181
|
-
failedChecks,
|
|
9182
|
-
unknownChecks,
|
|
9183
|
-
blockingReasonsCount: verdict.blocking_reasons.length
|
|
9184
|
-
});
|
|
9185
|
-
return {
|
|
9186
|
-
content: [{
|
|
9187
|
-
type: "text",
|
|
9188
|
-
text: JSON.stringify(buildPolicyVerdictResponse({
|
|
9189
|
-
verdict,
|
|
9190
|
-
manifestPath,
|
|
9191
|
-
policyName: manifest?.policy_name,
|
|
9192
|
-
schemaVersion: manifest?.schema_version,
|
|
9193
|
-
observedEvidence: policyArgs.observed || {},
|
|
9194
|
-
telemetryAttribution
|
|
9195
|
-
}), null, 2)
|
|
9196
|
-
}]
|
|
9197
|
-
};
|
|
8774
|
+
return { content: [{ type: "text", text: JSON.stringify({ text }) }] };
|
|
9198
8775
|
}
|
|
9199
8776
|
if (name2 === "code_audit") {
|
|
9200
8777
|
const scanArgs = args;
|
|
@@ -9495,56 +9072,9 @@ Competitive threats: ${competitors}` : ""
|
|
|
9495
9072
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
9496
9073
|
}
|
|
9497
9074
|
case "agent_chat": {
|
|
9498
|
-
const
|
|
9499
|
-
const { prompt, wikiMarkdown } = chatArgs;
|
|
9075
|
+
const { prompt, wikiMarkdown } = args;
|
|
9500
9076
|
const text = await cfGenerateChatSuggestion(prompt, wikiMarkdown);
|
|
9501
|
-
|
|
9502
|
-
const tokenIntent = TOKEN_INTENT_HINT_ENABLED ? detectTokenLimitIntentPrompt(prompt) : { detected: false };
|
|
9503
|
-
const env = process.env.CUTLINE_ENV === "staging" ? "staging" : "production";
|
|
9504
|
-
const resolvedInstallId = chatArgs.install_id || getStoredInstallId({ environment: env });
|
|
9505
|
-
if (isTestingIntent) {
|
|
9506
|
-
await emitAgentEvent({
|
|
9507
|
-
authToken: chatArgs.auth_token,
|
|
9508
|
-
installId: resolvedInstallId || void 0,
|
|
9509
|
-
eventName: "agent_test_intent_detected",
|
|
9510
|
-
eventProperties: {
|
|
9511
|
-
tool_name: "agent_chat",
|
|
9512
|
-
route: "cutline_testing_rgr"
|
|
9513
|
-
}
|
|
9514
|
-
});
|
|
9515
|
-
}
|
|
9516
|
-
if (tokenIntent.detected) {
|
|
9517
|
-
await emitAgentEvent({
|
|
9518
|
-
authToken: chatArgs.auth_token,
|
|
9519
|
-
installId: resolvedInstallId || void 0,
|
|
9520
|
-
eventName: "agent_token_limit_intent_detected",
|
|
9521
|
-
eventProperties: {
|
|
9522
|
-
tool_name: "agent_chat",
|
|
9523
|
-
route: "cutline_token_layer_install",
|
|
9524
|
-
query_cluster: tokenIntent.cluster
|
|
9525
|
-
}
|
|
9526
|
-
});
|
|
9527
|
-
await emitAgentEvent({
|
|
9528
|
-
authToken: chatArgs.auth_token,
|
|
9529
|
-
installId: resolvedInstallId || void 0,
|
|
9530
|
-
eventName: "agent_token_layer_install_prompted",
|
|
9531
|
-
eventProperties: {
|
|
9532
|
-
tool_name: "agent_chat",
|
|
9533
|
-
route: "cutline_token_layer_install",
|
|
9534
|
-
query_cluster: tokenIntent.cluster
|
|
9535
|
-
}
|
|
9536
|
-
});
|
|
9537
|
-
}
|
|
9538
|
-
return {
|
|
9539
|
-
content: [{
|
|
9540
|
-
type: "text",
|
|
9541
|
-
text: JSON.stringify({
|
|
9542
|
-
text,
|
|
9543
|
-
...isTestingIntent ? { cutline_testing_route: buildTestingIntentHintResponse(prompt) } : {},
|
|
9544
|
-
...tokenIntent.detected ? { cutline_token_route: buildTokenLimitIntentHintResponse(prompt, tokenIntent) } : {}
|
|
9545
|
-
})
|
|
9546
|
-
}]
|
|
9547
|
-
};
|
|
9077
|
+
return { content: [{ type: "text", text: JSON.stringify({ text }) }] };
|
|
9548
9078
|
}
|
|
9549
9079
|
// Integrations
|
|
9550
9080
|
case "integrations_create_issues": {
|
|
@@ -9902,7 +9432,7 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
9902
9432
|
}
|
|
9903
9433
|
const analysis = analyzeFileContext(fileContext);
|
|
9904
9434
|
let knownEntityNames = [];
|
|
9905
|
-
if (task_description ||
|
|
9435
|
+
if (task_description || scope_entity_name) {
|
|
9906
9436
|
try {
|
|
9907
9437
|
const entities = await getAllEntities(product_id);
|
|
9908
9438
|
knownEntityNames = entities.map((e) => e.name);
|
|
@@ -9911,7 +9441,6 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
9911
9441
|
}
|
|
9912
9442
|
const scopeExpansion = assessScopeExpansionIntent({
|
|
9913
9443
|
task_description,
|
|
9914
|
-
code_snippet,
|
|
9915
9444
|
file_paths,
|
|
9916
9445
|
known_entity_names: knownEntityNames,
|
|
9917
9446
|
hinted_entity_name: scope_entity_name
|
|
@@ -10040,8 +9569,9 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
10040
9569
|
} catch (e) {
|
|
10041
9570
|
}
|
|
10042
9571
|
}
|
|
10043
|
-
|
|
9572
|
+
let autoRgrPlan = void 0;
|
|
10044
9573
|
if (autoPhase === "auto") {
|
|
9574
|
+
autoRgrPlan = planRgrPhases(finalResults);
|
|
10045
9575
|
if (autoRgrPlan.strategy === "phased") {
|
|
10046
9576
|
finalResults = finalResults.filter((c) => {
|
|
10047
9577
|
const phased = filterByPhase([c], "test_spec");
|
|
@@ -11113,11 +10643,6 @@ ${JSON.stringify(metrics, null, 2)}` }
|
|
|
11113
10643
|
...plan,
|
|
11114
10644
|
entity: rgrMatched[0].name,
|
|
11115
10645
|
complexity,
|
|
11116
|
-
auto_execution: {
|
|
11117
|
-
scope_increase_triggers_rgr: true,
|
|
11118
|
-
mode: "pervasive",
|
|
11119
|
-
expected_runner: "local_vitest"
|
|
11120
|
-
},
|
|
11121
10646
|
governance
|
|
11122
10647
|
}, null, 2)
|
|
11123
10648
|
}]
|
|
@@ -11644,7 +11169,7 @@ Meta: ${JSON.stringify({
|
|
|
11644
11169
|
mode: "product",
|
|
11645
11170
|
codeContext: payload.codeContext || null
|
|
11646
11171
|
});
|
|
11647
|
-
runInput.productId =
|
|
11172
|
+
runInput.productId = newJobId;
|
|
11648
11173
|
await updatePremortem(newJobId, { payload: runInput });
|
|
11649
11174
|
return { jobId: newJobId };
|
|
11650
11175
|
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
requirePremiumWithAutoAuth,
|
|
15
15
|
updateExplorationSession,
|
|
16
16
|
validateRequestSize
|
|
17
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
requirePremiumWithAutoAuth,
|
|
22
22
|
validateAuth,
|
|
23
23
|
validateRequestSize
|
|
24
|
-
} from "./chunk-
|
|
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.11.
|
|
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,19 +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' | 'agent_token_limit_intent_detected' | 'agent_token_layer_install_prompted' | 'agent_token_layer_install_completed';
|
|
2
|
-
export declare function registerAgentInstall(input: {
|
|
3
|
-
idToken: string;
|
|
4
|
-
staging?: boolean;
|
|
5
|
-
projectRoot: string;
|
|
6
|
-
sourceSurface: string;
|
|
7
|
-
hostAgent?: string;
|
|
8
|
-
campaign?: string;
|
|
9
|
-
referrerUrl?: string;
|
|
10
|
-
metadata?: Record<string, unknown>;
|
|
11
|
-
}): Promise<string | null>;
|
|
12
|
-
export declare function trackAgentEvent(input: {
|
|
13
|
-
idToken: string;
|
|
14
|
-
installId: string;
|
|
15
|
-
eventName: AgentEventName;
|
|
16
|
-
eventProperties?: Record<string, unknown>;
|
|
17
|
-
staging?: boolean;
|
|
18
|
-
}): Promise<void>;
|
|
19
|
-
export {};
|
|
@@ -1,67 +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
|
-
...(input.campaign ? { campaign: input.campaign } : {}),
|
|
32
|
-
...(input.referrerUrl ? { referrer_url: input.referrerUrl } : {}),
|
|
33
|
-
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
34
|
-
}),
|
|
35
|
-
});
|
|
36
|
-
if (!response.ok)
|
|
37
|
-
return null;
|
|
38
|
-
const payload = await response.json();
|
|
39
|
-
if (!payload.install_id)
|
|
40
|
-
return null;
|
|
41
|
-
saveConfig({ [key]: payload.install_id });
|
|
42
|
-
return payload.install_id;
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
export async function trackAgentEvent(input) {
|
|
49
|
-
const { BASE_URL } = getConfig({ staging: input.staging });
|
|
50
|
-
try {
|
|
51
|
-
await fetch(`${BASE_URL}/api/agent/event`, {
|
|
52
|
-
method: 'POST',
|
|
53
|
-
headers: {
|
|
54
|
-
'Content-Type': 'application/json',
|
|
55
|
-
Authorization: `Bearer ${input.idToken}`,
|
|
56
|
-
},
|
|
57
|
-
body: JSON.stringify({
|
|
58
|
-
install_id: input.installId,
|
|
59
|
-
event_name: input.eventName,
|
|
60
|
-
event_properties: input.eventProperties || {},
|
|
61
|
-
}),
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Best effort telemetry; never block CLI command success.
|
|
66
|
-
}
|
|
67
|
-
}
|