@clien-ai/mcp 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,6 +68,25 @@ the top 3 threats", etc.
68
68
 
69
69
  ---
70
70
 
71
+ ## Recipes: composing beyond the hero
72
+
73
+ `clien_research` is the hero — the recommended default for validating an idea
74
+ end to end. Alongside it, the server exposes granular primitives (`create_persona`,
75
+ `interview_persona`, `list_projects` / `create_project`, `list_reports` /
76
+ `get_report`, `clien_research_status`) you can sequence for targeted, cheaper
77
+ checks.
78
+
79
+ See [`docs/recipes/`](../docs/recipes/) for the supported compositions:
80
+
81
+ - [Competitor spot-check](../docs/recipes/competitor-spot-check.md) — read `competitors[]` from a report instead of commissioning a new study
82
+ - [Pivot viability](../docs/recipes/pivot-viability.md) — research a pivot and compare it against the original
83
+ - [New-market persona validation](../docs/recipes/new-market-persona-validation.md) — `create_persona` → `interview_persona`, no credits, no full run
84
+
85
+ Rule of thumb: for a **full** validation, call the hero; reach for a primitive
86
+ composition only for a **narrow, cheaper probe**.
87
+
88
+ ---
89
+
71
90
  ## Inputs
72
91
 
73
92
  | Field | Type | Required | Default | Notes |
package/dist/server.js CHANGED
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * MCP stdio server entry point.
3
3
  *
4
- * Registers the `clien_research` tool. Auth happens lazily on first tool call
5
- * so the server can announce its tool list without a browser interruption.
4
+ * The tool surface lives in `tools/registry.ts` — both `tools/list` and
5
+ * `tools/call` iterate the `TOOLS` array, so adding a tool needs no edits here.
6
+ * Auth happens lazily on first tool call so the server can announce its tool
7
+ * list without a browser interruption.
6
8
  */
7
9
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
10
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -10,7 +12,8 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextpro
10
12
  import { loadConfig, PACKAGE_NAME, PACKAGE_VERSION } from './config.js';
11
13
  import { ensureSession, } from './auth/oauth.js';
12
14
  import { clearTokens, loadRefreshToken, saveRefreshToken, withTokenLock, } from './auth/storage.js';
13
- import { ResearchToolError, runResearch, } from './tools/research.js';
15
+ import { ToolError } from './tools/errors.js';
16
+ import { TOOLS } from './tools/registry.js';
14
17
  function log(...args) {
15
18
  // stderr only — stdout is the MCP transport.
16
19
  console.error('[clien-mcp]', ...args);
@@ -28,62 +31,6 @@ async function defaultBrowserOpener() {
28
31
  await fn(url);
29
32
  };
30
33
  }
