nodebench-mcp 2.69.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +95 -39
  2. package/dist/agents/alertRouter.d.ts +38 -0
  3. package/dist/agents/alertRouter.js +151 -0
  4. package/dist/agents/alertRouter.js.map +1 -0
  5. package/dist/agents/entityMemory.d.ts +40 -0
  6. package/dist/agents/entityMemory.js +64 -0
  7. package/dist/agents/entityMemory.js.map +1 -0
  8. package/dist/agents/subAgents.d.ts +35 -0
  9. package/dist/agents/subAgents.js +62 -0
  10. package/dist/agents/subAgents.js.map +1 -0
  11. package/dist/benchmarks/benchmarkRunner.js +14 -0
  12. package/dist/benchmarks/benchmarkRunner.js.map +1 -1
  13. package/dist/benchmarks/chainEval.js +107 -0
  14. package/dist/benchmarks/chainEval.js.map +1 -1
  15. package/dist/benchmarks/llmJudgeEval.js +85 -0
  16. package/dist/benchmarks/llmJudgeEval.js.map +1 -1
  17. package/dist/benchmarks/searchQualityEval.js +118 -5
  18. package/dist/benchmarks/searchQualityEval.js.map +1 -1
  19. package/dist/cli/search.d.ts +13 -0
  20. package/dist/cli/search.js +130 -0
  21. package/dist/cli/search.js.map +1 -0
  22. package/dist/db.d.ts +6 -2
  23. package/dist/db.js +470 -3
  24. package/dist/db.js.map +1 -1
  25. package/dist/index.js +349 -64
  26. package/dist/index.js.map +1 -1
  27. package/dist/profiler/behaviorStore.d.ts +97 -0
  28. package/dist/profiler/behaviorStore.js +276 -0
  29. package/dist/profiler/behaviorStore.js.map +1 -0
  30. package/dist/profiler/eventCollector.d.ts +119 -0
  31. package/dist/profiler/eventCollector.js +267 -0
  32. package/dist/profiler/eventCollector.js.map +1 -0
  33. package/dist/profiler/index.d.ts +15 -0
  34. package/dist/profiler/index.js +16 -0
  35. package/dist/profiler/index.js.map +1 -0
  36. package/dist/profiler/mcpProxy.d.ts +49 -0
  37. package/dist/profiler/mcpProxy.js +123 -0
  38. package/dist/profiler/mcpProxy.js.map +1 -0
  39. package/dist/profiler/modelRouter.d.ts +30 -0
  40. package/dist/profiler/modelRouter.js +99 -0
  41. package/dist/profiler/modelRouter.js.map +1 -0
  42. package/dist/profiler/otelReceiver.d.ts +17 -0
  43. package/dist/profiler/otelReceiver.js +62 -0
  44. package/dist/profiler/otelReceiver.js.map +1 -0
  45. package/dist/profiler/proofEngine.d.ts +41 -0
  46. package/dist/profiler/proofEngine.js +93 -0
  47. package/dist/profiler/proofEngine.js.map +1 -0
  48. package/dist/profiler/workflowTemplates.d.ts +41 -0
  49. package/dist/profiler/workflowTemplates.js +95 -0
  50. package/dist/profiler/workflowTemplates.js.map +1 -0
  51. package/dist/providers/localMemoryProvider.js +3 -2
  52. package/dist/providers/localMemoryProvider.js.map +1 -1
  53. package/dist/runtimeConfig.d.ts +11 -0
  54. package/dist/runtimeConfig.js +27 -0
  55. package/dist/runtimeConfig.js.map +1 -0
  56. package/dist/security/auditLog.js +8 -3
  57. package/dist/security/auditLog.js.map +1 -1
  58. package/dist/subconscious/blocks.d.ts +43 -0
  59. package/dist/subconscious/blocks.js +158 -0
  60. package/dist/subconscious/blocks.js.map +1 -0
  61. package/dist/subconscious/classifier.d.ts +22 -0
  62. package/dist/subconscious/classifier.js +118 -0
  63. package/dist/subconscious/classifier.js.map +1 -0
  64. package/dist/subconscious/graphEngine.d.ts +65 -0
  65. package/dist/subconscious/graphEngine.js +234 -0
  66. package/dist/subconscious/graphEngine.js.map +1 -0
  67. package/dist/subconscious/index.d.ts +19 -0
  68. package/dist/subconscious/index.js +20 -0
  69. package/dist/subconscious/index.js.map +1 -0
  70. package/dist/subconscious/tools.d.ts +5 -0
  71. package/dist/subconscious/tools.js +255 -0
  72. package/dist/subconscious/tools.js.map +1 -0
  73. package/dist/subconscious/whisperPolicy.d.ts +20 -0
  74. package/dist/subconscious/whisperPolicy.js +171 -0
  75. package/dist/subconscious/whisperPolicy.js.map +1 -0
  76. package/dist/sweep/engine.d.ts +27 -0
  77. package/dist/sweep/engine.js +244 -0
  78. package/dist/sweep/engine.js.map +1 -0
  79. package/dist/sweep/index.d.ts +9 -0
  80. package/dist/sweep/index.js +8 -0
  81. package/dist/sweep/index.js.map +1 -0
  82. package/dist/sweep/sources/github_trending.d.ts +6 -0
  83. package/dist/sweep/sources/github_trending.js +37 -0
  84. package/dist/sweep/sources/github_trending.js.map +1 -0
  85. package/dist/sweep/sources/hackernews.d.ts +7 -0
  86. package/dist/sweep/sources/hackernews.js +57 -0
  87. package/dist/sweep/sources/hackernews.js.map +1 -0
  88. package/dist/sweep/sources/openbb_finance.d.ts +9 -0
  89. package/dist/sweep/sources/openbb_finance.js +46 -0
  90. package/dist/sweep/sources/openbb_finance.js.map +1 -0
  91. package/dist/sweep/sources/producthunt.d.ts +6 -0
  92. package/dist/sweep/sources/producthunt.js +41 -0
  93. package/dist/sweep/sources/producthunt.js.map +1 -0
  94. package/dist/sweep/sources/web_signals.d.ts +7 -0
  95. package/dist/sweep/sources/web_signals.js +63 -0
  96. package/dist/sweep/sources/web_signals.js.map +1 -0
  97. package/dist/sweep/sources/yahoo_finance.d.ts +6 -0
  98. package/dist/sweep/sources/yahoo_finance.js +47 -0
  99. package/dist/sweep/sources/yahoo_finance.js.map +1 -0
  100. package/dist/sweep/types.d.ts +50 -0
  101. package/dist/sweep/types.js +9 -0
  102. package/dist/sweep/types.js.map +1 -0
  103. package/dist/sync/founderEpisodeStore.d.ts +98 -0
  104. package/dist/sync/founderEpisodeStore.js +230 -0
  105. package/dist/sync/founderEpisodeStore.js.map +1 -0
  106. package/dist/sync/hyperloopArchive.d.ts +51 -0
  107. package/dist/sync/hyperloopArchive.js +153 -0
  108. package/dist/sync/hyperloopArchive.js.map +1 -0
  109. package/dist/sync/hyperloopEval.d.ts +123 -0
  110. package/dist/sync/hyperloopEval.js +389 -0
  111. package/dist/sync/hyperloopEval.js.map +1 -0
  112. package/dist/sync/hyperloopEval.test.d.ts +4 -0
  113. package/dist/sync/hyperloopEval.test.js +60 -0
  114. package/dist/sync/hyperloopEval.test.js.map +1 -0
  115. package/dist/sync/protocol.d.ts +172 -0
  116. package/dist/sync/protocol.js +9 -0
  117. package/dist/sync/protocol.js.map +1 -0
  118. package/dist/sync/sessionMemory.d.ts +47 -0
  119. package/dist/sync/sessionMemory.js +138 -0
  120. package/dist/sync/sessionMemory.js.map +1 -0
  121. package/dist/sync/store.d.ts +384 -0
  122. package/dist/sync/store.js +1435 -0
  123. package/dist/sync/store.js.map +1 -0
  124. package/dist/sync/store.test.d.ts +4 -0
  125. package/dist/sync/store.test.js +43 -0
  126. package/dist/sync/store.test.js.map +1 -0
  127. package/dist/sync/syncBridgeClient.d.ts +30 -0
  128. package/dist/sync/syncBridgeClient.js +172 -0
  129. package/dist/sync/syncBridgeClient.js.map +1 -0
  130. package/dist/tools/autonomousDeliveryTools.d.ts +2 -0
  131. package/dist/tools/autonomousDeliveryTools.js +1104 -0
  132. package/dist/tools/autonomousDeliveryTools.js.map +1 -0
  133. package/dist/tools/claudeCodeIngestTools.d.ts +10 -0
  134. package/dist/tools/claudeCodeIngestTools.js +347 -0
  135. package/dist/tools/claudeCodeIngestTools.js.map +1 -0
  136. package/dist/tools/coreWorkflowTools.d.ts +2 -0
  137. package/dist/tools/coreWorkflowTools.js +488 -0
  138. package/dist/tools/coreWorkflowTools.js.map +1 -0
  139. package/dist/tools/deltaTools.d.ts +15 -0
  140. package/dist/tools/deltaTools.js +1522 -0
  141. package/dist/tools/deltaTools.js.map +1 -0
  142. package/dist/tools/entityLookupTools.d.ts +14 -0
  143. package/dist/tools/entityLookupTools.js +159 -0
  144. package/dist/tools/entityLookupTools.js.map +1 -0
  145. package/dist/tools/entityTemporalTools.d.ts +12 -0
  146. package/dist/tools/entityTemporalTools.js +330 -0
  147. package/dist/tools/entityTemporalTools.js.map +1 -0
  148. package/dist/tools/founderLocalPipeline.d.ts +215 -0
  149. package/dist/tools/founderLocalPipeline.js +1516 -2
  150. package/dist/tools/founderLocalPipeline.js.map +1 -1
  151. package/dist/tools/founderOperatingModel.d.ts +120 -0
  152. package/dist/tools/founderOperatingModel.js +469 -0
  153. package/dist/tools/founderOperatingModel.js.map +1 -0
  154. package/dist/tools/founderOperatingModelTools.d.ts +2 -0
  155. package/dist/tools/founderOperatingModelTools.js +169 -0
  156. package/dist/tools/founderOperatingModelTools.js.map +1 -0
  157. package/dist/tools/founderStrategicOpsTools.d.ts +2 -0
  158. package/dist/tools/founderStrategicOpsTools.js +1310 -0
  159. package/dist/tools/founderStrategicOpsTools.js.map +1 -0
  160. package/dist/tools/graphifyTools.d.ts +19 -0
  161. package/dist/tools/graphifyTools.js +375 -0
  162. package/dist/tools/graphifyTools.js.map +1 -0
  163. package/dist/tools/index.d.ts +3 -0
  164. package/dist/tools/index.js +4 -0
  165. package/dist/tools/index.js.map +1 -1
  166. package/dist/tools/monteCarloTools.d.ts +16 -0
  167. package/dist/tools/monteCarloTools.js +225 -0
  168. package/dist/tools/monteCarloTools.js.map +1 -0
  169. package/dist/tools/packetCompilerTools.d.ts +12 -0
  170. package/dist/tools/packetCompilerTools.js +322 -0
  171. package/dist/tools/packetCompilerTools.js.map +1 -0
  172. package/dist/tools/planSynthesisTools.d.ts +15 -0
  173. package/dist/tools/planSynthesisTools.js +455 -0
  174. package/dist/tools/planSynthesisTools.js.map +1 -0
  175. package/dist/tools/profilerTools.d.ts +20 -0
  176. package/dist/tools/profilerTools.js +364 -0
  177. package/dist/tools/profilerTools.js.map +1 -0
  178. package/dist/tools/savingsTools.d.ts +11 -0
  179. package/dist/tools/savingsTools.js +155 -0
  180. package/dist/tools/savingsTools.js.map +1 -0
  181. package/dist/tools/scenarioCompilerTools.d.ts +14 -0
  182. package/dist/tools/scenarioCompilerTools.js +290 -0
  183. package/dist/tools/scenarioCompilerTools.js.map +1 -0
  184. package/dist/tools/sharedContextTools.d.ts +2 -0
  185. package/dist/tools/sharedContextTools.js +423 -0
  186. package/dist/tools/sharedContextTools.js.map +1 -0
  187. package/dist/tools/sitemapTools.d.ts +15 -0
  188. package/dist/tools/sitemapTools.js +560 -0
  189. package/dist/tools/sitemapTools.js.map +1 -0
  190. package/dist/tools/sweepTools.d.ts +9 -0
  191. package/dist/tools/sweepTools.js +112 -0
  192. package/dist/tools/sweepTools.js.map +1 -0
  193. package/dist/tools/syncBridgeTools.d.ts +2 -0
  194. package/dist/tools/syncBridgeTools.js +258 -0
  195. package/dist/tools/syncBridgeTools.js.map +1 -0
  196. package/dist/tools/toolRegistry.js +1216 -49
  197. package/dist/tools/toolRegistry.js.map +1 -1
  198. package/dist/tools/workspaceTools.d.ts +19 -0
  199. package/dist/tools/workspaceTools.js +762 -0
  200. package/dist/tools/workspaceTools.js.map +1 -0
  201. package/dist/toolsetRegistry.js +88 -2
  202. package/dist/toolsetRegistry.js.map +1 -1
  203. package/package.json +36 -36
  204. package/rules/nodebench-agentic-reliability.md +32 -0
  205. package/rules/nodebench-analyst-diagnostic.md +25 -0
  206. package/rules/nodebench-auto-qa.md +31 -0
  207. package/rules/nodebench-completion-traceability.md +22 -0
  208. package/rules/nodebench-flywheel-continuous.md +25 -0
  209. package/rules/nodebench-pre-release-review.md +24 -0
  210. package/rules/nodebench-qa-dogfood.md +26 -0
  211. package/rules/nodebench-scenario-testing.md +30 -0
  212. package/rules/nodebench-self-direction.md +23 -0
  213. package/rules/nodebench-self-judge-loop.md +24 -0
  214. package/scripts/install.sh +215 -0
