@productbrain/mcp 0.0.1-beta.156 → 0.0.1-beta.158
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/dist/{chunk-X3S5UTTZ.js → chunk-KGGSEIZ3.js} +86 -1
- package/dist/chunk-KGGSEIZ3.js.map +1 -0
- package/dist/{chunk-GMSHWOXT.js → chunk-ZZHGJKLF.js} +235 -34
- package/dist/chunk-ZZHGJKLF.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +36 -2
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{setup-GUKWCWK6.js → setup-WAJMKE5Y.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-GMSHWOXT.js.map +0 -1
- package/dist/chunk-X3S5UTTZ.js.map +0 -1
- /package/dist/{setup-GUKWCWK6.js.map → setup-WAJMKE5Y.js.map} +0 -0
|
@@ -257,6 +257,86 @@ function trackFieldQualityWarning(workspaceId, props) {
|
|
|
257
257
|
} catch {
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
+
function trackSessionCaptureRate(workspaceId, props) {
|
|
261
|
+
if (!client) return;
|
|
262
|
+
try {
|
|
263
|
+
client.capture({
|
|
264
|
+
distinctId,
|
|
265
|
+
event: "session_capture_rate",
|
|
266
|
+
properties: {
|
|
267
|
+
...props,
|
|
268
|
+
workspace_id: workspaceId,
|
|
269
|
+
source_system: "mcp-server",
|
|
270
|
+
$groups: { workspace: workspaceId }
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function trackZeroCaptureAuditFired(workspaceId, props) {
|
|
277
|
+
if (!client) return;
|
|
278
|
+
try {
|
|
279
|
+
client.capture({
|
|
280
|
+
distinctId,
|
|
281
|
+
event: "zero_capture_audit_fired",
|
|
282
|
+
properties: {
|
|
283
|
+
...props,
|
|
284
|
+
workspace_id: workspaceId,
|
|
285
|
+
source_system: "mcp-server",
|
|
286
|
+
$groups: { workspace: workspaceId }
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function trackCaptureContractMiss(workspaceId, props) {
|
|
293
|
+
if (!client) return;
|
|
294
|
+
try {
|
|
295
|
+
client.capture({
|
|
296
|
+
distinctId,
|
|
297
|
+
event: "capture_contract_miss",
|
|
298
|
+
properties: {
|
|
299
|
+
...props,
|
|
300
|
+
workspace_id: workspaceId,
|
|
301
|
+
source_system: "mcp-server",
|
|
302
|
+
$groups: { workspace: workspaceId }
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function trackWriteBackHintServed(workspaceId, props) {
|
|
309
|
+
if (!client) return;
|
|
310
|
+
try {
|
|
311
|
+
client.capture({
|
|
312
|
+
distinctId,
|
|
313
|
+
event: "write_back_hint_served",
|
|
314
|
+
properties: {
|
|
315
|
+
...props,
|
|
316
|
+
workspace_id: workspaceId,
|
|
317
|
+
source_system: "mcp-server",
|
|
318
|
+
$groups: { workspace: workspaceId }
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function trackCommitErrorByCode(workspaceId, props) {
|
|
325
|
+
if (!client) return;
|
|
326
|
+
try {
|
|
327
|
+
client.capture({
|
|
328
|
+
distinctId,
|
|
329
|
+
event: "commit_error_by_code",
|
|
330
|
+
properties: {
|
|
331
|
+
...props,
|
|
332
|
+
workspace_id: workspaceId,
|
|
333
|
+
source_system: "mcp-server",
|
|
334
|
+
$groups: { workspace: workspaceId }
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
260
340
|
function getPostHogClient() {
|
|
261
341
|
return client;
|
|
262
342
|
}
|
|
@@ -354,10 +434,15 @@ export {
|
|
|
354
434
|
trackCollectionClassified,
|
|
355
435
|
trackFieldGuidanceApplied,
|
|
356
436
|
trackFieldQualityWarning,
|
|
437
|
+
trackSessionCaptureRate,
|
|
438
|
+
trackZeroCaptureAuditFired,
|
|
439
|
+
trackCaptureContractMiss,
|
|
440
|
+
trackWriteBackHintServed,
|
|
441
|
+
trackCommitErrorByCode,
|
|
357
442
|
getPostHogClient,
|
|
358
443
|
shutdownAnalytics,
|
|
359
444
|
MCP_NPX_PACKAGE,
|
|
360
445
|
resolveClient,
|
|
361
446
|
writeClientConfig
|
|
362
447
|
};
|
|
363
|
-
//# sourceMappingURL=chunk-
|
|
448
|
+
//# sourceMappingURL=chunk-KGGSEIZ3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/analytics.ts","../src/cli/config-writer.ts"],"sourcesContent":["/**\n * PostHog analytics for SynergyOS maintainers — tracks MCP usage (sessions, tool calls).\n * Not user-facing. Key is injected at build time via SYNERGYOS_POSTHOG_KEY.\n * Override with POSTHOG_MCP_KEY for self-hosted deployments.\n */\n\nimport { userInfo } from \"node:os\";\nimport { PostHog } from \"posthog-node\";\n\nlet client: PostHog | null = null;\nlet distinctId = \"anonymous\";\n\nconst POSTHOG_HOST = \"https://eu.i.posthog.com\";\n\n/** Injected at build time: SYNERGYOS_POSTHOG_KEY env when running `npm run build`/publish. */\ndeclare const __SYNERGYOS_POSTHOG_KEY__: string;\n\n/** Only write to stderr when MCP_DEBUG=1 for quieter default DX. */\nfunction log(msg: string): void {\n if (process.env.MCP_DEBUG === \"1\") {\n process.stderr.write(msg);\n }\n}\n\nfunction getBuildTimeKey(): string {\n try {\n return __SYNERGYOS_POSTHOG_KEY__;\n } catch {\n // Not replaced by bundler (e.g. running via tsx in tests) — treat as absent.\n return \"\";\n }\n}\n\nexport function initAnalytics(): void {\n const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();\n if (!apiKey) {\n log(\"[MCP-ANALYTICS] No PostHog key — tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\\n\");\n return;\n }\n\n client = new PostHog(apiKey, {\n host: POSTHOG_HOST,\n flushAt: 1,\n flushInterval: 5000,\n featureFlagsPollingInterval: 30_000,\n });\n distinctId = process.env.MCP_USER_ID || fallbackDistinctId();\n\n log(`[MCP-ANALYTICS] Initialized — host=${POSTHOG_HOST} distinctId=${distinctId}\\n`);\n}\n\nfunction fallbackDistinctId(): string {\n try {\n return userInfo().username;\n } catch {\n return `os-${process.pid}`;\n }\n}\n\nexport function trackSessionStarted(\n workspaceId: string,\n serverVersion: string,\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_session_started\",\n properties: {\n workspace_id: workspaceId,\n server_version: serverVersion,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackToolCall(\n fn: string,\n status: \"ok\" | \"error\",\n durationMs: number,\n workspaceId: string,\n errorMsg?: string,\n): void {\n const properties: Record<string, unknown> = {\n tool: fn,\n status,\n duration_ms: durationMs,\n workspace_id: workspaceId,\n source: \"mcp-server\",\n $groups: { workspace: workspaceId },\n };\n if (errorMsg) properties.error = errorMsg;\n\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_tool_called\",\n properties,\n });\n}\n\nexport function trackSetupStarted(): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_started\",\n properties: {\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackSetupCompleted(\n chosenClient: string,\n outcome: \"config_written\" | \"config_existed\" | \"snippet_shown\" | \"write_error\",\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"mcp_setup_completed\",\n properties: {\n client: chosenClient,\n outcome,\n source: \"mcp-server\",\n platform: process.platform,\n },\n });\n}\n\nexport function trackQualityVerdict(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n context: string;\n passed: boolean;\n source: string;\n criteria_total: number;\n criteria_failed: number;\n llm_scheduled: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_generated\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport function trackQualityCheck(\n workspaceId: string,\n props: {\n entry_id: string;\n entry_type: string;\n tier: string;\n passed: boolean;\n source: string;\n llm_status?: string;\n llm_duration_ms?: number;\n llm_error?: string;\n has_roger_martin: boolean;\n },\n): void {\n if (!client) return;\n client.capture({\n distinctId,\n event: \"quality_verdict_checked\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n}\n\nexport type ClassifierReasonCategory =\n | \"auto-routed\"\n | \"low-confidence\"\n | \"ambiguous\"\n | \"non-provisioned\";\n\ntype CaptureClassifierTelemetryProps = {\n predicted_collection: string;\n confidence: number;\n auto_routed: boolean;\n reason_category: ClassifierReasonCategory;\n explicit_collection_provided: boolean;\n};\n\nfunction trackCaptureClassifierEvent(\n event: \"mcp_capture_classifier_evaluated\" | \"mcp_capture_classifier_auto_routed\" | \"mcp_capture_classifier_fallback\",\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event,\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics are advisory and must never break capture flow.\n }\n}\n\nexport function trackCaptureClassifierEvaluated(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_evaluated\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierAutoRouted(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_auto_routed\", workspaceId, props);\n}\n\nexport function trackCaptureClassifierFallback(\n workspaceId: string,\n props: CaptureClassifierTelemetryProps,\n): void {\n trackCaptureClassifierEvent(\"mcp_capture_classifier_fallback\", workspaceId, props);\n}\n\n/** GLO-26 / TEN-156: every SSOT commit for PostHog funnels (split auto vs manual). */\nexport function trackChainEntryCommitted(\n workspaceId: string,\n props: {\n entry_id: string;\n collection?: string;\n commit_method: \"auto\" | \"manual\";\n surface:\n | \"mcp_commit_tool\"\n | \"mcp_capture\"\n | \"mcp_wrapup\";\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"chain_entry_committed\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n ...props,\n },\n });\n } catch {\n // Analytics must never break the tool path.\n }\n}\n\nexport function trackKnowledgeGap(\n workspaceId: string,\n props: {\n query: string;\n tool: string;\n action: string;\n gap_type: \"search_zero\" | \"context_task_empty\" | \"context_entry_isolated\" | \"context_graph_empty\";\n collection_scope?: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"knowledge_gap_detected\",\n properties: {\n ...props,\n query: props.query.slice(0, 200),\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the tool response path.\n }\n}\n\n// ── BET-272 S6 / STD-155: Capture intelligence observability ────────────────\n\n/** Fires when formative quality hints are returned at capture time. */\nexport function trackCaptureQualityHints(\n workspaceId: string,\n props: {\n collection: string;\n hint_count: number;\n hint_fields: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_quality_hints\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n/** Fires when relation suggestions are returned at capture time. */\nexport function trackCaptureRelationSuggestions(\n workspaceId: string,\n props: {\n collection: string;\n suggestion_count: number;\n relation_types: string[];\n avg_confidence: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"mcp_capture_relation_suggestions\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break capture flow.\n }\n}\n\n// ── BET-288 S0: Collection classification confusion matrix telemetry ────────\n\n/**\n * Fires on every collection classification (auto-routed, fallback, explicit-provided).\n * Captures the full confusion signal: predicted collection, runner-up, thinkingLayer,\n * and confidence scores. This is the baseline measurement for BET-288 algorithm changes.\n */\nexport function trackCollectionClassified(\n workspaceId: string,\n props: {\n collection_slug: string;\n thinking_layer: string | null;\n confidence: number;\n classified_by: \"llm\" | \"heuristic\";\n confidence_tier: \"high\" | \"medium\" | \"low\";\n alternative_slug: string | null;\n alternative_confidence: number | null;\n explicit_collection_provided: boolean;\n auto_routed: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"collection_classified\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n// ── BET-289 S5: Field-level writing guidance telemetry ────────────────────────\n\n/** Fires when field guidance is injected into a capture prompt or response. */\nexport function trackFieldGuidanceApplied(\n workspaceId: string,\n props: {\n collection: string;\n guided_field_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_guidance_applied\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the capture flow.\n }\n}\n\n/** Fires when commit-time heuristic detects field guidance violations. */\nexport function trackFieldQualityWarning(\n workspaceId: string,\n props: {\n warning_count: number;\n warning_types: string[];\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_warning\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n/** Fires when skipGuidanceCheck is used to bypass guidance validation. */\nexport function trackFieldQualityOverride(\n workspaceId: string,\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"field_quality_override\",\n properties: {\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit flow.\n }\n}\n\n// ── WP-306 S3: Write-Back Measurement (capture rate insights) ──────────────\n\n/** Fires on session close to track session capture rate. */\nexport function trackSessionCaptureRate(\n workspaceId: string,\n props: {\n entries_created: number;\n entries_modified: number;\n relations_created: number;\n had_captures: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"session_capture_rate\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break session closure.\n }\n}\n\n/** Fires when captureAudit is triggered (activity but no captures). */\nexport function trackZeroCaptureAuditFired(\n workspaceId: string,\n props: {\n suggestion_count: number;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"zero_capture_audit_fired\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break wrapup flow.\n }\n}\n\n// ── WP-316 S1b: Capture contract observability ──────────────────────────────\n\n/** Fires when a capture-contract resource request references an unknown collection slug. */\nexport function trackCaptureContractMiss(\n workspaceId: string,\n props: {\n slug: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"capture_contract_miss\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the resource response path.\n }\n}\n\n/** Fires when writeBackHints are included in an orient response. */\nexport function trackWriteBackHintServed(\n workspaceId: string,\n props: {\n hint_count: number;\n has_task: boolean;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"write_back_hint_served\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break orient flow.\n }\n}\n\n/**\n * WP-316 S1a: Fires when commit-entry fails with a structured error code.\n * Enables observability on which validation errors block commits most often.\n */\nexport function trackCommitErrorByCode(\n workspaceId: string,\n props: {\n error_code: string;\n missing_field_count: number;\n field_error_count: number;\n entry_id: string;\n },\n): void {\n if (!client) return;\n try {\n client.capture({\n distinctId,\n event: \"commit_error_by_code\",\n properties: {\n ...props,\n workspace_id: workspaceId,\n source_system: \"mcp-server\",\n $groups: { workspace: workspaceId },\n },\n });\n } catch {\n // Analytics must never break the commit error path.\n }\n}\n\nexport function getPostHogClient(): PostHog | null {\n return client;\n}\n\nexport async function shutdownAnalytics(): Promise<void> {\n await client?.shutdown();\n}\n","/**\n * Multi-client MCP config detection and writer.\n *\n * Supports:\n * - Cursor: .cursor/mcp.json in cwd (project-level)\n * - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)\n * %APPDATA%/Claude/claude_desktop_config.json (Windows)\n *\n * The writer reads existing config, merges the new server entry (never\n * overwrites existing entries), and writes back. Falls back to printing\n * a snippet for unsupported OS or unknown formats.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport interface McpClientInfo {\n name: string;\n configPath: string;\n}\n\nconst SERVER_ENTRY_KEY = \"Product Brain\";\nconst LEGACY_ENTRY_KEY = \"productbrain\";\n\n/**\n * Canonical npx package specifier. Update here when exiting beta.\n * Frontend mirror: src/lib/constants/mcp.ts\n * Business rule: BR-84 (Chain)\n */\nexport const MCP_NPX_PACKAGE = \"@productbrain/mcp@beta\";\n\nfunction buildServerEntry(apiKey: string) {\n return {\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n };\n}\n\n// ── Detection ───────────────────────────────────────────────────────────\n\nfunction getCursorConfigPath(): string {\n return join(process.cwd(), \".cursor\", \"mcp.json\");\n}\n\nfunction getClaudeDesktopConfigPath(): string | null {\n const os = platform();\n if (os === \"darwin\") {\n return join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n }\n if (os === \"win32\") {\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n return join(appData, \"Claude\", \"claude_desktop_config.json\");\n }\n // Linux: no official Claude Desktop location yet\n return null;\n}\n\nexport function resolveClient(name: \"Cursor\" | \"Claude Desktop\"): McpClientInfo | null {\n if (name === \"Cursor\") {\n return { name, configPath: getCursorConfigPath() };\n }\n const configPath = getClaudeDesktopConfigPath();\n return configPath ? { name, configPath } : null;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(path: string): Record<string, any> {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\n/**\n * Write or merge the Product Brain server entry into a client config file.\n * Migrates legacy \"productbrain\" key to \"Product Brain\" when present.\n * Returns true if the config was written, false if already present.\n */\nexport async function writeClientConfig(\n client: McpClientInfo,\n apiKey: string,\n): Promise<boolean> {\n const config = readJsonSafe(client.configPath);\n\n const serversKey = \"mcpServers\";\n if (!config[serversKey]) config[serversKey] = {};\n\n // Migrate legacy \"productbrain\" key or update existing Product Brain with new API key\n if (config[serversKey][LEGACY_ENTRY_KEY]) {\n const legacy = config[serversKey][LEGACY_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = {\n ...buildServerEntry(apiKey),\n env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey },\n };\n delete config[serversKey][LEGACY_ENTRY_KEY];\n } else {\n const existing = config[serversKey][SERVER_ENTRY_KEY];\n config[serversKey][SERVER_ENTRY_KEY] = existing\n ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } }\n : buildServerEntry(apiKey);\n }\n\n const dir = dirname(client.configPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(client.configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return true;\n}\n"],"mappings":"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","names":["client"]}
|
|
@@ -3,17 +3,22 @@ import {
|
|
|
3
3
|
trackCaptureClassifierAutoRouted,
|
|
4
4
|
trackCaptureClassifierEvaluated,
|
|
5
5
|
trackCaptureClassifierFallback,
|
|
6
|
+
trackCaptureContractMiss,
|
|
6
7
|
trackCaptureQualityHints,
|
|
7
8
|
trackCaptureRelationSuggestions,
|
|
8
9
|
trackChainEntryCommitted,
|
|
9
10
|
trackCollectionClassified,
|
|
11
|
+
trackCommitErrorByCode,
|
|
10
12
|
trackFieldGuidanceApplied,
|
|
11
13
|
trackFieldQualityWarning,
|
|
12
14
|
trackKnowledgeGap,
|
|
13
15
|
trackQualityCheck,
|
|
14
16
|
trackQualityVerdict,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
trackSessionCaptureRate,
|
|
18
|
+
trackToolCall,
|
|
19
|
+
trackWriteBackHintServed,
|
|
20
|
+
trackZeroCaptureAuditFired
|
|
21
|
+
} from "./chunk-KGGSEIZ3.js";
|
|
17
22
|
|
|
18
23
|
// src/auth.ts
|
|
19
24
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -83,11 +88,17 @@ var DEFAULT_CLOUD_URL = "https://gateway.productbrain.io";
|
|
|
83
88
|
var McpCallError = class extends Error {
|
|
84
89
|
status;
|
|
85
90
|
code;
|
|
86
|
-
|
|
91
|
+
/** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */
|
|
92
|
+
missingRequiredFields;
|
|
93
|
+
/** WP-316 S1a: Structured commit validation — field-level data errors. */
|
|
94
|
+
fieldErrors;
|
|
95
|
+
constructor(message, status, code, missingRequiredFields, fieldErrors) {
|
|
87
96
|
super(message);
|
|
88
97
|
this.name = "McpCallError";
|
|
89
98
|
this.status = status;
|
|
90
99
|
this.code = code;
|
|
100
|
+
this.missingRequiredFields = missingRequiredFields;
|
|
101
|
+
this.fieldErrors = fieldErrors;
|
|
91
102
|
}
|
|
92
103
|
};
|
|
93
104
|
var CACHE_TTL_MS = 6e4;
|
|
@@ -324,7 +335,13 @@ async function mcpCall(fn, args = {}) {
|
|
|
324
335
|
if (!res.ok || json.error) {
|
|
325
336
|
const msg = json.error ?? "unknown error";
|
|
326
337
|
audit(fn, "error", Date.now() - start, json.code ? `${msg} [${json.code}]` : msg);
|
|
327
|
-
throw new McpCallError(
|
|
338
|
+
throw new McpCallError(
|
|
339
|
+
`MCP call "${fn}" failed (${res.status}): ${msg}`,
|
|
340
|
+
res.status,
|
|
341
|
+
json.code,
|
|
342
|
+
Array.isArray(json.missingRequiredFields) ? json.missingRequiredFields : void 0,
|
|
343
|
+
Array.isArray(json.fieldErrors) ? json.fieldErrors : void 0
|
|
344
|
+
);
|
|
328
345
|
}
|
|
329
346
|
audit(fn, "ok", Date.now() - start);
|
|
330
347
|
const data = json.data;
|
|
@@ -748,7 +765,13 @@ function asMcpCallError(err) {
|
|
|
748
765
|
const code = err.code;
|
|
749
766
|
const match = MCP_CALL_ERROR_RE.exec(err.message);
|
|
750
767
|
if (match) {
|
|
751
|
-
|
|
768
|
+
const e = err;
|
|
769
|
+
return {
|
|
770
|
+
code,
|
|
771
|
+
cleanMessage: match[1],
|
|
772
|
+
missingRequiredFields: Array.isArray(e.missingRequiredFields) ? e.missingRequiredFields : void 0,
|
|
773
|
+
fieldErrors: Array.isArray(e.fieldErrors) ? e.fieldErrors : void 0
|
|
774
|
+
};
|
|
752
775
|
}
|
|
753
776
|
}
|
|
754
777
|
return void 0;
|
|
@@ -815,9 +838,19 @@ function classifyError(err) {
|
|
|
815
838
|
}
|
|
816
839
|
const mcpErr = asMcpCallError(err);
|
|
817
840
|
if (mcpErr) {
|
|
818
|
-
const { code: convexCode, cleanMessage } = mcpErr;
|
|
841
|
+
const { code: convexCode, cleanMessage, missingRequiredFields, fieldErrors } = mcpErr;
|
|
819
842
|
if (convexCode === "VALIDATION_FAILED") {
|
|
820
|
-
|
|
843
|
+
const hasDiagnostics = missingRequiredFields?.length || fieldErrors?.length;
|
|
844
|
+
return {
|
|
845
|
+
code: "VALIDATION_ERROR",
|
|
846
|
+
message: cleanMessage,
|
|
847
|
+
...hasDiagnostics ? {
|
|
848
|
+
diagnostics: {
|
|
849
|
+
...missingRequiredFields?.length ? { missingRequiredFields } : {},
|
|
850
|
+
...fieldErrors?.length ? { fieldErrors } : {}
|
|
851
|
+
}
|
|
852
|
+
} : {}
|
|
853
|
+
};
|
|
821
854
|
}
|
|
822
855
|
if (convexCode === "NOT_FOUND") {
|
|
823
856
|
return {
|
|
@@ -976,7 +1009,9 @@ function withEnvelope(handler) {
|
|
|
976
1009
|
classified.code,
|
|
977
1010
|
classified.message,
|
|
978
1011
|
classified.recovery,
|
|
979
|
-
classified.availableActions
|
|
1012
|
+
classified.availableActions,
|
|
1013
|
+
// WP-316 S1a: pass structured diagnostics (e.g. missingRequiredFields, fieldErrors)
|
|
1014
|
+
classified.diagnostics
|
|
980
1015
|
),
|
|
981
1016
|
_meta: { durationMs }
|
|
982
1017
|
};
|
|
@@ -2803,6 +2838,16 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2803
2838
|
type: f.type,
|
|
2804
2839
|
...f.required && { required: true }
|
|
2805
2840
|
}));
|
|
2841
|
+
const captureContractForEnvelope = {
|
|
2842
|
+
requiredFields: (col.fields ?? []).filter((f) => f.required === true).map((f) => f.key),
|
|
2843
|
+
allFields: (col.fields ?? []).map((f) => ({
|
|
2844
|
+
key: f.key,
|
|
2845
|
+
required: f.required === true,
|
|
2846
|
+
type: f.type,
|
|
2847
|
+
...f.helpText ? { helpText: f.helpText } : {}
|
|
2848
|
+
})),
|
|
2849
|
+
...col.captureExpectation ? { captureExpectation: col.captureExpectation } : {}
|
|
2850
|
+
};
|
|
2806
2851
|
const totalMs = Date.now() - timingStart;
|
|
2807
2852
|
const timing = {
|
|
2808
2853
|
classifyMs: tAfterClassify - timingStart,
|
|
@@ -2840,6 +2885,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2840
2885
|
},
|
|
2841
2886
|
next
|
|
2842
2887
|
),
|
|
2888
|
+
// WP-316 S1a: Populate contract field so agents know required fields upfront.
|
|
2889
|
+
contract: captureContractForEnvelope,
|
|
2843
2890
|
_meta: { timing }
|
|
2844
2891
|
}
|
|
2845
2892
|
};
|
|
@@ -3849,11 +3896,30 @@ ${formatted}` }],
|
|
|
3849
3896
|
});
|
|
3850
3897
|
} catch {
|
|
3851
3898
|
}
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3899
|
+
let result;
|
|
3900
|
+
try {
|
|
3901
|
+
result = await mcpMutation("chain.commitEntry", {
|
|
3902
|
+
entryId,
|
|
3903
|
+
author: getAgentSessionId() ? `agent:${getAgentSessionId()}` : void 0,
|
|
3904
|
+
sessionId: getAgentSessionId() ?? void 0
|
|
3905
|
+
});
|
|
3906
|
+
} catch (commitErr) {
|
|
3907
|
+
const errCode = commitErr?.code;
|
|
3908
|
+
if (errCode === "VALIDATION_FAILED") {
|
|
3909
|
+
try {
|
|
3910
|
+
const wsCtx2 = await getWorkspaceContext();
|
|
3911
|
+
const e = commitErr;
|
|
3912
|
+
trackCommitErrorByCode(wsCtx2.workspaceId, {
|
|
3913
|
+
error_code: errCode,
|
|
3914
|
+
missing_field_count: e.missingRequiredFields?.length ?? 0,
|
|
3915
|
+
field_error_count: e.fieldErrors?.length ?? 0,
|
|
3916
|
+
entry_id: entryId
|
|
3917
|
+
});
|
|
3918
|
+
} catch {
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
throw commitErr;
|
|
3922
|
+
}
|
|
3857
3923
|
const docId = result?._id ?? entry._id;
|
|
3858
3924
|
const wsCtx = await getWorkspaceContext();
|
|
3859
3925
|
const isProposal = result?.status === "proposal_created";
|
|
@@ -3942,6 +4008,17 @@ ${formatted}` }],
|
|
|
3942
4008
|
lines.push(` \u2192 ${epistemic.action}`);
|
|
3943
4009
|
}
|
|
3944
4010
|
}
|
|
4011
|
+
let captureContract;
|
|
4012
|
+
try {
|
|
4013
|
+
const collSlug = entry.collectionSlug ?? entry.collection?.slug;
|
|
4014
|
+
if (collSlug) {
|
|
4015
|
+
captureContract = await mcpQuery(
|
|
4016
|
+
"chain.getCaptureContract",
|
|
4017
|
+
{ collectionSlug: collSlug }
|
|
4018
|
+
) ?? void 0;
|
|
4019
|
+
}
|
|
4020
|
+
} catch {
|
|
4021
|
+
}
|
|
3945
4022
|
const next = isProposal ? [
|
|
3946
4023
|
{ tool: "entries", description: "View entry", parameters: { action: "get", entryId } }
|
|
3947
4024
|
] : [
|
|
@@ -3949,21 +4026,25 @@ ${formatted}` }],
|
|
|
3949
4026
|
{ tool: "entries", description: "View committed entry", parameters: { action: "get", entryId } }
|
|
3950
4027
|
];
|
|
3951
4028
|
const summary = isProposal ? `Proposal created for ${entryId} (${entry.name}). Awaiting consent.` : `Committed ${entryId} (${entry.name}) to the Chain.`;
|
|
4029
|
+
const commitSuccessEnvelope = success(
|
|
4030
|
+
summary,
|
|
4031
|
+
{
|
|
4032
|
+
entryId,
|
|
4033
|
+
name: entry.name,
|
|
4034
|
+
outcome: isProposal ? "proposal_created" : "committed",
|
|
4035
|
+
qualityVerdict: coachingResult?.verdict ?? void 0,
|
|
4036
|
+
source: coachingResult?.source ?? void 0,
|
|
4037
|
+
contradictions: advisoryWarnings.length,
|
|
4038
|
+
...epistemic ? { epistemicStatus: epistemic } : {}
|
|
4039
|
+
},
|
|
4040
|
+
next
|
|
4041
|
+
);
|
|
4042
|
+
if (captureContract) {
|
|
4043
|
+
commitSuccessEnvelope.contract = captureContract;
|
|
4044
|
+
}
|
|
3952
4045
|
return {
|
|
3953
4046
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
3954
|
-
structuredContent:
|
|
3955
|
-
summary,
|
|
3956
|
-
{
|
|
3957
|
-
entryId,
|
|
3958
|
-
name: entry.name,
|
|
3959
|
-
outcome: isProposal ? "proposal_created" : "committed",
|
|
3960
|
-
qualityVerdict: coachingResult?.verdict ?? void 0,
|
|
3961
|
-
source: coachingResult?.source ?? void 0,
|
|
3962
|
-
contradictions: advisoryWarnings.length,
|
|
3963
|
-
...epistemic ? { epistemicStatus: epistemic } : {}
|
|
3964
|
-
},
|
|
3965
|
-
next
|
|
3966
|
-
)
|
|
4047
|
+
structuredContent: commitSuccessEnvelope
|
|
3967
4048
|
};
|
|
3968
4049
|
})
|
|
3969
4050
|
);
|
|
@@ -7260,6 +7341,30 @@ async function runWrapupReview() {
|
|
|
7260
7341
|
lines.push("- **Commit all:** call `session-wrapup` with action `commit-all`");
|
|
7261
7342
|
lines.push("- **Skip:** call `session action=close` \u2014 drafts remain for next session's orient recovery.");
|
|
7262
7343
|
}
|
|
7344
|
+
if (data.captureAudit) {
|
|
7345
|
+
lines.push("");
|
|
7346
|
+
lines.push("### Capture Audit");
|
|
7347
|
+
lines.push("");
|
|
7348
|
+
lines.push(`_${data.captureAudit.message}_`);
|
|
7349
|
+
if (data.captureAudit.suggestions.length > 0) {
|
|
7350
|
+
lines.push("");
|
|
7351
|
+
lines.push("**What to consider capturing:**");
|
|
7352
|
+
for (const s of data.captureAudit.suggestions) {
|
|
7353
|
+
lines.push(`- **${s.collectionSlug}**: ${s.hint}`);
|
|
7354
|
+
}
|
|
7355
|
+
}
|
|
7356
|
+
lines.push("");
|
|
7357
|
+
}
|
|
7358
|
+
lines.push("");
|
|
7359
|
+
lines.push(`> **${data.closeNudge}**`);
|
|
7360
|
+
if (data.captureAudit) {
|
|
7361
|
+
const wsCtx = await getWorkspaceContext();
|
|
7362
|
+
if (wsCtx) {
|
|
7363
|
+
trackZeroCaptureAuditFired(wsCtx.workspaceId, {
|
|
7364
|
+
suggestion_count: data.captureAudit.suggestions.length
|
|
7365
|
+
});
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7263
7368
|
const gapCount = getSessionGaps().length;
|
|
7264
7369
|
return { text: lines.join("\n"), data, suggestions, gapCount, coherenceVerdict, persistentGaps };
|
|
7265
7370
|
}
|
|
@@ -7583,13 +7688,22 @@ async function handleClose() {
|
|
|
7583
7688
|
}
|
|
7584
7689
|
await closeAgentSession();
|
|
7585
7690
|
clearSessionGaps();
|
|
7691
|
+
const created = session?.entriesCreated?.length ?? 0;
|
|
7692
|
+
const modified = session?.entriesModified?.length ?? 0;
|
|
7693
|
+
const relations = session?.relationsCreated ?? 0;
|
|
7694
|
+
const wsCtx = await getWorkspaceContext();
|
|
7695
|
+
if (wsCtx) {
|
|
7696
|
+
trackSessionCaptureRate(wsCtx.workspaceId, {
|
|
7697
|
+
entries_created: created,
|
|
7698
|
+
entries_modified: modified,
|
|
7699
|
+
relations_created: relations,
|
|
7700
|
+
had_captures: created > 0
|
|
7701
|
+
});
|
|
7702
|
+
}
|
|
7586
7703
|
const lines = [
|
|
7587
7704
|
`Session ${sessionId} closed.`,
|
|
7588
7705
|
""
|
|
7589
7706
|
];
|
|
7590
|
-
const created = session?.entriesCreated?.length ?? 0;
|
|
7591
|
-
const modified = session?.entriesModified?.length ?? 0;
|
|
7592
|
-
const relations = session?.relationsCreated ?? 0;
|
|
7593
7707
|
const gates = session?.gateFailures ?? 0;
|
|
7594
7708
|
const warnings = session?.contradictionWarnings ?? 0;
|
|
7595
7709
|
if (session) {
|
|
@@ -8370,7 +8484,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
|
|
|
8370
8484
|
|
|
8371
8485
|
## Your Behavior
|
|
8372
8486
|
|
|
8373
|
-
1. **Tool-heavy, not chat-heavy.** Each round calls specific tools: orient, quality, verify, chain-review, context action=gather, review-against-rules. Use Context7 (
|
|
8487
|
+
1. **Tool-heavy, not chat-heavy.** Each round calls specific tools: orient, quality, verify, chain-review, context action=gather, review-against-rules. Use Context7 **CLI** (\`npx ctx7@latest library\` then \`docs\`) for relevant testing and framework docs \u2014 not for Chain truth. Do not use Context7 MCP.
|
|
8374
8488
|
2. **Spawn sub-agents** for parallel review: (a) code/architecture review, (b) test honesty (did we edit tests to pass or do they validate real behavior?). Max 2 agents in parallel, 30s budget each. **Constrain each sub-agent to the stated review scope only.**
|
|
8375
8489
|
3. **Fix-as-you-go (FEAT-172).** When you find a HIGH or MEDIUM issue: fix it immediately, verify (lints, types), then proceed. The user sees clean work, not a list of issues. Only defer if estimated >5 min \u2014 capture deferred items as tensions. If user says "skip", capture as tension and continue.
|
|
8376
8490
|
4. **Chain is SSOT.** Call orient or start first. Use entries action=search and context action=gather to load relevant BETs, DECs, BRs. If findings conflict with Chain entries, report the conflict and cite the entry ID.
|
|
@@ -8387,7 +8501,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
|
|
|
8387
8501
|
- \`verify\` \u2014 glossary code mappings, cross-references vs codebase
|
|
8388
8502
|
- \`review-against-rules\` (domain) \u2014 business rule compliance
|
|
8389
8503
|
- \`context action=gather task="implementation review for [BET-ID]"\` \u2014 related knowledge
|
|
8390
|
-
- Context7
|
|
8504
|
+
- Context7 CLI \u2014 \`npx ctx7@latest library\` then \`npx ctx7@latest docs\` for framework/testing docs (not Chain truth)
|
|
8391
8505
|
- \`session-wrapup\` \u2014 before close
|
|
8392
8506
|
|
|
8393
8507
|
## Sub-Agent Classification (for test failures)
|
|
@@ -8439,7 +8553,7 @@ This block is the final deliverable of the review. Everything else is supporting
|
|
|
8439
8553
|
label: "Code & Test Honesty",
|
|
8440
8554
|
type: "open",
|
|
8441
8555
|
instruction: "Spawn sub-agents to review implementation and tests for the stated scope only. Fix every HIGH/MEDIUM finding before proceeding. Did we meet high standards? Are tests validating real behavior or did we edit them just to pass?",
|
|
8442
|
-
facilitatorGuidance: "Restrict all review to the scope stated in Round 01 (by default, work in this conversation only). Spawn 1\u20132 sub-agents in parallel: (1) explore agent \u2014 code review, architecture boundaries, type-safety for scoped files only. (2) explore agent \u2014 test review for scoped code: are tests asserting real behavior or were they edited to pass? Pass the scope (files/BET) explicitly to sub-agents so they do not touch other work. Use Context7 (
|
|
8556
|
+
facilitatorGuidance: "Restrict all review to the scope stated in Round 01 (by default, work in this conversation only). Spawn 1\u20132 sub-agents in parallel: (1) explore agent \u2014 code review, architecture boundaries, type-safety for scoped files only. (2) explore agent \u2014 test review for scoped code: are tests asserting real behavior or were they edited to pass? Pass the scope (files/BET) explicitly to sub-agents so they do not touch other work. Use Context7 CLI (npx ctx7 library/docs) for relevant testing and framework best practices if needed. Classify any test failures: staleness, regression, or flaky. **Fix-as-you-go (FEAT-172):** For every HIGH/MEDIUM finding: fix immediately, run lints, verify types. Only defer fixes estimated >5 min \u2014 capture deferred items as tensions on the Chain. After fixing, do a boy-scout pass on each scoped file: clean imports, improve naming, remove dead code. **Counter-Metric Mandate (STD-19):** When reporting test coverage, accuracy, or any quantitative result, include the denominator, what was excluded, and at least one counter-metric. Never report accuracy without recall. Never report pass rate without coverage. Synthesize: findings, fixes applied, implementation grade, test honesty verdict.",
|
|
8443
8557
|
outputSchema: {
|
|
8444
8558
|
field: "codeAndTests",
|
|
8445
8559
|
description: "Implementation grade, test honesty, refactor suggestions",
|
|
@@ -8515,7 +8629,7 @@ This block is the final deliverable of the review. Everything else is supporting
|
|
|
8515
8629
|
|
|
8516
8630
|
1. **Tool failure**: Explain what failed. Continue with what you have. The conversation is the record.
|
|
8517
8631
|
2. **Sub-agent timeout**: Proceed without that agent's output. Note what wasn't checked.
|
|
8518
|
-
3. **Context7 unavailable
|
|
8632
|
+
3. **Context7 CLI unavailable** (e.g. auth/network): Skip library doc validation. Rely on Chain and codebase review.
|
|
8519
8633
|
4. **MCP unreachable**: Run from conversation knowledge. Summarize findings. Offer to capture manually later.
|
|
8520
8634
|
|
|
8521
8635
|
The review must never fail silently. Always end with BET/chain IDs.`
|
|
@@ -13249,6 +13363,15 @@ function registerOrientTool(server) {
|
|
|
13249
13363
|
} catch {
|
|
13250
13364
|
}
|
|
13251
13365
|
const hasTaskGrounding = Boolean(task && orientEntries?.taskContext?.context?.length > 0);
|
|
13366
|
+
if (wsCtx && hasTaskGrounding && orientEntries?.writeBackHints) {
|
|
13367
|
+
const hints = Array.isArray(orientEntries.writeBackHints) ? orientEntries.writeBackHints : [];
|
|
13368
|
+
if (hints.length > 0) {
|
|
13369
|
+
trackWriteBackHintServed(wsCtx.workspaceId, {
|
|
13370
|
+
hint_count: hints.length,
|
|
13371
|
+
has_task: !!task
|
|
13372
|
+
});
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13252
13375
|
let openTensions = [];
|
|
13253
13376
|
try {
|
|
13254
13377
|
const tensions = await mcpQuery("chain.listEntries", { collectionSlug: "tensions" });
|
|
@@ -13327,6 +13450,13 @@ function registerOrientTool(server) {
|
|
|
13327
13450
|
if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
|
|
13328
13451
|
lines.push(`Task context: ${orientEntries.taskContext.totalFound} relevant entries (${orientEntries.taskContext.confidence} confidence).`);
|
|
13329
13452
|
}
|
|
13453
|
+
if (orientEntries?.writeBackHints && orientEntries.writeBackHints.length > 0) {
|
|
13454
|
+
lines.push("");
|
|
13455
|
+
lines.push("Write-back hints (what to capture this session):");
|
|
13456
|
+
for (const h of orientEntries.writeBackHints) {
|
|
13457
|
+
lines.push(` ${h.collectionSlug}: ${h.hint}`);
|
|
13458
|
+
}
|
|
13459
|
+
}
|
|
13330
13460
|
if (orientEntries?.activeBets?.length > 0) {
|
|
13331
13461
|
for (const e of orientEntries.activeBets) {
|
|
13332
13462
|
const tensions = e.linkedTensions;
|
|
@@ -13574,6 +13704,16 @@ function registerOrientTool(server) {
|
|
|
13574
13704
|
orientEntries.taskContext.constellationBetName
|
|
13575
13705
|
));
|
|
13576
13706
|
}
|
|
13707
|
+
if (orientEntries?.writeBackHints && orientEntries.writeBackHints.length > 0) {
|
|
13708
|
+
lines.push("");
|
|
13709
|
+
lines.push("## Write-Back Hints");
|
|
13710
|
+
lines.push("_What to capture this session (derived from task context):_");
|
|
13711
|
+
lines.push("");
|
|
13712
|
+
for (const h of orientEntries.writeBackHints) {
|
|
13713
|
+
lines.push(`- **${h.collectionSlug}**: ${h.hint}`);
|
|
13714
|
+
}
|
|
13715
|
+
lines.push("");
|
|
13716
|
+
}
|
|
13577
13717
|
if (orientEntries) {
|
|
13578
13718
|
const result = await runAlignmentCheck(
|
|
13579
13719
|
task,
|
|
@@ -14990,6 +15130,67 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
|
|
|
14990
15130
|
};
|
|
14991
15131
|
}
|
|
14992
15132
|
);
|
|
15133
|
+
server.resource(
|
|
15134
|
+
"chain-collection-capture-contract",
|
|
15135
|
+
new ResourceTemplate("productbrain://collections/{slug}/capture-contract", {
|
|
15136
|
+
list: async () => {
|
|
15137
|
+
const collections = await mcpQuery("chain.listCollections") ?? [];
|
|
15138
|
+
return {
|
|
15139
|
+
resources: collections.map((c) => ({
|
|
15140
|
+
uri: `productbrain://collections/${c.slug}/capture-contract`,
|
|
15141
|
+
name: `${c.icon ?? ""} ${c.name} \u2014 capture contract`.trim()
|
|
15142
|
+
}))
|
|
15143
|
+
};
|
|
15144
|
+
}
|
|
15145
|
+
}),
|
|
15146
|
+
async (uri, { slug }) => {
|
|
15147
|
+
const collectionSlug = slug;
|
|
15148
|
+
let workspaceId = null;
|
|
15149
|
+
try {
|
|
15150
|
+
const ctx = await getWorkspaceContext();
|
|
15151
|
+
workspaceId = ctx.workspaceId;
|
|
15152
|
+
} catch {
|
|
15153
|
+
}
|
|
15154
|
+
let contract = null;
|
|
15155
|
+
try {
|
|
15156
|
+
contract = await mcpQuery(
|
|
15157
|
+
"chain.getCaptureContract",
|
|
15158
|
+
{ collectionSlug }
|
|
15159
|
+
);
|
|
15160
|
+
} catch {
|
|
15161
|
+
contract = null;
|
|
15162
|
+
}
|
|
15163
|
+
if (!contract) {
|
|
15164
|
+
if (workspaceId) {
|
|
15165
|
+
trackCaptureContractMiss(workspaceId, { slug: collectionSlug });
|
|
15166
|
+
}
|
|
15167
|
+
return {
|
|
15168
|
+
contents: [{
|
|
15169
|
+
uri: uri.href,
|
|
15170
|
+
mimeType: "application/json",
|
|
15171
|
+
text: JSON.stringify({
|
|
15172
|
+
ok: false,
|
|
15173
|
+
error: `Collection '${collectionSlug}' not found in this workspace.`,
|
|
15174
|
+
hint: "Use `collections action=list` to see available collections and their slugs."
|
|
15175
|
+
})
|
|
15176
|
+
}]
|
|
15177
|
+
};
|
|
15178
|
+
}
|
|
15179
|
+
const result = {
|
|
15180
|
+
collectionSlug,
|
|
15181
|
+
requiredFields: contract.requiredFields,
|
|
15182
|
+
allFields: contract.allFields,
|
|
15183
|
+
captureExpectation: contract.captureExpectation
|
|
15184
|
+
};
|
|
15185
|
+
return {
|
|
15186
|
+
contents: [{
|
|
15187
|
+
uri: uri.href,
|
|
15188
|
+
mimeType: "application/json",
|
|
15189
|
+
text: JSON.stringify(result)
|
|
15190
|
+
}]
|
|
15191
|
+
};
|
|
15192
|
+
}
|
|
15193
|
+
);
|
|
14993
15194
|
server.resource(
|
|
14994
15195
|
"chain-search",
|
|
14995
15196
|
new ResourceTemplate("productbrain://search/{query}", {
|
|
@@ -15319,4 +15520,4 @@ export {
|
|
|
15319
15520
|
SERVER_VERSION,
|
|
15320
15521
|
createProductBrainServer
|
|
15321
15522
|
};
|
|
15322
|
-
//# sourceMappingURL=chunk-
|
|
15523
|
+
//# sourceMappingURL=chunk-ZZHGJKLF.js.map
|