31
- /**
32
- * Input schema for the tool advert. Hand-rolled JSON Schema so we avoid the
33
- * `zod-to-json-schema` dependency. Must stay in sync with `ResearchInputSchema`
34
- * in `src/tools/research.ts` — the Zod parser is still the source of truth at
35
- * runtime; this is only the spec advertised to the caller.
36
- *
37
- * Drift surfaces silently: the Zod parser still accepts the input, but strict
38
- * MCP clients (`additionalProperties: false`) strip undeclared fields before
39
- * they reach the parser, and LLM callers never learn the field exists.
40
- * Enforcement: `__tests__/schema-contract.test.ts` walks both schemas and
41
- * fails if their property keys or required-field sets diverge. Exported here
42
- * so that test can import it. See
43
- * `docs/solutions/design-patterns/cross-package-schema-duplication-contract-2026-05-12.md`.
44
- */
45
- export const RESEARCH_INPUT_SCHEMA = {
46
- type: 'object',
47
- properties: {
48
- idea: {
49
- type: 'string',
50
- minLength: 1,
51
- maxLength: 10_000,
52
- description: 'The product idea, feature hypothesis, or research question to validate. ' +
53
- 'Can be a one-liner or several paragraphs — more context yields a better report.',
54
- },
55
- depth: {
56
- type: 'string',
57
- enum: ['quick', 'deep'],
58
- default: 'deep',
59
- description: '"deep" (default) runs a 15-25 minute full research pass. ' +
60
- '"quick" caps cost and runs a tighter pass.',
61
- },
62
- project_id: {
63
- type: 'string',
64
- format: 'uuid',
65
- description: 'Optional Clien.ai project UUID to attach this run to. When omitted, ' +
66
- 'the run is standalone and not associated with any project.',
67
- },
68
- project_name: {
69
- type: 'string',
70
- minLength: 1,
71
- maxLength: 100,
72
- description: 'Name for a new Clien.ai project to attach this run to. You SHOULD ' +
73
- 'supply this whenever `project_id` is not set, otherwise the ' +
74
- 'resulting report will not be discoverable in the Clien.ai web UI. ' +
75
- 'Derive a short (2-4 word) human-readable name from the idea — ' +
76
- 'typically the product name. Guidance on when to create a new ' +
77
- 'project vs. reuse an existing one: create a NEW project (supply a ' +
78
- 'fresh `project_name`) for each distinct research idea or product ' +
79
- 'concept; REUSE an existing `project_id` only when the user ' +
80
- 'explicitly asks to add follow-up research to a prior run on the ' +
81
- 'same idea. Ignored when `project_id` is also provided.',
82
- },
83
- },
84
- required: ['idea'],
85
- additionalProperties: false,
86
- };
87
34
  export function createMcpServer() {
88
35
  const config = loadConfig();
89
36
  // Per-instance session cache. Scoping this to the closure (rather than the
@@ -115,20 +62,23 @@ export function createMcpServer() {
115
62
  }
116
63
  const server = new Server({ name: PACKAGE_NAME, version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
117
64
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
118
- tools: [
119
- {
120
- name: 'clien_research',
121
- description: 'Run a full product-research pass on an idea. Returns a markdown report plus ' +
122
- 'structured report_data (hypotheses, competitors[], key findings). Typical wall time ' +
123
- '15-25 minutes; progress is streamed via notifications/progress. ' +
124
- 'First call opens a browser for one-time OAuth login; subsequent calls reuse the stored refresh token.',
125
- inputSchema: RESEARCH_INPUT_SCHEMA,
126
- },
127
- ],
65
+ tools: TOOLS.map((tool) => ({
66
+ name: tool.name,
67
+ description: tool.description,
68
+ inputSchema: tool.jsonSchema,
69
+ })),
128
70
  }));