@@ -0,0 +1,1310 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { genId, getDb } from "../db.js";
5
+ import { buildSharedContextPacketResource, getSharedContextPacket, invalidateSharedContextPacket, linkDurableObjects, proposeSharedContextTask, publishSharedContextPacket, pullSharedContextPackets, recordExecutionReceipt, recordLocalArtifact, recordLocalOutcome, registerSharedContextPeer, upsertDurableObject, } from "../sync/store.js";
6
+ import { buildFounderDirectionAssessment, } from "./founderLocalPipeline.js";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ const PACKAGE_ROOT = resolve(__dirname, "..", "..");
10
+ const REPO_ROOT = resolve(PACKAGE_ROOT, "..", "..");
11
+ function ensureFounderOpsSchema() {
12
+ const db = getDb();
13
+ db.exec(`
14
+ CREATE TABLE IF NOT EXISTS founder_watchlist_entities (
15
+ id TEXT PRIMARY KEY,
16
+ entity_name TEXT NOT NULL,
17
+ entity_name_lower TEXT NOT NULL UNIQUE,
18
+ strategic_angle_id TEXT,
19
+ added_at TEXT NOT NULL,
20
+ last_checked TEXT,
21
+ alert_preferences_json TEXT NOT NULL,
22
+ change_count INTEGER NOT NULL DEFAULT 0,
23
+ last_change_summary TEXT,
24
+ metadata_json TEXT NOT NULL DEFAULT '{}'
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_founder_watchlist_entities_added_at
28
+ ON founder_watchlist_entities(added_at DESC);
29
+
30
+ CREATE TABLE IF NOT EXISTS founder_share_links (
31
+ share_id TEXT PRIMARY KEY,
32
+ packet_id TEXT NOT NULL,
33
+ packet_type TEXT NOT NULL,
34
+ subject TEXT NOT NULL,
35
+ summary TEXT,
36
+ payload_json TEXT NOT NULL,
37
+ visibility TEXT NOT NULL,
38
+ created_at TEXT NOT NULL,
39
+ expires_at TEXT NOT NULL,
40
+ revoked_at TEXT
41
+ );
42
+
43
+ CREATE INDEX IF NOT EXISTS idx_founder_share_links_packet_id
44
+ ON founder_share_links(packet_id);
45
+
46
+ CREATE TABLE IF NOT EXISTS founder_retention_connections (
47
+ team_code TEXT PRIMARY KEY,
48
+ peer_id TEXT NOT NULL,
49
+ connected_at TEXT NOT NULL,
50
+ last_sync TEXT,
51
+ qa_score REAL,
52
+ member_count INTEGER,
53
+ tokens_saved INTEGER,
54
+ version TEXT,
55
+ metadata_json TEXT NOT NULL DEFAULT '{}'
56
+ );
57
+
58
+ CREATE TABLE IF NOT EXISTS founder_retention_events (
59
+ id TEXT PRIMARY KEY,
60
+ team_code TEXT,
61
+ event_type TEXT NOT NULL,
62
+ data_json TEXT NOT NULL,
63
+ created_at TEXT NOT NULL
64
+ );
65
+
66
+ CREATE INDEX IF NOT EXISTS idx_founder_retention_events_team_code
67
+ ON founder_retention_events(team_code, created_at DESC);
68
+ `);
69
+ }
70
+ function json(value) {
71
+ return JSON.stringify(value ?? {});
72
+ }
73
+ function parseJson(value, fallback) {
74
+ if (!value)
75
+ return fallback;
76
+ try {
77
+ return JSON.parse(value);
78
+ }
79
+ catch {
80
+ return fallback;
81
+ }
82
+ }
83
+ function safeReadText(filePath) {
84
+ try {
85
+ return existsSync(filePath) ? readFileSync(filePath, "utf-8") : null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ function safeReadJson(filePath) {
92
+ const text = safeReadText(filePath);
93
+ if (!text)
94
+ return null;
95
+ try {
96
+ return JSON.parse(text);
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ function uniq(values) {
103
+ return [...new Set(values)];
104
+ }
105
+ function includesAny(value, terms) {
106
+ const normalized = value.toLowerCase();
107
+ return terms.some((term) => normalized.includes(term.toLowerCase()));
108
+ }
109
+ function clamp(value, min, max) {
110
+ return Math.max(min, Math.min(max, value));
111
+ }
112
+ function selectStrategicAngle(assessment, angleId) {
113
+ if (angleId) {
114
+ const exact = assessment.strategicAngles.find((angle) => angle.id === angleId);
115
+ if (exact)
116
+ return exact;
117
+ }
118
+ const priority = { watch: 0, unknown: 1, strong: 2 };
119
+ return [...assessment.strategicAngles].sort((left, right) => {
120
+ const leftPriority = priority[left.status];
121
+ const rightPriority = priority[right.status];
122
+ if (leftPriority !== rightPriority)
123
+ return leftPriority - rightPriority;
124
+ return left.id.localeCompare(right.id);
125
+ })[0];
126
+ }
127
+ function startFounderTrace(toolName, label, metadata) {
128
+ const runId = `run:${genId("founder_run")}`;
129
+ const traceId = `trace:${genId("founder_trace")}`;
130
+ upsertDurableObject({
131
+ id: runId,
132
+ kind: "run",
133
+ label,
134
+ metadata: { toolName, ...(metadata ?? {}) },
135
+ });
136
+ upsertDurableObject({
137
+ id: traceId,
138
+ kind: "trace",
139
+ label: `${label} trace`,
140
+ metadata: { toolName, ...(metadata ?? {}) },
141
+ });
142
+ linkDurableObjects({
143
+ fromId: runId,
144
+ toId: traceId,
145
+ edgeType: "produces",
146
+ metadata: { toolName },
147
+ });
148
+ recordExecutionReceipt({
149
+ runId,
150
+ traceId,
151
+ objectId: traceId,
152
+ toolName,
153
+ actionType: "tool_started",
154
+ summary: label,
155
+ metadata,
156
+ });
157
+ return { runId, traceId };
158
+ }
159
+ function completeFounderTrace(args) {
160
+ recordExecutionReceipt({
161
+ runId: args.runId,
162
+ traceId: args.traceId,
163
+ objectId: args.traceId,
164
+ toolName: args.toolName,
165
+ actionType: "tool_completed",
166
+ summary: args.summary,
167
+ output: args.output,
168
+ status: "completed",
169
+ });
170
+ recordLocalOutcome({
171
+ runId: args.runId,
172
+ objectId: args.traceId,
173
+ outcomeType: args.outcomeType,
174
+ headline: args.summary,
175
+ userValue: args.userValue,
176
+ stakeholderValue: args.stakeholderValue,
177
+ status: "completed",
178
+ evidence: [],
179
+ metadata: { toolName: args.toolName },
180
+ });
181
+ }
182
+ function normalizeFounderAssessmentInput(args) {
183
+ if ("assessment" in args)
184
+ return args.assessment;
185
+ return buildFounderDirectionAssessment(args);
186
+ }
187
+ function getFounderPackets(options) {
188
+ const packets = pullSharedContextPackets({
189
+ contextType: "issue_packet",
190
+ requestingPeerId: options.requestingPeerId,
191
+ producerPeerId: options.producerPeerId,
192
+ workspaceId: options.workspaceId,
193
+ tenantId: options.tenantId,
194
+ status: options.status,
195
+ limit: options.limit ?? 50,
196
+ });
197
+ return packets.filter((packet) => {
198
+ const metadata = packet.metadata ?? {};
199
+ if (metadata.packetNamespace !== "founder_issue")
200
+ return false;
201
+ if (options.angleId && metadata.strategicAngleId !== options.angleId)
202
+ return false;
203
+ return true;
204
+ });
205
+ }
206
+ function getDistributionSurfacesInternal() {
207
+ const packageJson = safeReadJson(join(PACKAGE_ROOT, "package.json"));
208
+ const smitheryPath = join(PACKAGE_ROOT, "smithery.yaml");
209
+ const installScriptPath = join(PACKAGE_ROOT, "scripts", "install.sh");
210
+ const claudeDir = join(PACKAGE_ROOT, ".claude");
211
+ const cursorDir = join(PACKAGE_ROOT, ".cursor");
212
+ const ledgerViewPath = join(REPO_ROOT, "src", "features", "mcp", "views", "McpToolLedgerView.tsx");
213
+ return [
214
+ {
215
+ id: "npm_package",
216
+ label: "npm package",
217
+ status: packageJson?.version ? "ready" : "missing",
218
+ whyItMatters: "The package version is the canonical update surface for NodeBench MCP.",
219
+ evidence: packageJson?.version ? [`package.json version ${packageJson.version}`] : ["packages/mcp-local/package.json missing version"],
220
+ },
221
+ {
222
+ id: "npx_cli",
223
+ label: "npx CLI",
224
+ status: packageJson?.name === "nodebench-mcp" ? "ready" : "partial",
225
+ whyItMatters: "npx is the fastest install path for individual founders and Claude Code users.",
226
+ evidence: [`package name ${packageJson?.name ?? "unknown"}`],
227
+ },
228
+ {
229
+ id: "install_script",
230
+ label: "curl/bash installer",
231
+ status: existsSync(installScriptPath) ? "ready" : "missing",
232
+ whyItMatters: "A one-command installer reduces adoption friction for teams.",
233
+ evidence: [installScriptPath],
234
+ },
235
+ {
236
+ id: "claude_plugin",
237
+ label: "Claude Code config",
238
+ status: existsSync(claudeDir) ? "ready" : "missing",
239
+ whyItMatters: "Claude Code is a high-frequency workflow for the target user base.",
240
+ evidence: [claudeDir],
241
+ },
242
+ {
243
+ id: "cursor_plugin",
244
+ label: "Cursor config",
245
+ status: existsSync(cursorDir) ? "ready" : "partial",
246
+ whyItMatters: "Cursor parity matters for teams that do not standardize on Claude Code.",
247
+ evidence: [cursorDir],
248
+ },
249
+ {
250
+ id: "smithery",
251
+ label: "Smithery metadata",
252
+ status: existsSync(smitheryPath) ? "ready" : "missing",
253
+ whyItMatters: "Smithery increases discoverability for external MCP distribution.",
254
+ evidence: [smitheryPath],
255
+ },
256
+ {
257
+ id: "local_dashboard",
258
+ label: "local dashboard / ledger",
259
+ status: existsSync(ledgerViewPath) ? "ready" : "partial",
260
+ whyItMatters: "The operator needs an inspectable UI for receipts, packets, and sync provenance.",
261
+ evidence: [ledgerViewPath],
262
+ },
263
+ {
264
+ id: "shared_web_app",
265
+ label: "NodeBench AI web account",
266
+ status: existsSync(ledgerViewPath) ? "ready" : "partial",
267
+ whyItMatters: "Shared history and approvals need a multi-device web surface.",
268
+ evidence: [ledgerViewPath],
269
+ },
270
+ ];
271
+ }
272
+ function buildInstallCommand(preset) {
273
+ return `npx -y nodebench-mcp --preset=${preset}`;
274
+ }
275
+ function buildTeamInstallPlan(args) {
276
+ const preset = args.preferredPreset ?? (args.teamType === "founder" ? "founder" : "delta");
277
+ const workflow = (args.targetWorkflow ?? "claude code").toLowerCase();
278
+ const steps = [
279
+ `Install via ${buildInstallCommand(preset)} for the first operator.`,
280
+ workflow.includes("claude") || workflow.includes("mcp")
281
+ ? "Connect the preset to Claude Code first because that is the fastest path to high-frequency use."
282
+ : "Start with the local CLI and .mcp.json path, then add editor-native surfaces after the first team workflow is stable.",
283
+ args.requiresOffline
284
+ ? "Keep SQLite local-first and treat outbound sync as optional replication only."
285
+ : "Enable the outbound sync bridge after the first local workflow is working so shared history does not block initial adoption.",
286
+ args.needsDashboard || (args.seatCount ?? 1) > 1
287
+ ? "Turn on shared account review in NodeBench AI once at least one repeated packet/review workflow exists."
288
+ : "Delay shared dashboard work until the single-user loop shows weekly reuse.",
289
+ ];
290
+ return {
291
+ recommendedPreset: preset,
292
+ installCommand: buildInstallCommand(preset),
293
+ steps,
294
+ blockers: [
295
+ "MCP preset must match the real daily workflow instead of a generic tool bundle.",
296
+ "Installer, update story, and support path must be clear before broader team rollout.",
297
+ ],
298
+ successMetric: args.seatCount && args.seatCount > 1
299
+ ? "Two or more teammates can install, run the founder workflow, and publish/share a packet without manual setup help."
300
+ : "One founder can install from scratch and get to a useful packet inside one session.",
301
+ };
302
+ }
303
+ function installNodebenchPlugin(args) {
304
+ const preset = args.preset ?? "founder";
305
+ const targetDir = resolve(args.targetDir);
306
+ const configPath = join(targetDir, ".mcp.json");
307
+ const config = {
308
+ mcpServers: {
309
+ nodebench: {
310
+ command: "npx",
311
+ args: ["-y", "nodebench-mcp", "--preset", preset],
312
+ env: {},
313
+ },
314
+ },
315
+ };
316
+ if (args.dryRun !== false) {
317
+ return {
318
+ dryRun: true,
319
+ configPath,
320
+ config,
321
+ };
322
+ }
323
+ writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
324
+ return {
325
+ dryRun: false,
326
+ configPath,
327
+ config,
328
+ };
329
+ }
330
+ function checkPluginUpdateReadinessInternal() {
331
+ const packageJsonPath = join(PACKAGE_ROOT, "package.json");
332
+ const packageJson = safeReadJson(packageJsonPath);
333
+ const installScriptPath = join(PACKAGE_ROOT, "scripts", "install.sh");
334
+ const smitheryPath = join(PACKAGE_ROOT, "smithery.yaml");
335
+ const claudeDir = join(PACKAGE_ROOT, ".claude");
336
+ const cursorDir = join(PACKAGE_ROOT, ".cursor");
337
+ const missing = [];
338
+ if (!packageJson?.version)
339
+ missing.push("package version");
340
+ if (!existsSync(installScriptPath))
341
+ missing.push("install.sh");
342
+ if (!existsSync(smitheryPath))
343
+ missing.push("smithery.yaml");
344
+ if (!existsSync(claudeDir))
345
+ missing.push(".claude config");
346
+ if (!existsSync(cursorDir))
347
+ missing.push(".cursor config");
348
+ return {
349
+ version: packageJson?.version ?? null,
350
+ ready: missing.length === 0,
351
+ missing,
352
+ readinessChecks: {
353
+ packageVersion: Boolean(packageJson?.version),
354
+ installer: existsSync(installScriptPath),
355
+ smithery: existsSync(smitheryPath),
356
+ claudeConfig: existsSync(claudeDir),
357
+ cursorConfig: existsSync(cursorDir),
358
+ buildScript: Boolean(packageJson?.scripts?.build),
359
+ },
360
+ nextAction: missing.length === 0
361
+ ? "Version, installer, and discovery metadata are present. Publish only after the local install + sync bridge smoke test is green."
362
+ : `Fill the missing distribution surfaces: ${missing.join(", ")}.`,
363
+ };
364
+ }
365
+ function scanWorkflowAdoption(args) {
366
+ const query = args.query.toLowerCase();
367
+ const marketWorkflow = uniq((args.marketWorkflow ?? []).map((entry) => entry.toLowerCase()));
368
+ const targetUsers = uniq((args.targetUsers ?? []).map((entry) => entry.toLowerCase()));
369
+ const installSurface = uniq((args.installSurface ?? []).map((entry) => entry.toLowerCase()));
370
+ const constraints = uniq((args.constraints ?? []).map((entry) => entry.toLowerCase()));
371
+ let fitScore = 50;
372
+ if (includesAny(query, ["claude code", "mcp", "local", "sqlite"]))
373
+ fitScore += 20;
374
+ if (marketWorkflow.some((entry) => entry.includes("claude code") || entry.includes("mcp")))
375
+ fitScore += 15;
376
+ if (installSurface.some((entry) => entry.includes("dashboard") || entry.includes("subscription")))
377
+ fitScore += 5;
378
+ if (constraints.some((entry) => entry.includes("heavy setup") || entry.includes("manual onboarding")))
379
+ fitScore -= 15;
380
+ if (constraints.some((entry) => entry.includes("no ai") || entry.includes("anti ai")))
381
+ fitScore -= 10;
382
+ fitScore = clamp(fitScore, 0, 100);
383
+ return {
384
+ fitScore,
385
+ recommendedSurface: fitScore >= 70 ? "claude_code_mcp" : "local_cli_then_dashboard",
386
+ adoptionRisks: [
387
+ !marketWorkflow.length ? "Current user workflow is underspecified, so install and messaging risk are still fuzzy." : null,
388
+ !includesAny(query, ["claude code", "mcp"]) && marketWorkflow.length > 0
389
+ ? "The direction is not yet clearly anchored to the real workflow people already use."
390
+ : null,
391
+ constraints.some((entry) => entry.includes("no ai") || entry.includes("anti ai"))
392
+ ? "AI skepticism needs to be translated into a bounded, user-valued stance rather than a blanket product veto."
393
+ : null,
394
+ ].filter(Boolean),
395
+ installRecommendation: fitScore >= 70
396
+ ? "Lead with Claude Code + NodeBench MCP because that matches the current high-frequency workflow."
397
+ : "Lead with a narrow CLI/local-first wedge, then add the dashboard only after the loop proves sticky.",
398
+ maintenanceView: includesAny(query, ["dashboard", "subscription", "teams"])
399
+ ? "A team-facing dashboard creates support and uptime burden. Keep the local MCP surface as the system of record and add web collaboration only where reuse is proven."
400
+ : "Keep the initial surface light enough that one builder can maintain it without creating an ops tax.",
401
+ targetUsers,
402
+ marketWorkflow,
403
+ installSurface,
404
+ };
405
+ }
406
+ function buildServiceToDashboardPath(args) {
407
+ const assets = uniq(args.currentAssets ?? []);
408
+ const workflowAnchor = uniq(args.workflowAnchor ?? []);
409
+ const seatCount = args.seatCount ?? 1;
410
+ const supportLoad = args.supportLoad ?? "medium";
411
+ const recurring = args.needsRecurringValue ?? true;
412
+ const serviceFirstScore = clamp(60 + (assets.length > 0 ? 10 : 0) + (seatCount <= 3 ? 10 : -5) + (supportLoad === "low" ? 10 : supportLoad === "high" ? -10 : 0), 0, 100);
413
+ const dashboardScore = clamp(45 + (recurring ? 15 : 0) + (workflowAnchor.some((entry) => entry.toLowerCase().includes("claude code") || entry.toLowerCase().includes("mcp")) ? 10 : 0) + (seatCount > 3 ? 10 : 0), 0, 100);
414
+ const hybridScore = clamp(Math.round((serviceFirstScore + dashboardScore) / 2) + 5, 0, 100);
415
+ const recommendedPath = hybridScore >= serviceFirstScore && hybridScore >= dashboardScore ? "hybrid" : serviceFirstScore >= dashboardScore ? "service_first" : "dashboard_first";
416
+ return {
417
+ concept: args.concept,
418
+ pathScores: {
419
+ serviceFirst: serviceFirstScore,
420
+ dashboardFirst: dashboardScore,
421
+ hybrid: hybridScore,
422
+ },
423
+ recommendedPath,
424
+ rationale: recommendedPath === "service_first"
425
+ ? "Lead with done-for-you services until the value is obvious, then productize the repeated workflow."
426
+ : recommendedPath === "dashboard_first"
427
+ ? "The recurring team use case is already strong enough to justify a dashboard surface early."
428
+ : "Use the local MCP flow as the proving ground, then graduate the repeated artifacts and approvals into a dashboard subscription.",
429
+ milestones: [
430
+ "Prove one repeated high-value workflow with a founder or operator.",
431
+ "Capture the packet, review, and follow-up loop with durable receipts.",
432
+ "Turn the repeated review surface into a lightweight dashboard only after people want shared history or approvals.",
433
+ ],
434
+ };
435
+ }
436
+ function registerFounderPeerIfNeeded(args) {
437
+ return registerSharedContextPeer({
438
+ peerId: args.peerId,
439
+ product: "nodebench",
440
+ tenantId: args.tenantId,
441
+ workspaceId: args.workspaceId,
442
+ surface: "local_runtime",
443
+ role: args.role ?? "compiler",
444
+ capabilities: args.capabilities ?? ["founder-direction-assessment", "shared-context-publish", "execution-receipts"],
445
+ contextScopes: ["workspace", "run", "packet"],
446
+ status: "active",
447
+ summary: {
448
+ currentTask: "Founder strategic ops",
449
+ currentState: "ready",
450
+ confidence: 0.8,
451
+ availableArtifacts: ["issue_packet", "direction_assessment"],
452
+ lastUpdate: new Date().toISOString(),
453
+ },
454
+ });
455
+ }
456
+ export const founderStrategicOpsTools = [
457
+ {
458
+ name: "publish_founder_issue_packet",
459
+ description: "Turn the weakest founder-direction angle into a durable shared-context issue packet with lineage, proof links, and a reusable resource URI.",
460
+ inputSchema: {
461
+ type: "object",
462
+ properties: {
463
+ producerPeerId: { type: "string" },
464
+ workspaceId: { type: "string" },
465
+ tenantId: { type: "string" },
466
+ angleId: { type: "string" },
467
+ assessment: { type: "object" },
468
+ query: { type: "string" },
469
+ lens: { type: "string" },
470
+ daysBack: { type: "number" },
471
+ userSkillset: { type: "array", items: { type: "string" } },
472
+ interests: { type: "array", items: { type: "string" } },
473
+ constraints: { type: "array", items: { type: "string" } },
474
+ marketWorkflow: { type: "array", items: { type: "string" } },
475
+ extraContext: { type: "string" },
476
+ visibility: { type: "string", enum: ["internal", "workspace", "tenant"] },
477
+ },
478
+ required: ["producerPeerId"],
479
+ },
480
+ handler: async (rawArgs) => {
481
+ const args = rawArgs;
482
+ const trace = startFounderTrace("publish_founder_issue_packet", "Publish founder issue packet", {
483
+ producerPeerId: args.producerPeerId,
484
+ workspaceId: args.workspaceId,
485
+ });
486
+ registerFounderPeerIfNeeded({
487
+ peerId: args.producerPeerId,
488
+ workspaceId: args.workspaceId,
489
+ tenantId: args.tenantId,
490
+ });
491
+ const assessment = normalizeFounderAssessmentInput(args.assessment ? { assessment: args.assessment } : {
492
+ query: args.query ?? "Founder direction issue",
493
+ lens: args.lens,
494
+ daysBack: args.daysBack,
495
+ userSkillset: args.userSkillset,
496
+ interests: args.interests,
497
+ constraints: args.constraints,
498
+ marketWorkflow: args.marketWorkflow,
499
+ extraContext: args.extraContext,
500
+ });
501
+ const strategicAngle = selectStrategicAngle(assessment, args.angleId);
502
+ const published = publishSharedContextPacket({
503
+ contextType: "issue_packet",
504
+ producerPeerId: args.producerPeerId,
505
+ workspaceId: args.workspaceId,
506
+ tenantId: args.tenantId,
507
+ scope: uniq(["workspace", `angle:${strategicAngle.id}`, ...assessment.issueAngles.map((issueAngle) => `issue:${issueAngle}`)]),
508
+ subject: `${strategicAngle.title} issue`,
509
+ summary: strategicAngle.summary,
510
+ claims: [strategicAngle.summary, strategicAngle.whyItMatters, `Next question: ${strategicAngle.nextQuestion}`],
511
+ evidenceRefs: strategicAngle.evidenceRefIds,
512
+ confidence: assessment.confidence,
513
+ permissions: {
514
+ visibility: args.visibility ?? "workspace",
515
+ allowedRoles: ["compiler", "judge", "researcher", "router", "monitor"],
516
+ },
517
+ freshness: {
518
+ status: strategicAngle.status === "strong" ? "fresh" : "warming",
519
+ trustTier: "directional",
520
+ },
521
+ lineage: {
522
+ sourceRunId: trace.runId,
523
+ sourceTraceId: trace.traceId,
524
+ },
525
+ metadata: {
526
+ packetNamespace: "founder_issue",
527
+ strategicAngleId: strategicAngle.id,
528
+ strategicAngleTitle: strategicAngle.title,
529
+ assessmentId: assessment.assessmentId,
530
+ packetId: assessment.packetId,
531
+ recommendedNextAction: assessment.recommendedNextAction,
532
+ nextQuestions: assessment.nextQuestions,
533
+ sourceRefs: assessment.sourceRefs,
534
+ },
535
+ nextActions: [assessment.recommendedNextAction, `Resolve or delegate ${strategicAngle.id}`],
536
+ });
537
+ const packet = getSharedContextPacket(published.contextId, args.producerPeerId);
538
+ const resource = packet ? buildSharedContextPacketResource(packet, args.producerPeerId) : null;
539
+ completeFounderTrace({
540
+ runId: trace.runId,
541
+ traceId: trace.traceId,
542
+ toolName: "publish_founder_issue_packet",
543
+ summary: `Published founder issue packet for ${strategicAngle.id}`,
544
+ output: { contextId: published.contextId, angleId: strategicAngle.id },
545
+ outcomeType: "founder_issue_packet",
546
+ userValue: "Weak founder-direction angles are now durable packets instead of disposable chat output.",
547
+ stakeholderValue: "Stakeholders can inspect, delegate, and invalidate strategic risks with lineage.",
548
+ });
549
+ return {
550
+ contextId: published.contextId,
551
+ strategicAngle,
552
+ assessmentId: assessment.assessmentId,
553
+ resourceUri: resource?.resourceUri ?? null,
554
+ pullQuery: resource?.pullQuery ?? null,
555
+ subscriptionQuery: resource?.subscriptionQuery ?? null,
556
+ provenance: trace,
557
+ };
558
+ },
559
+ },
560
+ {
561
+ name: "list_founder_issue_packets",
562
+ description: "List founder issue packets from shared context by workspace, producer, status, or strategic angle.",
563
+ inputSchema: {
564
+ type: "object",
565
+ properties: {
566
+ requestingPeerId: { type: "string" },
567
+ producerPeerId: { type: "string" },
568
+ workspaceId: { type: "string" },
569
+ tenantId: { type: "string" },
570
+ status: { type: "string", enum: ["active", "superseded", "invalidated"] },
571
+ angleId: { type: "string" },
572
+ limit: { type: "number" },
573
+ },
574
+ },
575
+ annotations: { readOnlyHint: true },
576
+ handler: async (rawArgs) => {
577
+ const args = rawArgs;
578
+ const packets = getFounderPackets(args);
579
+ return {
580
+ count: packets.length,
581
+ packets: packets.map((packet) => ({
582
+ contextId: packet.contextId,
583
+ subject: packet.subject,
584
+ summary: packet.summary,
585
+ status: packet.status,
586
+ strategicAngleId: packet.metadata?.strategicAngleId ?? null,
587
+ strategicAngleTitle: packet.metadata?.strategicAngleTitle ?? null,
588
+ confidence: packet.confidence ?? null,
589
+ })),
590
+ };
591
+ },
592
+ },
593
+ {
594
+ name: "resolve_founder_issue",
595
+ description: "Invalidate a founder issue packet and optionally publish a resolution packet so the issue lifecycle stays explicit.",
596
+ inputSchema: {
597
+ type: "object",
598
+ properties: {
599
+ contextId: { type: "string" },
600
+ producerPeerId: { type: "string" },
601
+ resolverPeerId: { type: "string" },
602
+ resolutionSummary: { type: "string" },
603
+ publishResolution: { type: "boolean" },
604
+ },
605
+ required: ["contextId", "producerPeerId", "resolutionSummary"],
606
+ },
607
+ handler: async (rawArgs) => {
608
+ const args = rawArgs;
609
+ const trace = startFounderTrace("resolve_founder_issue", "Resolve founder issue", {
610
+ contextId: args.contextId,
611
+ });
612
+ const sourcePacket = getSharedContextPacket(args.contextId, args.resolverPeerId ?? args.producerPeerId);
613
+ if (!sourcePacket) {
614
+ throw new Error(`Founder issue packet not found or inaccessible: ${args.contextId}`);
615
+ }
616
+ const invalidation = invalidateSharedContextPacket(args.contextId, args.producerPeerId, args.resolutionSummary, [args.contextId]);
617
+ let resolutionContextId = null;
618
+ if (args.publishResolution !== false) {
619
+ const published = publishSharedContextPacket({
620
+ contextType: "workflow_packet",
621
+ producerPeerId: args.resolverPeerId ?? args.producerPeerId,
622
+ workspaceId: sourcePacket.workspaceId ?? undefined,
623
+ tenantId: sourcePacket.tenantId ?? undefined,
624
+ scope: uniq(["workspace", ...(sourcePacket.scope ?? [])]),
625
+ subject: `Resolved: ${sourcePacket.subject}`,
626
+ summary: args.resolutionSummary,
627
+ claims: [args.resolutionSummary],
628
+ evidenceRefs: [args.contextId],
629
+ confidence: sourcePacket.confidence ?? 0.8,
630
+ permissions: sourcePacket.permissions,
631
+ freshness: { status: "fresh", trustTier: "internal" },
632
+ lineage: {
633
+ parentContextIds: [args.contextId],
634
+ sourceRunId: trace.runId,
635
+ sourceTraceId: trace.traceId,
636
+ },
637
+ metadata: {
638
+ packetNamespace: "founder_issue_resolution",
639
+ resolvedIssueContextId: args.contextId,
640
+ strategicAngleId: sourcePacket.metadata?.strategicAngleId ?? null,
641
+ },
642
+ nextActions: ["Verify the issue stays resolved in the next founder run."],
643
+ });
644
+ resolutionContextId = published.contextId;
645
+ }
646
+ completeFounderTrace({
647
+ runId: trace.runId,
648
+ traceId: trace.traceId,
649
+ toolName: "resolve_founder_issue",
650
+ summary: `Resolved founder issue ${args.contextId}`,
651
+ output: { invalidated: invalidation.contextId, resolutionContextId },
652
+ outcomeType: "founder_issue_resolution",
653
+ userValue: "Resolved founder issues do not linger as stale context.",
654
+ stakeholderValue: "Issue invalidation and resolution packets make strategic state transitions auditable.",
655
+ });
656
+ return {
657
+ invalidatedContextId: invalidation.contextId,
658
+ resolutionContextId,
659
+ provenance: trace,
660
+ };
661
+ },
662
+ },
663
+ {
664
+ name: "delegate_founder_issue",
665
+ description: "Create a bounded shared task handoff for a founder issue packet so the weak angle becomes assigned work.",
666
+ inputSchema: {
667
+ type: "object",
668
+ properties: {
669
+ contextId: { type: "string" },
670
+ proposerPeerId: { type: "string" },
671
+ assigneePeerId: { type: "string" },
672
+ taskType: { type: "string" },
673
+ instructions: { type: "string" },
674
+ expectedOutputContextType: { type: "string" },
675
+ },
676
+ required: ["contextId", "proposerPeerId", "assigneePeerId"],
677
+ },
678
+ handler: async (rawArgs) => {
679
+ const args = rawArgs;
680
+ const trace = startFounderTrace("delegate_founder_issue", "Delegate founder issue", {
681
+ contextId: args.contextId,
682
+ assigneePeerId: args.assigneePeerId,
683
+ });
684
+ const packet = getSharedContextPacket(args.contextId, args.proposerPeerId);
685
+ if (!packet)
686
+ throw new Error(`Founder issue packet not found or inaccessible: ${args.contextId}`);
687
+ const proposed = proposeSharedContextTask({
688
+ taskType: args.taskType ?? "founder_issue_followup",
689
+ proposerPeerId: args.proposerPeerId,
690
+ assigneePeerId: args.assigneePeerId,
691
+ inputContextIds: [args.contextId],
692
+ taskSpec: {
693
+ instructions: args.instructions ?? packet.summary,
694
+ expectedOutputContextType: args.expectedOutputContextType ?? "workflow_packet",
695
+ strategicAngleId: packet.metadata?.strategicAngleId ?? null,
696
+ },
697
+ reason: `Resolve founder issue: ${packet.subject}`,
698
+ });
699
+ completeFounderTrace({
700
+ runId: trace.runId,
701
+ traceId: trace.traceId,
702
+ toolName: "delegate_founder_issue",
703
+ summary: `Delegated founder issue ${args.contextId} to ${args.assigneePeerId}`,
704
+ output: { taskId: proposed.taskId },
705
+ outcomeType: "founder_issue_delegation",
706
+ userValue: "Weak strategic angles become assigned tasks instead of vague concerns.",
707
+ stakeholderValue: "Delegation is tied to the packet lineage and can be audited later.",
708
+ });
709
+ return {
710
+ taskId: proposed.taskId,
711
+ contextId: args.contextId,
712
+ assigneePeerId: args.assigneePeerId,
713
+ provenance: trace,
714
+ };
715
+ },
716
+ },
717
+ {
718
+ name: "compare_founder_directions",
719
+ description: "Compare multiple founder directions side by side across strategic angles, issue count, confidence, and recommended wedge.",
720
+ inputSchema: {
721
+ type: "object",
722
+ properties: {
723
+ directions: {
724
+ type: "array",
725
+ items: {
726
+ type: "object",
727
+ properties: {
728
+ name: { type: "string" },
729
+ query: { type: "string" },
730
+ lens: { type: "string" },
731
+ userSkillset: { type: "array", items: { type: "string" } },
732
+ interests: { type: "array", items: { type: "string" } },
733
+ constraints: { type: "array", items: { type: "string" } },
734
+ marketWorkflow: { type: "array", items: { type: "string" } },
735
+ extraContext: { type: "string" },
736
+ },
737
+ required: ["name", "query"],
738
+ },
739
+ },
740
+ },
741
+ required: ["directions"],
742
+ },
743
+ annotations: { readOnlyHint: true },
744
+ handler: async (rawArgs) => {
745
+ const args = rawArgs;
746
+ const trace = startFounderTrace("compare_founder_directions", "Compare founder directions", {
747
+ directions: args.directions.map((direction) => direction.name),
748
+ });
749
+ const comparisons = args.directions.map((direction) => {
750
+ const assessment = buildFounderDirectionAssessment(direction);
751
+ const strong = assessment.strategicAngles.filter((angle) => angle.status === "strong").length;
752
+ const watch = assessment.strategicAngles.filter((angle) => angle.status === "watch").length;
753
+ const unknown = assessment.strategicAngles.filter((angle) => angle.status === "unknown").length;
754
+ const score = clamp(Math.round((assessment.confidence * 100) + (strong * 8) - (watch * 6) - (unknown * 4)), 0, 100);
755
+ return {
756
+ name: direction.name,
757
+ assessment,
758
+ score,
759
+ counts: { strong, watch, unknown },
760
+ weakestAngle: selectStrategicAngle(assessment),
761
+ };
762
+ }).sort((left, right) => right.score - left.score);
763
+ const recommended = comparisons[0];
764
+ const result = {
765
+ recommendation: {
766
+ direction: recommended.name,
767
+ score: recommended.score,
768
+ why: recommended.assessment.summary,
769
+ falsifier: recommended.weakestAngle.nextQuestion,
770
+ },
771
+ comparisons: comparisons.map((entry) => ({
772
+ name: entry.name,
773
+ score: entry.score,
774
+ confidence: entry.assessment.confidence,
775
+ counts: entry.counts,
776
+ weakestAngle: {
777
+ id: entry.weakestAngle.id,
778
+ title: entry.weakestAngle.title,
779
+ status: entry.weakestAngle.status,
780
+ nextQuestion: entry.weakestAngle.nextQuestion,
781
+ },
782
+ recommendedNextAction: entry.assessment.recommendedNextAction,
783
+ })),
784
+ };
785
+ completeFounderTrace({
786
+ runId: trace.runId,
787
+ traceId: trace.traceId,
788
+ toolName: "compare_founder_directions",
789
+ summary: `Compared ${args.directions.length} founder directions`,
790
+ output: result,
791
+ outcomeType: "founder_direction_compare",
792
+ userValue: "Multiple directions can be pressure-tested before committing roadmap time.",
793
+ stakeholderValue: "Direction choice is backed by explicit tradeoffs and falsifiers instead of intuition alone.",
794
+ });
795
+ return { ...result, provenance: trace };
796
+ },
797
+ },
798
+ {
799
+ name: "workflow_adoption_scan",
800
+ description: "Evaluate how naturally a direction fits current high-frequency user workflows, install surfaces, and maintenance burden.",
801
+ inputSchema: {
802
+ type: "object",
803
+ properties: {
804
+ query: { type: "string" },
805
+ marketWorkflow: { type: "array", items: { type: "string" } },
806
+ targetUsers: { type: "array", items: { type: "string" } },
807
+ installSurface: { type: "array", items: { type: "string" } },
808
+ constraints: { type: "array", items: { type: "string" } },
809
+ },
810
+ required: ["query"],
811
+ },
812
+ annotations: { readOnlyHint: true },
813
+ handler: async (rawArgs) => scanWorkflowAdoption(rawArgs),
814
+ },
815
+ {
816
+ name: "service_to_dashboard_path",
817
+ description: "Map a concept from bespoke service work to a possible dashboard subscription path without losing the local-first wedge.",
818
+ inputSchema: {
819
+ type: "object",
820
+ properties: {
821
+ concept: { type: "string" },
822
+ currentAssets: { type: "array", items: { type: "string" } },
823
+ seatCount: { type: "number" },
824
+ needsRecurringValue: { type: "boolean" },
825
+ supportLoad: { type: "string", enum: ["low", "medium", "high"] },
826
+ workflowAnchor: { type: "array", items: { type: "string" } },
827
+ },
828
+ required: ["concept"],
829
+ },
830
+ annotations: { readOnlyHint: true },
831
+ handler: async (rawArgs) => buildServiceToDashboardPath(rawArgs),
832
+ },
833
+ {
834
+ name: "get_founder_packet_resource",
835
+ description: "Fetch the resource URI, pull query, and subscription query for a founder issue or resolution packet.",
836
+ inputSchema: {
837
+ type: "object",
838
+ properties: {
839
+ contextId: { type: "string" },
840
+ peerId: { type: "string" },
841
+ },
842
+ required: ["contextId"],
843
+ },
844
+ annotations: { readOnlyHint: true },
845
+ handler: async (rawArgs) => {
846
+ const args = rawArgs;
847
+ const packet = getSharedContextPacket(args.contextId, args.peerId);
848
+ if (!packet) {
849
+ return { found: false, resourceUri: null, pullQuery: null, subscriptionQuery: null };
850
+ }
851
+ const resource = buildSharedContextPacketResource(packet, args.peerId);
852
+ return { found: true, ...resource };
853
+ },
854
+ },
855
+ {
856
+ name: "get_distribution_surfaces",
857
+ description: "Inspect NodeBench MCP distribution surfaces: npm/npx, installer, plugin configs, Smithery, and shared web review surfaces.",
858
+ inputSchema: { type: "object", properties: {} },
859
+ annotations: { readOnlyHint: true },
860
+ handler: async () => {
861
+ const surfaces = getDistributionSurfacesInternal();
862
+ return {
863
+ surfaces,
864
+ readyCount: surfaces.filter((surface) => surface.status === "ready").length,
865
+ partialCount: surfaces.filter((surface) => surface.status === "partial").length,
866
+ missingCount: surfaces.filter((surface) => surface.status === "missing").length,
867
+ };
868
+ },
869
+ },
870
+ {
871
+ name: "generate_team_install_plan",
872
+ description: "Generate a practical install and rollout plan for a founder, solo developer, or small team using NodeBench MCP.",
873
+ inputSchema: {
874
+ type: "object",
875
+ properties: {
876
+ teamType: { type: "string" },
877
+ targetWorkflow: { type: "string" },
878
+ preferredPreset: { type: "string" },
879
+ seatCount: { type: "number" },
880
+ requiresOffline: { type: "boolean" },
881
+ needsDashboard: { type: "boolean" },
882
+ },
883
+ },
884
+ annotations: { readOnlyHint: true },
885
+ handler: async (rawArgs) => buildTeamInstallPlan(rawArgs),
886
+ },
887
+ {
888
+ name: "install_nodebench_plugin",
889
+ description: "Generate or write a starter .mcp.json entry for NodeBench MCP so a local team member can install the preset quickly.",
890
+ inputSchema: {
891
+ type: "object",
892
+ properties: {
893
+ targetDir: { type: "string" },
894
+ preset: { type: "string" },
895
+ dryRun: { type: "boolean" },
896
+ },
897
+ required: ["targetDir"],
898
+ },
899
+ handler: async (rawArgs) => installNodebenchPlugin(rawArgs),
900
+ },
901
+ {
902
+ name: "check_plugin_update_readiness",
903
+ description: "Check whether NodeBench MCP is ready for a version/update push across installer, plugin metadata, and editor surfaces.",
904
+ inputSchema: { type: "object", properties: {} },
905
+ annotations: { readOnlyHint: true },
906
+ handler: async () => checkPluginUpdateReadinessInternal(),
907
+ },
908
+ {
909
+ name: "watchlist_add_entity",
910
+ description: "Add an entity to the local founder watchlist with alert preferences and optional strategic-angle linkage.",
911
+ inputSchema: {
912
+ type: "object",
913
+ properties: {
914
+ entityName: { type: "string" },
915
+ alertPreferences: { type: "array", items: { type: "string" } },
916
+ strategicAngleId: { type: "string" },
917
+ metadata: { type: "object" },
918
+ },
919
+ required: ["entityName"],
920
+ },
921
+ handler: async (rawArgs) => {
922
+ ensureFounderOpsSchema();
923
+ const args = rawArgs;
924
+ const db = getDb();
925
+ const normalized = args.entityName.trim().toLowerCase();
926
+ const existing = db.prepare(`
927
+ SELECT * FROM founder_watchlist_entities WHERE entity_name_lower = ? LIMIT 1
928
+ `).get(normalized);
929
+ if (existing) {
930
+ return {
931
+ status: "already_watching",
932
+ entity: {
933
+ id: existing.id,
934
+ entityName: existing.entity_name,
935
+ strategicAngleId: existing.strategic_angle_id ?? null,
936
+ },
937
+ };
938
+ }
939
+ const id = genId("watch");
940
+ const now = new Date().toISOString();
941
+ db.prepare(`
942
+ INSERT INTO founder_watchlist_entities
943
+ (id, entity_name, entity_name_lower, strategic_angle_id, added_at, last_checked, alert_preferences_json, change_count, last_change_summary, metadata_json)
944
+ VALUES (?, ?, ?, ?, ?, NULL, ?, 0, NULL, ?)
945
+ `).run(id, args.entityName, normalized, args.strategicAngleId ?? null, now, json(args.alertPreferences ?? ["any_material"]), json(args.metadata ?? {}));
946
+ upsertDurableObject({
947
+ id,
948
+ kind: "artifact",
949
+ label: `Watchlist entity: ${args.entityName}`,
950
+ metadata: {
951
+ entityName: args.entityName,
952
+ strategicAngleId: args.strategicAngleId ?? null,
953
+ },
954
+ });
955
+ recordLocalArtifact({
956
+ objectId: id,
957
+ kind: "watchlist_entity",
958
+ summary: `Watching ${args.entityName}`,
959
+ verificationStatus: "verified",
960
+ metadata: {
961
+ alertPreferences: args.alertPreferences ?? ["any_material"],
962
+ strategicAngleId: args.strategicAngleId ?? null,
963
+ },
964
+ });
965
+ return {
966
+ status: "added",
967
+ entity: {
968
+ id,
969
+ entityName: args.entityName,
970
+ alertPreferences: args.alertPreferences ?? ["any_material"],
971
+ strategicAngleId: args.strategicAngleId ?? null,
972
+ },
973
+ };
974
+ },
975
+ },
976
+ {
977
+ name: "watchlist_list_entities",
978
+ description: "List watched entities from the local founder watchlist.",
979
+ inputSchema: { type: "object", properties: {} },
980
+ annotations: { readOnlyHint: true },
981
+ handler: async () => {
982
+ ensureFounderOpsSchema();
983
+ const rows = getDb().prepare(`
984
+ SELECT *
985
+ FROM founder_watchlist_entities
986
+ ORDER BY added_at DESC
987
+ `).all();
988
+ return {
989
+ count: rows.length,
990
+ entities: rows.map((row) => ({
991
+ id: row.id,
992
+ entityName: row.entity_name,
993
+ strategicAngleId: row.strategic_angle_id ?? null,
994
+ addedAt: row.added_at,
995
+ lastChecked: row.last_checked ?? null,
996
+ alertPreferences: parseJson(row.alert_preferences_json, []),
997
+ changeCount: row.change_count,
998
+ lastChangeSummary: row.last_change_summary ?? null,
999
+ metadata: parseJson(row.metadata_json, {}),
1000
+ })),
1001
+ };
1002
+ },
1003
+ },
1004
+ {
1005
+ name: "watchlist_refresh_entities",
1006
+ description: "Refresh watchlist timestamps and optionally attach change summaries for watched entities.",
1007
+ inputSchema: {
1008
+ type: "object",
1009
+ properties: {
1010
+ changes: {
1011
+ type: "array",
1012
+ items: {
1013
+ type: "object",
1014
+ properties: {
1015
+ entityName: { type: "string" },
1016
+ summary: { type: "string" },
1017
+ },
1018
+ required: ["entityName", "summary"],
1019
+ },
1020
+ },
1021
+ },
1022
+ },
1023
+ handler: async (rawArgs) => {
1024
+ ensureFounderOpsSchema();
1025
+ const args = rawArgs;
1026
+ const db = getDb();
1027
+ const now = new Date().toISOString();
1028
+ db.prepare(`UPDATE founder_watchlist_entities SET last_checked = ?`).run(now);
1029
+ for (const change of args.changes ?? []) {
1030
+ db.prepare(`
1031
+ UPDATE founder_watchlist_entities
1032
+ SET change_count = change_count + 1,
1033
+ last_change_summary = ?,
1034
+ last_checked = ?
1035
+ WHERE entity_name_lower = ?
1036
+ `).run(change.summary, now, change.entityName.toLowerCase());
1037
+ }
1038
+ const refreshed = db.prepare(`SELECT COUNT(*) as count FROM founder_watchlist_entities`).get().count;
1039
+ return {
1040
+ refreshed,
1041
+ changed: (args.changes ?? []).length,
1042
+ hint: "Use delta diligence or founder issue packets to turn real changes into durable packets.",
1043
+ };
1044
+ },
1045
+ },
1046
+ {
1047
+ name: "watchlist_get_alerts",
1048
+ description: "Return watchlist entries with attached change summaries or non-zero alert counts.",
1049
+ inputSchema: { type: "object", properties: {} },
1050
+ annotations: { readOnlyHint: true },
1051
+ handler: async () => {
1052
+ ensureFounderOpsSchema();
1053
+ const rows = getDb().prepare(`
1054
+ SELECT *
1055
+ FROM founder_watchlist_entities
1056
+ WHERE change_count > 0 OR last_change_summary IS NOT NULL
1057
+ ORDER BY last_checked DESC
1058
+ `).all();
1059
+ return {
1060
+ count: rows.length,
1061
+ alerts: rows.map((row) => ({
1062
+ entityName: row.entity_name,
1063
+ changeCount: row.change_count,
1064
+ lastChange: row.last_change_summary ?? null,
1065
+ lastChecked: row.last_checked ?? null,
1066
+ })),
1067
+ };
1068
+ },
1069
+ },
1070
+ {
1071
+ name: "share_create_packet_link",
1072
+ description: "Create a durable local share link record for a packet or founder memo so it can be rendered or synced later.",
1073
+ inputSchema: {
1074
+ type: "object",
1075
+ properties: {
1076
+ packetId: { type: "string" },
1077
+ packetType: { type: "string" },
1078
+ subject: { type: "string" },
1079
+ summary: { type: "string" },
1080
+ payload: { type: "object" },
1081
+ visibility: { type: "string" },
1082
+ baseUrl: { type: "string" },
1083
+ },
1084
+ required: ["subject"],
1085
+ },
1086
+ handler: async (rawArgs) => {
1087
+ ensureFounderOpsSchema();
1088
+ const args = rawArgs;
1089
+ const shareId = genId("share");
1090
+ const createdAt = new Date().toISOString();
1091
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
1092
+ getDb().prepare(`
1093
+ INSERT INTO founder_share_links
1094
+ (share_id, packet_id, packet_type, subject, summary, payload_json, visibility, created_at, expires_at, revoked_at)
1095
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
1096
+ `).run(shareId, args.packetId ?? shareId, args.packetType ?? "memo", args.subject, args.summary ?? "", json(args.payload ?? {}), args.visibility ?? "workspace", createdAt, expiresAt);
1097
+ upsertDurableObject({
1098
+ id: shareId,
1099
+ kind: "artifact",
1100
+ label: `Share link: ${args.subject}`,
1101
+ metadata: {
1102
+ packetId: args.packetId ?? shareId,
1103
+ packetType: args.packetType ?? "memo",
1104
+ },
1105
+ });
1106
+ recordLocalArtifact({
1107
+ objectId: shareId,
1108
+ kind: "share_link",
1109
+ summary: `Share link for ${args.subject}`,
1110
+ verificationStatus: "verified",
1111
+ metadata: {
1112
+ packetId: args.packetId ?? shareId,
1113
+ packetType: args.packetType ?? "memo",
1114
+ subject: args.subject,
1115
+ },
1116
+ });
1117
+ const baseUrl = (args.baseUrl ?? "https://nodebenchai.com").replace(/\/$/, "");
1118
+ return {
1119
+ shareId,
1120
+ shareUrl: `${baseUrl}/share/${shareId}`,
1121
+ expiresAt,
1122
+ };
1123
+ },
1124
+ },
1125
+ {
1126
+ name: "share_get_packet_link",
1127
+ description: "Retrieve a local share link record by share ID.",
1128
+ inputSchema: {
1129
+ type: "object",
1130
+ properties: { shareId: { type: "string" } },
1131
+ required: ["shareId"],
1132
+ },
1133
+ annotations: { readOnlyHint: true },
1134
+ handler: async (rawArgs) => {
1135
+ ensureFounderOpsSchema();
1136
+ const args = rawArgs;
1137
+ const row = getDb().prepare(`
1138
+ SELECT *
1139
+ FROM founder_share_links
1140
+ WHERE share_id = ?
1141
+ LIMIT 1
1142
+ `).get(args.shareId);
1143
+ if (!row || row.revoked_at) {
1144
+ return { found: false };
1145
+ }
1146
+ return {
1147
+ found: true,
1148
+ shareId: row.share_id,
1149
+ packetId: row.packet_id,
1150
+ packetType: row.packet_type,
1151
+ subject: row.subject,
1152
+ summary: row.summary,
1153
+ payload: parseJson(row.payload_json, {}),
1154
+ createdAt: row.created_at,
1155
+ expiresAt: row.expires_at,
1156
+ };
1157
+ },
1158
+ },
1159
+ {
1160
+ name: "share_revoke_packet_link",
1161
+ description: "Revoke a local share link so it no longer counts as active.",
1162
+ inputSchema: {
1163
+ type: "object",
1164
+ properties: { shareId: { type: "string" } },
1165
+ required: ["shareId"],
1166
+ },
1167
+ handler: async (rawArgs) => {
1168
+ ensureFounderOpsSchema();
1169
+ const args = rawArgs;
1170
+ const now = new Date().toISOString();
1171
+ const result = getDb().prepare(`
1172
+ UPDATE founder_share_links
1173
+ SET revoked_at = ?
1174
+ WHERE share_id = ? AND revoked_at IS NULL
1175
+ `).run(now, args.shareId);
1176
+ return { revoked: result.changes > 0, shareId: args.shareId };
1177
+ },
1178
+ },
1179
+ {
1180
+ name: "retention_register_connection",
1181
+ description: "Register a retention.sh team connection in local MCP state so QA findings and token savings can flow into founder packets.",
1182
+ inputSchema: {
1183
+ type: "object",
1184
+ properties: {
1185
+ teamCode: { type: "string" },
1186
+ peerId: { type: "string" },
1187
+ version: { type: "string" },
1188
+ memberCount: { type: "number" },
1189
+ },
1190
+ required: ["teamCode"],
1191
+ },
1192
+ handler: async (rawArgs) => {
1193
+ ensureFounderOpsSchema();
1194
+ const args = rawArgs;
1195
+ const now = new Date().toISOString();
1196
+ const peerId = args.peerId ?? `peer:monitor:retention:${args.teamCode}`;
1197
+ getDb().prepare(`
1198
+ INSERT INTO founder_retention_connections
1199
+ (team_code, peer_id, connected_at, last_sync, qa_score, member_count, tokens_saved, version, metadata_json)
1200
+ VALUES (?, ?, ?, NULL, NULL, ?, NULL, ?, '{}')
1201
+ ON CONFLICT(team_code) DO UPDATE SET
1202
+ peer_id = excluded.peer_id,
1203
+ version = excluded.version,
1204
+ member_count = COALESCE(excluded.member_count, founder_retention_connections.member_count)
1205
+ `).run(args.teamCode, peerId, now, args.memberCount ?? null, args.version ?? null);
1206
+ registerFounderPeerIfNeeded({
1207
+ peerId,
1208
+ role: "monitor",
1209
+ capabilities: ["retention-sync", "qa-score", "tokens-saved"],
1210
+ });
1211
+ getDb().prepare(`
1212
+ INSERT INTO founder_retention_events (id, team_code, event_type, data_json, created_at)
1213
+ VALUES (?, ?, ?, ?, ?)
1214
+ `).run(genId("ret_event"), args.teamCode, "registered", json({ peerId }), now);
1215
+ return {
1216
+ status: "connected",
1217
+ teamCode: args.teamCode,
1218
+ peerId,
1219
+ };
1220
+ },
1221
+ },
1222
+ {
1223
+ name: "retention_sync_findings",
1224
+ description: "Sync retention.sh QA findings, scores, and token savings into local MCP state.",
1225
+ inputSchema: {
1226
+ type: "object",
1227
+ properties: {
1228
+ teamCode: { type: "string" },
1229
+ qaFindings: { type: "array", items: { type: "object" } },
1230
+ qaScore: { type: "number" },
1231
+ tokensSaved: { type: "number" },
1232
+ teamMembers: { type: "number" },
1233
+ },
1234
+ required: ["teamCode"],
1235
+ },
1236
+ handler: async (rawArgs) => {
1237
+ ensureFounderOpsSchema();
1238
+ const args = rawArgs;
1239
+ const now = new Date().toISOString();
1240
+ getDb().prepare(`
1241
+ UPDATE founder_retention_connections
1242
+ SET last_sync = ?,
1243
+ qa_score = COALESCE(?, qa_score),
1244
+ tokens_saved = COALESCE(?, tokens_saved),
1245
+ member_count = COALESCE(?, member_count)
1246
+ WHERE team_code = ?
1247
+ `).run(now, args.qaScore ?? null, args.tokensSaved ?? null, args.teamMembers ?? null, args.teamCode);
1248
+ getDb().prepare(`
1249
+ INSERT INTO founder_retention_events (id, team_code, event_type, data_json, created_at)
1250
+ VALUES (?, ?, ?, ?, ?)
1251
+ `).run(genId("ret_event"), args.teamCode, "sync", json({
1252
+ qaScore: args.qaScore ?? null,
1253
+ tokensSaved: args.tokensSaved ?? null,
1254
+ findingCount: args.qaFindings?.length ?? 0,
1255
+ }), now);
1256
+ return {
1257
+ status: "synced",
1258
+ findingsReceived: args.qaFindings?.length ?? 0,
1259
+ qaScore: args.qaScore ?? null,
1260
+ };
1261
+ },
1262
+ },
1263
+ {
1264
+ name: "retention_get_status",
1265
+ description: "Return the latest retention.sh connection and recent event history from local MCP state.",
1266
+ inputSchema: {
1267
+ type: "object",
1268
+ properties: { teamCode: { type: "string" } },
1269
+ },
1270
+ annotations: { readOnlyHint: true },
1271
+ handler: async (rawArgs) => {
1272
+ ensureFounderOpsSchema();
1273
+ const args = rawArgs;
1274
+ const row = getDb().prepare(`
1275
+ SELECT *
1276
+ FROM founder_retention_connections
1277
+ WHERE (? IS NULL OR team_code = ?)
1278
+ ORDER BY connected_at DESC
1279
+ LIMIT 1
1280
+ `).get(args.teamCode ?? null, args.teamCode ?? null);
1281
+ if (!row) {
1282
+ return { connected: false };
1283
+ }
1284
+ const events = getDb().prepare(`
1285
+ SELECT *
1286
+ FROM founder_retention_events
1287
+ WHERE team_code = ?
1288
+ ORDER BY created_at DESC
1289
+ LIMIT 10
1290
+ `).all(row.team_code);
1291
+ return {
1292
+ connected: true,
1293
+ teamCode: row.team_code,
1294
+ peerId: row.peer_id,
1295
+ connectedAt: row.connected_at,
1296
+ lastSync: row.last_sync ?? null,
1297
+ qaScore: row.qa_score ?? null,
1298
+ memberCount: row.member_count ?? null,
1299
+ tokensSaved: row.tokens_saved ?? null,
1300
+ version: row.version ?? null,
1301
+ recentEvents: events.map((event) => ({
1302
+ type: event.event_type,
1303
+ timestamp: event.created_at,
1304
+ data: parseJson(event.data_json, {}),
1305
+ })),
1306
+ };
1307
+ },
1308
+ },
1309
+ ];
1310
+ //# sourceMappingURL=founderStrategicOpsTools.js.map