idea-gauntlet 0.1.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.
- package/README.md +154 -0
- package/dist/chunk-A6GCV4RD.js +379 -0
- package/dist/chunk-FF7CULAJ.js +11 -0
- package/dist/chunk-HW6JACOL.js +324 -0
- package/dist/chunk-P4FDULQC.js +703 -0
- package/dist/chunk-VQHEJYTS.js +32 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +644 -0
- package/dist/defender-IQOS3YI7.js +6 -0
- package/dist/index.d.ts +210 -0
- package/dist/index.js +139 -0
- package/dist/server-FLE4IK6S.js +9 -0
- package/dist/setup-QMYBP3QE.js +6 -0
- package/dist/skeptic-KU7EW27C.js +6 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# IdeaGauntlet
|
|
2
|
+
|
|
3
|
+
Stress-test product ideas with adversarial agents, synthetic users, and court-style critique.
|
|
4
|
+
|
|
5
|
+
> Most AI tools help you generate more ideas. IdeaGauntlet helps you survive the one you already have.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/idea-gauntlet)
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx idea-gauntlet quick "A synthetic focus room app for remote workers" --mock
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install idea-gauntlet
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or run directly:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx idea-gauntlet quick "Your product idea here"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage Paths
|
|
28
|
+
|
|
29
|
+
| Path | Provider? | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| **Standalone CLI** | API key or Ollama | `idea-gauntlet quick "idea"` |
|
|
32
|
+
| **Mock mode** | None needed | `idea-gauntlet quick "idea" --mock` |
|
|
33
|
+
| **Prompt mode** | None needed | `idea-gauntlet prompt quick "idea"` |
|
|
34
|
+
| **Agent-native** | None needed | `idea-gauntlet setup` |
|
|
35
|
+
|
|
36
|
+
## CLI Commands
|
|
37
|
+
|
|
38
|
+
### quick
|
|
39
|
+
Fast adversarial critique.
|
|
40
|
+
```bash
|
|
41
|
+
idea-gauntlet quick "Your idea" [--mock] [--json] [--output report.md]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### court
|
|
45
|
+
Multi-role structured debate.
|
|
46
|
+
```bash
|
|
47
|
+
idea-gauntlet court idea.md [--mock]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### users
|
|
51
|
+
Generate synthetic user personas.
|
|
52
|
+
```bash
|
|
53
|
+
idea-gauntlet users idea.md --personas 6 [--mock]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### mvp
|
|
57
|
+
Generate a 14-day validation plan.
|
|
58
|
+
```bash
|
|
59
|
+
idea-gauntlet mvp idea.md [--mock]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### compare
|
|
63
|
+
Compare multiple ideas.
|
|
64
|
+
```bash
|
|
65
|
+
idea-gauntlet compare idea1.md idea2.md [--mock]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### prompt
|
|
69
|
+
Generate structured prompts (no API key needed).
|
|
70
|
+
```bash
|
|
71
|
+
idea-gauntlet prompt quick "Your idea"
|
|
72
|
+
idea-gauntlet prompt court idea.md
|
|
73
|
+
idea-gauntlet prompt users idea.md --personas 8
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### init
|
|
77
|
+
Scaffold a workspace.
|
|
78
|
+
```bash
|
|
79
|
+
idea-gauntlet init [--directory path]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### setup
|
|
83
|
+
Generate integration files for Claude Code, Cursor, Codex.
|
|
84
|
+
```bash
|
|
85
|
+
idea-gauntlet setup [--all] [--dry-run]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### doctor
|
|
89
|
+
Check environment and configuration.
|
|
90
|
+
```bash
|
|
91
|
+
idea-gauntlet doctor
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### mcp
|
|
95
|
+
Start MCP server for Claude Desktop integration.
|
|
96
|
+
```bash
|
|
97
|
+
idea-gauntlet mcp
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## TypeScript API
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { runGauntlet, MockProvider } from "idea-gauntlet";
|
|
104
|
+
|
|
105
|
+
const report = await runGauntlet({
|
|
106
|
+
idea: "A synthetic focus room app for remote workers",
|
|
107
|
+
targetUsers: ["remote workers", "students"],
|
|
108
|
+
mode: "quick",
|
|
109
|
+
provider: new MockProvider(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
console.log(report.markdown);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Scoring Philosophy
|
|
116
|
+
|
|
117
|
+
Scores are diagnostic signals, not predictions.
|
|
118
|
+
|
|
119
|
+
| Dimension | What it measures |
|
|
120
|
+
|---|---|
|
|
121
|
+
| Clarity | Is the idea specific and well-defined? |
|
|
122
|
+
| Pain | Does the user have a real, painful problem? |
|
|
123
|
+
| Differentiation | Is the approach distinct? |
|
|
124
|
+
| Buildability | Can a small team build this? |
|
|
125
|
+
| Distribution | Can this reach its target users? |
|
|
126
|
+
| Monetization | Is there a clear revenue model? |
|
|
127
|
+
| Evidence | What real evidence supports the idea? |
|
|
128
|
+
|
|
129
|
+
Evidence score defaults low unless you provide real user evidence.
|
|
130
|
+
|
|
131
|
+
## No API Key? No Problem.
|
|
132
|
+
|
|
133
|
+
- **Prompt mode** generates structured prompts for any AI
|
|
134
|
+
- **Mock mode** runs deterministically for demos
|
|
135
|
+
- **Agent-native mode** generates Claude/Cursor/Codex integration files
|
|
136
|
+
- **Setup** never requires an API key
|
|
137
|
+
|
|
138
|
+
## Product Philosophy
|
|
139
|
+
|
|
140
|
+
IdeaGauntlet is:
|
|
141
|
+
- adversarial but useful
|
|
142
|
+
- skeptical but not cynical
|
|
143
|
+
- evidence-aware
|
|
144
|
+
- honest about uncertainty
|
|
145
|
+
|
|
146
|
+
IdeaGauntlet is not:
|
|
147
|
+
- a startup idea generator
|
|
148
|
+
- a replacement for real user research
|
|
149
|
+
- a market research oracle
|
|
150
|
+
- a hype machine
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MockProvider,
|
|
3
|
+
buildReport,
|
|
4
|
+
resolveProvider,
|
|
5
|
+
runCompareEngine,
|
|
6
|
+
runCourtEngine,
|
|
7
|
+
runImmuneEngine,
|
|
8
|
+
runMvpPlanner,
|
|
9
|
+
runUserLab
|
|
10
|
+
} from "./chunk-P4FDULQC.js";
|
|
11
|
+
import {
|
|
12
|
+
skeptic
|
|
13
|
+
} from "./chunk-VQHEJYTS.js";
|
|
14
|
+
import {
|
|
15
|
+
defender
|
|
16
|
+
} from "./chunk-FF7CULAJ.js";
|
|
17
|
+
|
|
18
|
+
// src/utils/safeWrite.ts
|
|
19
|
+
import { existsSync, writeFileSync } from "fs";
|
|
20
|
+
import { isAbsolute, resolve } from "path";
|
|
21
|
+
function safeWriteOutput(outputPath, content, label) {
|
|
22
|
+
if (!outputPath) {
|
|
23
|
+
console.log(content);
|
|
24
|
+
return { ok: true, path: "" };
|
|
25
|
+
}
|
|
26
|
+
const resolved2 = resolve(process.cwd(), outputPath);
|
|
27
|
+
const parts = outputPath.split(/[\\/]/);
|
|
28
|
+
if (!isAbsolute(outputPath) && parts.includes("..")) {
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
if (!resolved2.startsWith(cwd)) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
reason: "traversal",
|
|
34
|
+
message: `Path traversal detected: '${outputPath}'. Use an absolute path or a path within the working directory.`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (existsSync(resolved2)) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
reason: "exists",
|
|
42
|
+
message: `'${outputPath}' already exists. Use --force to overwrite.`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
writeFileSync(resolved2, content, "utf-8");
|
|
46
|
+
console.log(`${label ?? "File"} written to ${outputPath}`);
|
|
47
|
+
return { ok: true, path: resolved2 };
|
|
48
|
+
}
|
|
49
|
+
function safeWriteReport(reportId, content, workspaceDir) {
|
|
50
|
+
const baseDir = resolve(workspaceDir, ".idea-gauntlet", "reports");
|
|
51
|
+
const filePath = resolve(baseDir, `${reportId}.md`);
|
|
52
|
+
if (!filePath.startsWith(baseDir)) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
reason: "traversal",
|
|
56
|
+
message: "Invalid path: report must be written inside .idea-gauntlet/reports/"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!existsSync(baseDir)) {
|
|
60
|
+
import("fs").then((fs) => fs.mkdirSync(baseDir, { recursive: true }));
|
|
61
|
+
}
|
|
62
|
+
writeFileSync(filePath, content, "utf-8");
|
|
63
|
+
return { ok: true, path: filePath };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/mcp/tools.ts
|
|
67
|
+
var reports = /* @__PURE__ */ new Map();
|
|
68
|
+
var provider = new MockProvider();
|
|
69
|
+
var resolved = resolveProvider({});
|
|
70
|
+
if (resolved) provider = resolved.provider;
|
|
71
|
+
function getReportIds() {
|
|
72
|
+
return Array.from(reports.keys());
|
|
73
|
+
}
|
|
74
|
+
async function handleToolCall(name, args) {
|
|
75
|
+
switch (name) {
|
|
76
|
+
case "create_prompt": {
|
|
77
|
+
const mode = args.mode ?? "quick";
|
|
78
|
+
const ideaText = args.idea ?? "";
|
|
79
|
+
if (!ideaText) throw new Error("idea parameter required");
|
|
80
|
+
let output = "";
|
|
81
|
+
const idea = { idea: ideaText };
|
|
82
|
+
switch (mode) {
|
|
83
|
+
case "quick": {
|
|
84
|
+
const sk = skeptic(idea);
|
|
85
|
+
const df = defender(idea);
|
|
86
|
+
output = [
|
|
87
|
+
"# IdeaGauntlet \u2014 Quick Critique Prompt",
|
|
88
|
+
"",
|
|
89
|
+
"Copy the prompts below into your AI assistant.",
|
|
90
|
+
"",
|
|
91
|
+
"---",
|
|
92
|
+
"",
|
|
93
|
+
"## Prompt 1: Skeptic",
|
|
94
|
+
`**System:** ${sk.system}`,
|
|
95
|
+
"",
|
|
96
|
+
sk.userMessage,
|
|
97
|
+
"",
|
|
98
|
+
"## Prompt 2: Defender",
|
|
99
|
+
`**System:** ${df.system}`,
|
|
100
|
+
"",
|
|
101
|
+
df.userMessage,
|
|
102
|
+
"",
|
|
103
|
+
"## Instructions for the AI",
|
|
104
|
+
"Respond to both prompts, then synthesize a verdict with: core insight, strongest case, weakest assumption, top failure modes, dangerous assumptions, kill tests, and next actions."
|
|
105
|
+
].join("\n");
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case "court": {
|
|
109
|
+
const roles = [
|
|
110
|
+
"Prosecutor",
|
|
111
|
+
"Defender",
|
|
112
|
+
"User Advocate",
|
|
113
|
+
"Investor",
|
|
114
|
+
"Competitor",
|
|
115
|
+
"Judge"
|
|
116
|
+
];
|
|
117
|
+
const roleDescriptions = [
|
|
118
|
+
"attacks the idea",
|
|
119
|
+
"argues why it could work",
|
|
120
|
+
"argues from the user's perspective",
|
|
121
|
+
"evaluates market, scale, defensibility",
|
|
122
|
+
"explains how the idea could be copied or crushed",
|
|
123
|
+
"summarizes the verdict"
|
|
124
|
+
];
|
|
125
|
+
output = [
|
|
126
|
+
"# IdeaGauntlet \u2014 Court Debate Prompt",
|
|
127
|
+
"",
|
|
128
|
+
`Product idea: ${ideaText}`,
|
|
129
|
+
"",
|
|
130
|
+
"Run a structured debate with these roles:",
|
|
131
|
+
...roles.map(
|
|
132
|
+
(r, i) => `${i + 1}. **${r}** \u2014 ${roleDescriptions[i]}`
|
|
133
|
+
),
|
|
134
|
+
"",
|
|
135
|
+
"Each role speaks once, max 300 words. End with a judge verdict and unresolved questions."
|
|
136
|
+
].join("\n");
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "users": {
|
|
140
|
+
const count = args.personas ?? "6";
|
|
141
|
+
output = [
|
|
142
|
+
"# IdeaGauntlet \u2014 Synthetic User Prompt",
|
|
143
|
+
"",
|
|
144
|
+
`Product idea: ${ideaText}`,
|
|
145
|
+
"",
|
|
146
|
+
`Generate ${count} fictional user archetypes who would encounter this product.`,
|
|
147
|
+
"",
|
|
148
|
+
"For each persona, include:",
|
|
149
|
+
"- Name and archetype label",
|
|
150
|
+
"- Goal they are trying to accomplish",
|
|
151
|
+
"- Current workaround (what they do today)",
|
|
152
|
+
"- Trigger that would make them try this product",
|
|
153
|
+
"- Primary objection (why they would hesitate)",
|
|
154
|
+
"- Willingness to pay (none/low/medium/high)",
|
|
155
|
+
"- Likely reason they would churn",
|
|
156
|
+
"- A quote expressing their skepticism",
|
|
157
|
+
"- One question a founder should ask a real user like this",
|
|
158
|
+
"",
|
|
159
|
+
"**IMPORTANT:** These are fictional archetypes for hypothesis generation, not real validation."
|
|
160
|
+
].join("\n");
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case "mvp": {
|
|
164
|
+
output = [
|
|
165
|
+
"# IdeaGauntlet \u2014 MVP Validation Plan Prompt",
|
|
166
|
+
"",
|
|
167
|
+
`Product idea: ${ideaText}`,
|
|
168
|
+
"",
|
|
169
|
+
"Generate an aggressive MVP validation plan with:",
|
|
170
|
+
"- 14-day MVP plan (max 3 things to build)",
|
|
171
|
+
"- Fake-door test design (what would the landing page say?)",
|
|
172
|
+
"- User interview script (5 questions to ask)",
|
|
173
|
+
"- Success metrics (what numbers justify continuing?)",
|
|
174
|
+
"- Kill criteria (what results mean pivot or stop?)",
|
|
175
|
+
"- Pivot options (adjacent directions if the core doesn't work)",
|
|
176
|
+
"",
|
|
177
|
+
"Be aggressive about reducing scope."
|
|
178
|
+
].join("\n");
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Unknown prompt mode: ${mode}. Use: quick, court, users, mvp`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return { type: "text", text: output };
|
|
187
|
+
}
|
|
188
|
+
case "quick_critique": {
|
|
189
|
+
if (!args.idea) throw new Error("idea required");
|
|
190
|
+
const report = await runImmuneEngine({ idea: args.idea }, provider);
|
|
191
|
+
report.markdown = buildReport(report);
|
|
192
|
+
reports.set(report.id, report);
|
|
193
|
+
return { type: "text", text: report.markdown };
|
|
194
|
+
}
|
|
195
|
+
case "run_court": {
|
|
196
|
+
if (!args.idea) throw new Error("idea required");
|
|
197
|
+
const report = await runCourtEngine({ idea: args.idea }, provider);
|
|
198
|
+
reports.set(report.id, report);
|
|
199
|
+
return { type: "text", text: report.markdown };
|
|
200
|
+
}
|
|
201
|
+
case "generate_users": {
|
|
202
|
+
if (!args.idea) throw new Error("idea required");
|
|
203
|
+
const report = await runUserLab(
|
|
204
|
+
{ idea: args.idea },
|
|
205
|
+
provider,
|
|
206
|
+
args.personas ?? 6
|
|
207
|
+
);
|
|
208
|
+
reports.set(report.id, report);
|
|
209
|
+
return { type: "text", text: report.markdown };
|
|
210
|
+
}
|
|
211
|
+
case "plan_mvp": {
|
|
212
|
+
if (!args.idea) throw new Error("idea required");
|
|
213
|
+
const report = await runMvpPlanner({ idea: args.idea }, provider);
|
|
214
|
+
reports.set(report.id, report);
|
|
215
|
+
return { type: "text", text: report.markdown };
|
|
216
|
+
}
|
|
217
|
+
case "compare_ideas": {
|
|
218
|
+
if (!args.ideas || !Array.isArray(args.ideas))
|
|
219
|
+
throw new Error("ideas array required");
|
|
220
|
+
const ideas = args.ideas.map((i) => ({ idea: i }));
|
|
221
|
+
const report = await runCompareEngine(ideas, provider);
|
|
222
|
+
reports.set(report.id, report);
|
|
223
|
+
return { type: "text", text: report.markdown };
|
|
224
|
+
}
|
|
225
|
+
case "save_report": {
|
|
226
|
+
if (!args.id) throw new Error("report id required");
|
|
227
|
+
const report = reports.get(args.id);
|
|
228
|
+
if (!report) throw new Error(`Report not found: ${args.id}`);
|
|
229
|
+
const result = safeWriteReport(args.id, report.markdown, process.cwd());
|
|
230
|
+
if (!result.ok) throw new Error(result.message);
|
|
231
|
+
return { type: "text", text: `Report saved to ${result.path}` };
|
|
232
|
+
}
|
|
233
|
+
default:
|
|
234
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
var toolDefinitions = [
|
|
238
|
+
{
|
|
239
|
+
name: "create_prompt",
|
|
240
|
+
description: "Generate a structured prompt for any mode",
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: {
|
|
244
|
+
mode: { type: "string" },
|
|
245
|
+
idea: { type: "string" }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "quick_critique",
|
|
251
|
+
description: "Run quick adversarial critique",
|
|
252
|
+
inputSchema: {
|
|
253
|
+
type: "object",
|
|
254
|
+
properties: { idea: { type: "string" } },
|
|
255
|
+
required: ["idea"]
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "run_court",
|
|
260
|
+
description: "Run court-style debate",
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: { idea: { type: "string" } },
|
|
264
|
+
required: ["idea"]
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "generate_users",
|
|
269
|
+
description: "Generate synthetic user personas",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: {
|
|
273
|
+
idea: { type: "string" },
|
|
274
|
+
personas: { type: "number" }
|
|
275
|
+
},
|
|
276
|
+
required: ["idea"]
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: "plan_mvp",
|
|
281
|
+
description: "Generate MVP validation plan",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: { idea: { type: "string" } },
|
|
285
|
+
required: ["idea"]
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "compare_ideas",
|
|
290
|
+
description: "Compare multiple ideas",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
ideas: { type: "array", items: { type: "string" } }
|
|
295
|
+
},
|
|
296
|
+
required: ["ideas"]
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "save_report",
|
|
301
|
+
description: "Save report to .idea-gauntlet/reports/",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: { id: { type: "string" } },
|
|
305
|
+
required: ["id"]
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
// src/mcp/resources.ts
|
|
311
|
+
function listResources(reportIds) {
|
|
312
|
+
return {
|
|
313
|
+
resources: [
|
|
314
|
+
...reportIds.map((id) => ({
|
|
315
|
+
uri: `idea-gauntlet://report/${id}`,
|
|
316
|
+
name: `Report ${id}`,
|
|
317
|
+
mimeType: "text/markdown"
|
|
318
|
+
})),
|
|
319
|
+
...reportIds.map((id) => ({
|
|
320
|
+
uri: `idea-gauntlet://report/${id}/summary`,
|
|
321
|
+
name: `Report ${id} Summary`,
|
|
322
|
+
mimeType: "text/plain"
|
|
323
|
+
}))
|
|
324
|
+
]
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/mcp/server.ts
|
|
329
|
+
function respond(id, result, error) {
|
|
330
|
+
const msg = { jsonrpc: "2.0", id };
|
|
331
|
+
if (error) {
|
|
332
|
+
msg.error = { code: error.code ?? -32e3, message: error.message ?? "Unknown error" };
|
|
333
|
+
} else {
|
|
334
|
+
msg.result = result;
|
|
335
|
+
}
|
|
336
|
+
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
337
|
+
}
|
|
338
|
+
function handleMessage(line) {
|
|
339
|
+
const trimmed = line.trim();
|
|
340
|
+
if (!trimmed) return;
|
|
341
|
+
let msg;
|
|
342
|
+
try {
|
|
343
|
+
msg = JSON.parse(trimmed);
|
|
344
|
+
} catch {
|
|
345
|
+
console.error("Failed to parse JSON-RPC message:", trimmed.slice(0, 200));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (msg.method === "tools/list") {
|
|
349
|
+
respond(msg.id, { tools: toolDefinitions });
|
|
350
|
+
} else if (msg.method === "tools/call") {
|
|
351
|
+
handleToolCall(msg.params.name, msg.params.arguments ?? {}).then((result) => respond(msg.id, { content: [result] })).catch(
|
|
352
|
+
(err) => respond(msg.id, null, { code: -32e3, message: err.message })
|
|
353
|
+
);
|
|
354
|
+
} else if (msg.method === "resources/list") {
|
|
355
|
+
respond(msg.id, listResources(getReportIds()));
|
|
356
|
+
} else {
|
|
357
|
+
respond(msg.id, null, { code: -32601, message: "Method not found" });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function startMcpServer() {
|
|
361
|
+
let buffer = "";
|
|
362
|
+
process.stdin.on("data", (chunk) => {
|
|
363
|
+
buffer += chunk.toString("utf-8");
|
|
364
|
+
const lines = buffer.split("\n");
|
|
365
|
+
buffer = lines.pop() ?? "";
|
|
366
|
+
for (const line of lines) {
|
|
367
|
+
handleMessage(line);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
process.stdin.on("error", (err) => {
|
|
371
|
+
console.error("stdin error:", err.message);
|
|
372
|
+
});
|
|
373
|
+
process.stdin.resume();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export {
|
|
377
|
+
safeWriteOutput,
|
|
378
|
+
startMcpServer
|
|
379
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/agents/defender.ts
|
|
2
|
+
var defender = (idea) => ({
|
|
3
|
+
system: "You are the Defender in IdeaGauntlet. Your job is to make the strongest honest case for the product idea. Do not exaggerate. Do not invent evidence. Identify the most compelling wedge, the likely early adopters, and the narrow version of the idea most likely to work.",
|
|
4
|
+
userMessage: `Product idea: ${idea.idea}
|
|
5
|
+
|
|
6
|
+
Make the strongest honest case for this idea. Focus on the real problem it solves, the users who need it most, and the version most likely to work. Return plain text, 2-3 paragraphs.`
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
defender
|
|
11
|
+
};
|