129
71
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
130
- if (request.params.name !== 'clien_research') {
131
- throw new Error(`Unknown tool: ${request.params.name}`);
72
+ const toolName = request.params.name;
73
+ // Unknown-tool check happens BEFORE the try/catch so the MCP SDK
74
+ // converts a thrown Error into a JSON-RPC protocol error (per spec:
75
+ // unknown tool name is a protocol-level fault). Putting the check
76
+ // inside the try-catch would convert it to a tool-level `isError`
77
+ // response — the wrong shape, and a regression caught by
78
+ // server.test.ts > 'unknown tool name surfaces an error'.
79
+ const tool = TOOLS.find((t) => t.name === toolName);
80
+ if (!tool) {
81
+ throw new Error(`Unknown tool: ${toolName}`);
132
82
  }
133
83
  // Per MCP spec, `progressToken` may be a string OR a number, and `0` or
134
84
  // `''` are valid token values. A truthy check (`progressToken ?`) silently
@@ -154,7 +104,7 @@ export function createMcpServer() {
154
104
  : undefined;
155
105
  try {
156
106
  const session = await getSession();
157
- const result = await runResearch(request.params.arguments, {
107
+ const result = await tool.handler(request.params.arguments, {
158
108
  config,
159
109
  session,
160
110
  onProgress,
@@ -166,15 +116,21 @@ export function createMcpServer() {
166
116
  };
167
117
  }
168
118
  catch (err) {
169
- const message = err instanceof ResearchToolError
119
+ const message = err instanceof ToolError
170
120
  ? err.userMessage
171
121
  : err instanceof Error
172
122
  ? err.message
173
123
  : String(err);
124
+ // Preserve the job_id on error responses so the host AI can pivot to
125
+ // `clien_research_status` (or surface the id to the user) without
126
+ // having to parse it out of the human-readable error text. Tool error
127
+ // classes carry a `jobId` field when one is known.
128
+ const errorJobId = err instanceof ToolError ? err.jobId : undefined;
174
129
  log('tool error:', message);
175
130
  return {
176
131
  isError: true,
177
132
  content: [{ type: 'text', text: message }],
133
+ ...(errorJobId !== undefined ? { _meta: { job_id: errorJobId } } : {}),
178
134
  };
179
135
  }
180
136
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared base class for tool-level errors.
3
+ *
4
+ * Every registry tool signals a user-recoverable failure by throwing a
5
+ * subclass of `ToolError`. The server's `CallTool` catch block maps any
6
+ * `ToolError` to a tool-level `{ isError: true, content, _meta.job_id? }`
7
+ * response (as opposed to a JSON-RPC protocol error). Carrying `userMessage`
8
+ * and the optional `jobId` on a single base means the dispatch needs exactly
9
+ * one `instanceof ToolError` check regardless of how many tools the registry
10
+ * holds — see `src/server.ts`.
11
+ */
12
+ export class ToolError extends Error {
13
+ userMessage;
14
+ jobId;
15
+ constructor(userMessage, opts = {}) {
16
+ super(userMessage);
17
+ this.userMessage = userMessage;
18
+ this.jobId = opts.jobId;
19
+ if (opts.cause !== undefined)
20
+ this.cause = opts.cause;
21
+ }
22
+ }
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/tools/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,WAAW,CAAQ;IACnB,KAAK,CAAS;IACvB,YAAY,WAAmB,EAAE,OAA4C,EAAE;QAC7E,KAAK,CAAC,WAAW,CAAC,CAAA;QAClB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACvB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAG,IAA4B,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IAChF,CAAC;CACF"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * `interview_persona` — stateful, full-lifecycle interview tool over the
3
+ * interview REST API (FUL-17 lifecycle + FUL-43 conversational turn). Lets an
4
+ * agent drive a real multi-turn interview with a persona: start it, exchange
5
+ * turns (each returns the persona's FULL reply), read the transcript, and
6
+ * complete/reopen — reusing the same lifecycle as the web app (greeting seed,
7
+ * auto-summary on message thresholds, message-count maintenance), so an
8
+ * MCP-driven interview is indistinguishable from a web one in the DB.
9
+ *
10
+ * Like `personas.ts` / `projects.ts` this is a thin bearer-auth HTTP client: it
11
+ * reuses the shared `apiCall` from `research.ts` (401→refresh→retry, per-endpoint
12
+ * timeouts). No new HTTP machinery.
13
+ *
14
+ * One tool, many actions (an `action` discriminator on a FLAT object schema —
15
+ * the registry type and the schema-contract test both assume a `z.ZodObject`, so
16
+ * a `discriminatedUnion` is off the table). Per-action required fields are
17
+ * enforced in the handler, not via `.superRefine` (which would yield a
18
+ * `ZodEffects`, not a `ZodObject`).
19
+ *
20
+ * Contract (all bearer-authenticated, ownership derived from the parent project):
21
+ * - start → POST /api/interviews { projectId, personaId, withFirstMessage:true, ephemeral } → { interview, personaName, firstMessageContent }
22
+ * - turn → POST /api/interviews/{id}/turn { content } → { reply, message, userMessage, messageCount }
23
+ * - transcript → GET /api/interviews/{id} → { interview }
24
+ * - complete → PATCH /api/interviews/{id} { status:'completed' } → { interview }
25
+ * - reopen → PATCH /api/interviews/{id} { status:'active' } → { interview }
26
+ *
27
+ * The backend stamps `source='mcp'` automatically for bearer callers (FUL-21);
28
+ * `ephemeral: true` on `start` sets `include_in_insights=false`.
29
+ *
30
+ * Scope note: a `turn` generates a real AI reply, metered today only by the
31
+ * monthly usage limit. Per-turn credit debiting is deferred to FUL-33 (the turn
32
+ * endpoint and the generalized per-tool spend RPC / cost table aren't built yet).
33
+ */
34
+ import { z } from 'zod';
35
+ import { ToolError } from './errors.js';
36
+ import { apiCall, categorizeFetchError, truncate, } from './research.js';
37
+ const INTERVIEW_FETCH_TIMEOUT_MS = 60_000;
38
+ /** Upper bound on a turn's question — matches the server's `MESSAGE_CONTENT_MAX`. */
39
+ const CONTENT_MAX = 10_000;
40
+ export const InterviewPersonaInputSchema = z.object({
41
+ action: z
42
+ .enum(['start', 'turn', 'transcript', 'complete', 'reopen'])
43
+ .describe('What to do this call. Drive a full interview as a loop: `start` once to open an ' +
44
+ 'interview (seeds the persona\'s greeting), then `turn` repeatedly — send a question, ' +
45
+ 'read the persona\'s full reply, decide the next question. `transcript` re-reads the ' +
46
+ 'whole conversation. `complete` closes the interview when you\'ve learned enough ' +
47
+ '(5-12 turns is typical); `reopen` resumes a completed one.'),
48
+ projectId: z
49
+ .string()
50
+ .uuid()
51
+ .optional()
52
+ .describe('action=start: UUID of the project the interview belongs to. Get it from `list_projects` ' +
53
+ 'or `create_project`. You are 404\'d if you do not own it.'),
54
+ personaId: z
55
+ .string()
56
+ .uuid()
57
+ .optional()
58
+ .describe('action=start: UUID of the persona to interview (must belong to `projectId`). Get it from ' +
59
+ '`create_persona` (`_meta.persona.id`).'),
60
+ interviewId: z
61
+ .string()
62
+ .uuid()
63
+ .optional()
64
+ .describe('action=turn/transcript/complete/reopen: UUID of the interview to act on. Returned by ' +
65
+ '`start` in `_meta.interview.id`.'),
66
+ content: z
67
+ .string()
68
+ .trim()
69
+ .min(1, 'content must be non-empty')
70
+ .max(CONTENT_MAX, `content too long (max ${CONTENT_MAX} chars)`)
71
+ .optional()
72
+ .describe('action=turn: the next question to ask the persona. The persona\'s full reply comes back ' +
73
+ 'in the tool result text (and `_meta.reply`).'),
74
+ ephemeral: z
75
+ .boolean()
76
+ .optional()
77
+ .default(false)
78
+ .describe('action=start: set true for a scratch/throwaway interview that should NOT count toward the ' +
79
+ 'project\'s aggregate insights (sets include_in_insights=false). Leave false (the default) ' +
80
+ 'for a real interview you want reflected in project-level analysis.'),
81
+ });
82
+ export class InterviewToolError extends ToolError {
83
+ }
84
+ function formatZodError(error) {
85
+ return error.issues.map((i) => `${i.path.join('.') || 'input'}: ${i.message}`).join('; ');
86
+ }
87
+ function makeCtx(deps) {
88
+ return { config: deps.config, session: deps.session, fetch: deps.fetch ?? fetch };
89
+ }
90
+ /**
91
+ * Shared non-ok handling, mirroring `personas.ts`: 401 drops the cached session
92
+ * (so the next tool call rebuilds from disk) and surfaces a re-auth message;
93
+ * 404 means the interview/project isn't owned by the caller; 400 surfaces the
94
+ * server's validation detail; everything else surfaces a generic HTTP error.
95
+ */
96
+ async function throwForResponse(response, deps, action) {
97
+ const category = categorizeFetchError(response.status);
98
+ if (category === 'auth') {
99
+ if (response.refreshError?.kind !== 'transient')
100
+ deps.invalidateSession?.();
101
+ throw new InterviewToolError('Session expired. Re-run any Clien.ai tool to re-authenticate, then try again.');
102
+ }
103
+ if (response.status === 404) {
104
+ throw new InterviewToolError(`Could not ${action} — interview or project not found, or you don't own it. ` +
105
+ 'Confirm the ids (list_projects / the id from `start`).');
106
+ }
107
+ const text = await response.text().catch(() => '');
108
+ let detail = truncate(text, 200);
109
+ try {
110
+ const parsed = JSON.parse(text);
111
+ if (parsed?.error)
112
+ detail = `${parsed.error}${parsed.code ? ` (${parsed.code})` : ''}`;
113
+ }
114
+ catch {
115
+ // non-JSON body — fall back to the truncated raw text
116
+ }
117
+ if (response.status === 400) {
118
+ throw new InterviewToolError(`Could not ${action} — ${detail}`);
119
+ }
120
+ throw new InterviewToolError(`Failed to ${action} (HTTP ${response.status}): ${detail}`);
121
+ }
122
+ /** Run one `apiCall`, converting timeout/abort into a clear tool error. */
123
+ async function call(ctx, method, path, action, body) {
124
+ try {
125
+ return await apiCall(ctx, method, path, {
126
+ ...(body ? { body } : {}),
127
+ timeoutMs: INTERVIEW_FETCH_TIMEOUT_MS,
128
+ });
129
+ }
130
+ catch (err) {
131
+ const e = err;
132
+ if (e.name === 'AbortError' || e.name === 'TimeoutError') {
133
+ throw new InterviewToolError(`Request to ${action} timed out after ${INTERVIEW_FETCH_TIMEOUT_MS / 1000}s. ` +
134
+ 'It may or may not have been applied — fetch the transcript before retrying.');
135
+ }
136
+ throw err;
137
+ }
138
+ }
139
+ async function readJson(response) {
140
+ return (await response.json().catch(() => null));
141
+ }
142
+ function formatTranscript(interview) {
143
+ const messages = interview.messages ?? [];
144
+ const lines = messages.map((m) => {
145
+ const speaker = m.role === 'assistant' ? 'Persona' : 'Interviewer';
146
+ return `${speaker}: ${m.content ?? ''}`;
147
+ });
148
+ const header = `Interview ${interview.id ?? '?'} — status: ${interview.status ?? 'unknown'}, ` +
149
+ `${messages.length} message${messages.length === 1 ? '' : 's'}.`;
150
+ return lines.length > 0 ? `${header}\n\n${lines.join('\n\n')}` : header;
151
+ }
152
+ async function startInterview(input, ctx, deps) {
153
+ if (!input.projectId || !input.personaId) {
154
+ throw new InterviewToolError("action 'start' requires projectId and personaId.");
155
+ }
156
+ const response = await call(ctx, 'POST', '/api/interviews', 'start interview', {
157
+ projectId: input.projectId,
158
+ personaId: input.personaId,
159
+ withFirstMessage: true,
160
+ ephemeral: input.ephemeral,
161
+ });
162
+ if (!response.ok)
163
+ await throwForResponse(response, deps, 'start interview');
164
+ const json = await readJson(response);
165
+ const interview = json?.interview;
166
+ if (!interview?.id) {
167
+ throw new InterviewToolError('Interview creation returned no interview id; the operation may not have succeeded.');
168
+ }
169
+ const who = json?.personaName ? ` with ${json.personaName}` : '';
170
+ const greeting = json?.firstMessageContent ? `\nGreeting: ${json.firstMessageContent}` : '';
171
+ const insightsNote = input.ephemeral
172
+ ? ' Marked ephemeral — excluded from project insights.'
173
+ : '';
174
+ return {
175
+ content: [
176
+ {
177
+ type: 'text',
178
+ text: `Started interview ${interview.id}${who}.${insightsNote}${greeting}\n` +
179
+ "Ask questions with action='turn'; close it with action='complete' when done.",
180
+ },
181
+ ],
182
+ _meta: { interview, personaName: json?.personaName, firstMessageContent: json?.firstMessageContent },
183
+ };
184
+ }
185
+ async function sendTurn(input, ctx, deps) {
186
+ if (!input.interviewId || !input.content) {
187
+ throw new InterviewToolError("action 'turn' requires interviewId and content.");
188
+ }
189
+ const response = await call(ctx, 'POST', `/api/interviews/${input.interviewId}/turn`, 'send interview turn', { content: input.content });
190
+ if (!response.ok)
191
+ await throwForResponse(response, deps, 'send interview turn');
192
+ const json = await readJson(response);
193
+ const reply = json?.reply;
194
+ if (typeof reply !== 'string' || reply.length === 0) {
195
+ throw new InterviewToolError('Turn returned no persona reply; the turn may not have succeeded.');
196
+ }
197
+ return {
198
+ content: [{ type: 'text', text: reply }],
199
+ _meta: {
200
+ reply,
201
+ messageCount: json?.messageCount,
202
+ message: json?.message,
203
+ userMessage: json?.userMessage,
204
+ },
205
+ };
206
+ }
207
+ async function fetchTranscript(input, ctx, deps) {
208
+ if (!input.interviewId) {
209
+ throw new InterviewToolError("action 'transcript' requires interviewId.");
210
+ }
211
+ const response = await call(ctx, 'GET', `/api/interviews/${input.interviewId}`, 'fetch transcript');
212
+ if (!response.ok)
213
+ await throwForResponse(response, deps, 'fetch transcript');
214
+ const json = await readJson(response);
215
+ const interview = json?.interview;
216
+ if (!interview?.id) {
217
+ throw new InterviewToolError('Transcript fetch returned no interview.');
218
+ }
219
+ return {
220
+ content: [{ type: 'text', text: formatTranscript(interview) }],
221
+ _meta: { interview },
222
+ };
223
+ }
224
+ async function setStatus(input, ctx, deps, status) {
225
+ const action = status === 'completed' ? 'complete' : 'reopen';
226
+ if (!input.interviewId) {
227
+ throw new InterviewToolError(`action '${action}' requires interviewId.`);
228
+ }
229
+ const response = await call(ctx, 'PATCH', `/api/interviews/${input.interviewId}`, `${action} interview`, { status });
230
+ if (!response.ok)
231
+ await throwForResponse(response, deps, `${action} interview`);
232
+ const json = await readJson(response);
233
+ const interview = json?.interview;
234
+ if (!interview?.id) {
235
+ throw new InterviewToolError(`Could not ${action} interview — no interview returned.`);
236
+ }
237
+ const verb = status === 'completed' ? 'Completed' : 'Reopened';
238
+ return {
239
+ content: [{ type: 'text', text: `${verb} interview ${interview.id} (status: ${interview.status ?? status}).` }],
240
+ _meta: { interview },
241
+ };
242
+ }
243
+ export async function interviewPersona(input, deps) {
244
+ const parsed = InterviewPersonaInputSchema.safeParse(input);
245
+ if (!parsed.success) {
246
+ throw new InterviewToolError(`Invalid input — ${formatZodError(parsed.error)}`);
247
+ }
248
+ const ctx = makeCtx(deps);
249
+ const data = parsed.data;
250
+ switch (data.action) {
251
+ case 'start':
252
+ return startInterview(data, ctx, deps);
253
+ case 'turn':
254
+ return sendTurn(data, ctx, deps);
255
+ case 'transcript':
256
+ return fetchTranscript(data, ctx, deps);
257
+ case 'complete':
258
+ return setStatus(data, ctx, deps, 'completed');
259
+ case 'reopen':
260
+ return setStatus(data, ctx, deps, 'active');
261
+ }
262
+ }
263
+ //# sourceMappingURL=interview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interview.js","sourceRoot":"","sources":["../../src/tools/interview.ts"],"names":[],"mappings":"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"}