@harness-engineering/mcp-server 0.5.2 → 0.6.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.
@@ -1,6 +1,7 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { resolveProjectConfig } from './utils/config-resolver.js';
4
5
  import { validateToolDefinition, handleValidateProject } from './tools/validate.js';
5
6
  import { checkDependenciesDefinition, handleCheckDependencies } from './tools/architecture.js';
6
7
  import { checkDocsDefinition, handleCheckDocs, validateKnowledgeMapDefinition, handleValidateKnowledgeMap, } from './tools/docs.js';
@@ -25,6 +26,9 @@ import { queryGraphDefinition, handleQueryGraph, searchSimilarDefinition, handle
25
26
  import { getGraphResource, getEntitiesResource, getRelationshipsResource, } from './resources/graph.js';
26
27
  import { generateAgentDefinitionsDefinition, handleGenerateAgentDefinitions, } from './tools/agent-definitions.js';
27
28
  import { runSecurityScanDefinition, handleRunSecurityScan } from './tools/security.js';
29
+ import { manageRoadmapDefinition, handleManageRoadmap } from './tools/roadmap.js';
30
+ import { emitInteractionDefinition, handleEmitInteraction } from './tools/interaction.js';
31
+ import { runCodeReviewDefinition, handleRunCodeReview } from './tools/review-pipeline.js';
28
32
  const TOOL_DEFINITIONS = [
29
33
  validateToolDefinition,
30
34
  checkDependenciesDefinition,
@@ -63,6 +67,9 @@ const TOOL_DEFINITIONS = [
63
67
  updatePerfBaselinesDefinition,
64
68
  getCriticalPathsDefinition,
65
69
  listStreamsDefinition,
70
+ manageRoadmapDefinition,
71
+ emitInteractionDefinition,
72
+ runCodeReviewDefinition,
66
73
  ];
67
74
  const TOOL_HANDLERS = {
68
75
  validate_project: handleValidateProject,
@@ -102,6 +109,9 @@ const TOOL_HANDLERS = {
102
109
  update_perf_baselines: handleUpdatePerfBaselines,
103
110
  get_critical_paths: handleGetCriticalPaths,
104
111
  list_streams: handleListStreams,
112
+ manage_roadmap: handleManageRoadmap,
113
+ emit_interaction: handleEmitInteraction,
114
+ run_code_review: handleRunCodeReview,
105
115
  };
106
116
  const RESOURCE_DEFINITIONS = [
107
117
  {
@@ -171,6 +181,7 @@ export function getResourceDefinitions() {
171
181
  }
172
182
  export function createHarnessServer(projectRoot) {
173
183
  const resolvedRoot = projectRoot ?? process.cwd();
184
+ let sessionChecked = false;
174
185
  const server = new Server({ name: 'harness-engineering', version: '0.1.0' }, { capabilities: { tools: {}, resources: {} } });
175
186
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
176
187
  tools: TOOL_DEFINITIONS,
@@ -181,7 +192,43 @@ export function createHarnessServer(projectRoot) {
181
192
  if (!handler) {
182
193
  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
183
194
  }
184
- return handler(args ?? {});
195
+ const result = await handler(args ?? {});
196
+ // On first tool invocation per session, check for updates
197
+ if (!sessionChecked) {
198
+ sessionChecked = true;
199
+ try {
200
+ const { getUpdateNotification, isUpdateCheckEnabled, shouldRunCheck, readCheckState, spawnBackgroundCheck, VERSION, } = await import('@harness-engineering/core');
201
+ // Read updateCheckInterval from project config (if available)
202
+ let configInterval;
203
+ try {
204
+ const configResult = resolveProjectConfig(resolvedRoot);
205
+ if (configResult.ok) {
206
+ const raw = configResult.value.updateCheckInterval;
207
+ if (typeof raw === 'number' && Number.isInteger(raw) && raw >= 0) {
208
+ configInterval = raw;
209
+ }
210
+ }
211
+ }
212
+ catch {
213
+ // Config read failure is non-fatal for update checks
214
+ }
215
+ const DEFAULT_INTERVAL = 86_400_000; // 24 hours
216
+ if (isUpdateCheckEnabled(configInterval)) {
217
+ const state = readCheckState();
218
+ if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
219
+ spawnBackgroundCheck(VERSION);
220
+ }
221
+ const notification = getUpdateNotification(VERSION);
222
+ if (notification) {
223
+ result.content.push({ type: 'text', text: `\n---\n${notification}` });
224
+ }
225
+ }
226
+ }
227
+ catch {
228
+ // Graceful degradation — update check failures must never break tool responses
229
+ }
230
+ }
231
+ return result;
185
232
  });
186
233
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
187
234
  resources: RESOURCE_DEFINITIONS,
@@ -69,11 +69,11 @@ export declare function handleRunAgentTask(input: {
69
69
  type: "text";
70
70
  text: string;
71
71
  }[];
72
- isError?: undefined;
72
+ isError: boolean;
73
73
  } | {
74
74
  content: {
75
75
  type: "text";
76
76
  text: string;
77
77
  }[];
78
- isError: boolean;
78
+ isError?: undefined;
79
79
  }>;
@@ -1,4 +1,4 @@
1
- import * as path from 'path';
1
+ import { sanitizePath } from '../utils/sanitize-path.js';
2
2
  export const addComponentDefinition = {
3
3
  name: 'add_component',
4
4
  description: 'Add a component (layer, doc, or component type) to the project using the harness CLI',
@@ -16,8 +16,9 @@ export const addComponentDefinition = {
16
16
  required: ['path', 'type', 'name'],
17
17
  },
18
18
  };
19
+ const COMPONENT_NAME_REGEX = /^[a-z0-9][a-z0-9._-]{0,64}$/i;
19
20
  export async function handleAddComponent(input) {
20
- const projectPath = path.resolve(input.path);
21
+ const projectPath = sanitizePath(input.path);
21
22
  const ALLOWED_TYPES = new Set(['layer', 'doc', 'component']);
22
23
  if (!ALLOWED_TYPES.has(input.type)) {
23
24
  return {
@@ -30,6 +31,19 @@ export async function handleAddComponent(input) {
30
31
  isError: true,
31
32
  };
32
33
  }
34
+ if (!COMPONENT_NAME_REGEX.test(input.name)) {
35
+ return {
36
+ content: [
37
+ {
38
+ type: 'text',
39
+ text: JSON.stringify({
40
+ error: `Invalid component name: must match ${COMPONENT_NAME_REGEX} (lowercase alphanumeric, dots, hyphens, underscores, max 65 chars)`,
41
+ }),
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
33
47
  try {
34
48
  const { execFileSync } = await import('node:child_process');
35
49
  const output = execFileSync('npx', ['harness', 'add', input.type, input.name], {
@@ -67,8 +81,22 @@ export const runAgentTaskDefinition = {
67
81
  required: ['task'],
68
82
  },
69
83
  };
84
+ const ALLOWED_AGENT_TASKS = new Set(['review', 'doc-review', 'test-review']);
70
85
  export async function handleRunAgentTask(input) {
71
- const projectPath = input.path ? path.resolve(input.path) : process.cwd();
86
+ if (!ALLOWED_AGENT_TASKS.has(input.task)) {
87
+ return {
88
+ content: [
89
+ {
90
+ type: 'text',
91
+ text: JSON.stringify({
92
+ error: `Invalid task: "${input.task}". Allowed tasks: ${[...ALLOWED_AGENT_TASKS].join(', ')}`,
93
+ }),
94
+ },
95
+ ],
96
+ isError: true,
97
+ };
98
+ }
99
+ const projectPath = input.path ? sanitizePath(input.path) : process.cwd();
72
100
  try {
73
101
  const { execFileSync } = await import('node:child_process');
74
102
  const output = execFileSync('npx', ['harness', 'agent', 'run', input.task], {
@@ -1,6 +1,6 @@
1
- import * as path from 'path';
2
1
  import { resultToMcpResponse } from '../utils/result-adapter.js';
3
2
  import { resolveProjectConfig } from '../utils/config-resolver.js';
3
+ import { sanitizePath } from '../utils/sanitize-path.js';
4
4
  export const checkDependenciesDefinition = {
5
5
  name: 'check_dependencies',
6
6
  description: 'Validate layer boundaries and detect circular dependencies',
@@ -13,7 +13,21 @@ export const checkDependenciesDefinition = {
13
13
  },
14
14
  };
15
15
  export async function handleCheckDependencies(input) {
16
- const projectPath = path.resolve(input.path);
16
+ let projectPath;
17
+ try {
18
+ projectPath = sanitizePath(input.path);
19
+ }
20
+ catch (error) {
21
+ return {
22
+ content: [
23
+ {
24
+ type: 'text',
25
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
26
+ },
27
+ ],
28
+ isError: true,
29
+ };
30
+ }
17
31
  const configResult = resolveProjectConfig(projectPath);
18
32
  if (!configResult.ok)
19
33
  return resultToMcpResponse(configResult);
@@ -1,4 +1,5 @@
1
1
  import * as path from 'path';
2
+ import { sanitizePath } from '../utils/sanitize-path.js';
2
3
  export const validateCrossCheckDefinition = {
3
4
  name: 'validate_cross_check',
4
5
  description: 'Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness',
@@ -19,13 +20,41 @@ export const validateCrossCheckDefinition = {
19
20
  },
20
21
  };
21
22
  export async function handleValidateCrossCheck(input) {
22
- const projectPath = path.resolve(input.path);
23
+ let projectPath;
24
+ try {
25
+ projectPath = sanitizePath(input.path);
26
+ }
27
+ catch (error) {
28
+ return {
29
+ content: [
30
+ {
31
+ type: 'text',
32
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
33
+ },
34
+ ],
35
+ isError: true,
36
+ };
37
+ }
23
38
  try {
24
39
  const { runCrossCheck } = await import('@harness-engineering/cli');
40
+ const specsDir = path.resolve(projectPath, input.specsDir ?? 'docs/specs');
41
+ if (!specsDir.startsWith(projectPath)) {
42
+ return {
43
+ content: [{ type: 'text', text: 'Error: specsDir escapes project root' }],
44
+ isError: true,
45
+ };
46
+ }
47
+ const plansDir = path.resolve(projectPath, input.plansDir ?? 'docs/plans');
48
+ if (!plansDir.startsWith(projectPath)) {
49
+ return {
50
+ content: [{ type: 'text', text: 'Error: plansDir escapes project root' }],
51
+ isError: true,
52
+ };
53
+ }
25
54
  const result = await runCrossCheck({
26
55
  projectPath,
27
- specsDir: path.resolve(projectPath, input.specsDir ?? 'docs/specs'),
28
- plansDir: path.resolve(projectPath, input.plansDir ?? 'docs/plans'),
56
+ specsDir,
57
+ plansDir,
29
58
  });
30
59
  if (result.ok) {
31
60
  return { content: [{ type: 'text', text: JSON.stringify(result.value) }] };
@@ -1,5 +1,6 @@
1
1
  import * as path from 'path';
2
2
  import { resultToMcpResponse } from '../utils/result-adapter.js';
3
+ import { sanitizePath } from '../utils/sanitize-path.js';
3
4
  export const checkDocsDefinition = {
4
5
  name: 'check_docs',
5
6
  description: 'Analyze documentation coverage',
@@ -18,7 +19,8 @@ export async function handleCheckDocs(input) {
18
19
  const domain = input.domain ?? 'src';
19
20
  // Attempt to load graph for enhanced coverage analysis
20
21
  const { loadGraphStore } = await import('../utils/graph-loader.js');
21
- const store = await loadGraphStore(path.resolve(input.path));
22
+ const projectPath = sanitizePath(input.path);
23
+ const store = await loadGraphStore(projectPath);
22
24
  let graphCoverage;
23
25
  if (store) {
24
26
  const { Assembler } = await import('@harness-engineering/graph');
@@ -31,8 +33,8 @@ export async function handleCheckDocs(input) {
31
33
  };
32
34
  }
33
35
  const result = await checkDocCoverage(domain, {
34
- sourceDir: path.resolve(input.path, 'src'),
35
- docsDir: path.resolve(input.path, 'docs'),
36
+ sourceDir: path.resolve(projectPath, 'src'),
37
+ docsDir: path.resolve(projectPath, 'docs'),
36
38
  graphCoverage,
37
39
  });
38
40
  return resultToMcpResponse(result);
@@ -63,7 +65,7 @@ export const validateKnowledgeMapDefinition = {
63
65
  export async function handleValidateKnowledgeMap(input) {
64
66
  try {
65
67
  const { validateKnowledgeMap } = await import('@harness-engineering/core');
66
- const result = await validateKnowledgeMap(path.resolve(input.path));
68
+ const result = await validateKnowledgeMap(sanitizePath(input.path));
67
69
  return resultToMcpResponse(result);
68
70
  }
69
71
  catch (error) {
@@ -35,6 +35,14 @@ export declare const applyFixesDefinition: {
35
35
  type: string;
36
36
  description: string;
37
37
  };
38
+ fixTypes: {
39
+ type: string;
40
+ items: {
41
+ type: string;
42
+ enum: string[];
43
+ };
44
+ description: string;
45
+ };
38
46
  };
39
47
  required: string[];
40
48
  };
@@ -42,4 +50,5 @@ export declare const applyFixesDefinition: {
42
50
  export declare function handleApplyFixes(input: {
43
51
  path: string;
44
52
  dryRun?: boolean;
53
+ fixTypes?: string[];
45
54
  }): Promise<import("../utils/result-adapter.js").McpToolResponse>;
@@ -1,6 +1,6 @@
1
- import * as path from 'path';
2
1
  import { Ok } from '@harness-engineering/core';
3
2
  import { resultToMcpResponse } from '../utils/result-adapter.js';
3
+ import { sanitizePath } from '../utils/sanitize-path.js';
4
4
  async function loadEntropyGraphOptions(projectPath) {
5
5
  const { loadGraphStore } = await import('../utils/graph-loader.js');
6
6
  const store = await loadGraphStore(projectPath);
@@ -51,14 +51,14 @@ export async function handleDetectEntropy(input) {
51
51
  const { EntropyAnalyzer } = await import('@harness-engineering/core');
52
52
  const typeFilter = input.type ?? 'all';
53
53
  const analyzer = new EntropyAnalyzer({
54
- rootDir: path.resolve(input.path),
54
+ rootDir: sanitizePath(input.path),
55
55
  analyze: {
56
56
  drift: typeFilter === 'all' || typeFilter === 'drift',
57
57
  deadCode: typeFilter === 'all' || typeFilter === 'dead-code',
58
58
  patterns: typeFilter === 'all' || typeFilter === 'patterns',
59
59
  },
60
60
  });
61
- const graphOptions = await loadEntropyGraphOptions(path.resolve(input.path));
61
+ const graphOptions = await loadEntropyGraphOptions(sanitizePath(input.path));
62
62
  const result = await analyzer.analyze(graphOptions);
63
63
  return resultToMcpResponse(result);
64
64
  }
@@ -82,6 +82,22 @@ export const applyFixesDefinition = {
82
82
  properties: {
83
83
  path: { type: 'string', description: 'Path to project root' },
84
84
  dryRun: { type: 'boolean', description: 'Preview fixes without applying' },
85
+ fixTypes: {
86
+ type: 'array',
87
+ items: {
88
+ type: 'string',
89
+ enum: [
90
+ 'unused-imports',
91
+ 'dead-files',
92
+ 'dead-exports',
93
+ 'commented-code',
94
+ 'orphaned-deps',
95
+ 'forbidden-import-replacement',
96
+ 'import-ordering',
97
+ ],
98
+ },
99
+ description: 'Specific fix types to apply (default: all safe types)',
100
+ },
85
101
  },
86
102
  required: ['path'],
87
103
  },
@@ -90,16 +106,19 @@ export async function handleApplyFixes(input) {
90
106
  try {
91
107
  const { EntropyAnalyzer, createFixes, applyFixes, generateSuggestions } = await import('@harness-engineering/core');
92
108
  const analyzer = new EntropyAnalyzer({
93
- rootDir: path.resolve(input.path),
109
+ rootDir: sanitizePath(input.path),
94
110
  analyze: { drift: true, deadCode: true, patterns: true },
95
111
  });
96
- const graphOptions = await loadEntropyGraphOptions(path.resolve(input.path));
112
+ const graphOptions = await loadEntropyGraphOptions(sanitizePath(input.path));
97
113
  const analysisResult = await analyzer.analyze(graphOptions);
98
114
  if (!analysisResult.ok)
99
115
  return resultToMcpResponse(analysisResult);
100
116
  const report = analysisResult.value;
101
117
  const deadCode = report.deadCode;
102
- const fixes = deadCode ? createFixes(deadCode, {}) : [];
118
+ const fixTypesConfig = input.fixTypes
119
+ ? { fixTypes: input.fixTypes }
120
+ : undefined;
121
+ const fixes = deadCode ? createFixes(deadCode, fixTypesConfig) : [];
103
122
  const suggestions = generateSuggestions(report.deadCode, report.drift, report.patterns);
104
123
  if (input.dryRun) {
105
124
  return { content: [{ type: 'text', text: JSON.stringify({ fixes, suggestions }) }] };
@@ -1,5 +1,5 @@
1
- import * as path from 'path';
2
1
  import { resultToMcpResponse } from '../utils/result-adapter.js';
2
+ import { sanitizePath } from '../utils/sanitize-path.js';
3
3
  // ============ create_self_review ============
4
4
  export const createSelfReviewDefinition = {
5
5
  name: 'create_self_review',
@@ -33,8 +33,9 @@ export async function handleCreateSelfReview(input) {
33
33
  if (!parseResult.ok) {
34
34
  return resultToMcpResponse(parseResult);
35
35
  }
36
+ const projectPath = sanitizePath(input.path);
36
37
  const config = {
37
- rootDir: path.resolve(input.path),
38
+ rootDir: projectPath,
38
39
  harness: {
39
40
  context: true,
40
41
  constraints: true,
@@ -49,7 +50,7 @@ export async function handleCreateSelfReview(input) {
49
50
  };
50
51
  // Attempt to load graph for enhanced review
51
52
  const { loadGraphStore } = await import('../utils/graph-loader.js');
52
- const store = await loadGraphStore(path.resolve(input.path));
53
+ const store = await loadGraphStore(projectPath);
53
54
  let graphData;
54
55
  if (store) {
55
56
  const { GraphFeedbackAdapter } = await import('@harness-engineering/graph');
@@ -135,7 +136,7 @@ export async function handleAnalyzeDiff(input) {
135
136
  if (input.path) {
136
137
  try {
137
138
  const { loadGraphStore } = await import('../utils/graph-loader.js');
138
- const store = await loadGraphStore(path.resolve(input.path));
139
+ const store = await loadGraphStore(sanitizePath(input.path));
139
140
  if (store) {
140
141
  const { GraphFeedbackAdapter } = await import('@harness-engineering/graph');
141
142
  const adapter = new GraphFeedbackAdapter(store);
@@ -207,7 +208,7 @@ export async function handleRequestPeerReview(input) {
207
208
  // Attempt to load graph for enhanced context
208
209
  try {
209
210
  const { loadGraphStore } = await import('../utils/graph-loader.js');
210
- const store = await loadGraphStore(path.resolve(input.path));
211
+ const store = await loadGraphStore(sanitizePath(input.path));
211
212
  if (store) {
212
213
  const { GraphFeedbackAdapter } = await import('@harness-engineering/graph');
213
214
  const adapter = new GraphFeedbackAdapter(store);
@@ -1,6 +1,7 @@
1
1
  import { Ok, Err } from '@harness-engineering/core';
2
2
  import { generateSlashCommands } from '@harness-engineering/cli';
3
3
  import { resultToMcpResponse } from '../utils/result-adapter.js';
4
+ import { sanitizePath } from '../utils/sanitize-path.js';
4
5
  export const generateSlashCommandsDefinition = {
5
6
  name: 'generate_slash_commands',
6
7
  description: 'Generate native slash commands for Claude Code and Gemini CLI from harness skill metadata',
@@ -43,8 +44,8 @@ export async function handleGenerateSlashCommands(input) {
43
44
  platforms,
44
45
  global: input.global ?? false,
45
46
  includeGlobal: input.includeGlobal ?? false,
46
- output: input.output,
47
- skillsDir: input.skillsDir ?? '',
47
+ output: input.output ? sanitizePath(input.output) : undefined,
48
+ skillsDir: input.skillsDir ? sanitizePath(input.skillsDir) : '',
48
49
  dryRun: input.dryRun ?? false,
49
50
  yes: true,
50
51
  });
@@ -1,13 +1,6 @@
1
1
  import * as path from 'path';
2
2
  import { loadGraphStore } from '../utils/graph-loader.js';
3
- // ── Shared helper ────────────────────────────────────────────────────
4
- function sanitizePath(inputPath) {
5
- const resolved = path.resolve(inputPath);
6
- if (resolved === '/' || resolved === path.parse(resolved).root) {
7
- throw new Error('Invalid project path: cannot use filesystem root');
8
- }
9
- return resolved;
10
- }
3
+ import { sanitizePath } from '../utils/sanitize-path.js';
11
4
  function graphNotFoundError() {
12
5
  return {
13
6
  content: [
@@ -1,6 +1,7 @@
1
1
  import * as path from 'path';
2
2
  import { resultToMcpResponse } from '../utils/result-adapter.js';
3
3
  import { resolveTemplatesDir } from '../utils/paths.js';
4
+ import { sanitizePath } from '../utils/sanitize-path.js';
4
5
  export const initProjectDefinition = {
5
6
  name: 'init_project',
6
7
  description: 'Scaffold a new harness engineering project from a template',
@@ -29,14 +30,15 @@ export async function handleInitProject(input) {
29
30
  const resolveResult = engine.resolveTemplate(level, input.framework);
30
31
  if (!resolveResult.ok)
31
32
  return resultToMcpResponse(resolveResult);
33
+ const safePath = sanitizePath(input.path);
32
34
  const renderResult = engine.render(resolveResult.value, {
33
- projectName: input.name ?? path.basename(input.path),
35
+ projectName: input.name ?? path.basename(safePath),
34
36
  level,
35
37
  framework: input.framework,
36
38
  });
37
39
  if (!renderResult.ok)
38
40
  return resultToMcpResponse(renderResult);
39
- const writeResult = engine.write(renderResult.value, path.resolve(input.path), {
41
+ const writeResult = engine.write(renderResult.value, safePath, {
40
42
  overwrite: false,
41
43
  });
42
44
  return resultToMcpResponse(writeResult);
@@ -0,0 +1,130 @@
1
+ export declare const emitInteractionDefinition: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ path: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ type: {
12
+ type: string;
13
+ enum: string[];
14
+ description: string;
15
+ };
16
+ stream: {
17
+ type: string;
18
+ description: string;
19
+ };
20
+ question: {
21
+ type: string;
22
+ description: string;
23
+ properties: {
24
+ text: {
25
+ type: string;
26
+ description: string;
27
+ };
28
+ options: {
29
+ type: string;
30
+ items: {
31
+ type: string;
32
+ };
33
+ description: string;
34
+ };
35
+ default: {
36
+ type: string;
37
+ description: string;
38
+ };
39
+ };
40
+ required: string[];
41
+ };
42
+ confirmation: {
43
+ type: string;
44
+ description: string;
45
+ properties: {
46
+ text: {
47
+ type: string;
48
+ description: string;
49
+ };
50
+ context: {
51
+ type: string;
52
+ description: string;
53
+ };
54
+ };
55
+ required: string[];
56
+ };
57
+ transition: {
58
+ type: string;
59
+ description: string;
60
+ properties: {
61
+ completedPhase: {
62
+ type: string;
63
+ description: string;
64
+ };
65
+ suggestedNext: {
66
+ type: string;
67
+ description: string;
68
+ };
69
+ reason: {
70
+ type: string;
71
+ description: string;
72
+ };
73
+ artifacts: {
74
+ type: string;
75
+ items: {
76
+ type: string;
77
+ };
78
+ description: string;
79
+ };
80
+ requiresConfirmation: {
81
+ type: string;
82
+ description: string;
83
+ };
84
+ summary: {
85
+ type: string;
86
+ description: string;
87
+ };
88
+ };
89
+ required: string[];
90
+ };
91
+ };
92
+ required: string[];
93
+ };
94
+ };
95
+ interface EmitInteractionInput {
96
+ path: string;
97
+ type: 'question' | 'confirmation' | 'transition';
98
+ stream?: string;
99
+ question?: {
100
+ text: string;
101
+ options?: string[];
102
+ default?: string;
103
+ };
104
+ confirmation?: {
105
+ text: string;
106
+ context: string;
107
+ };
108
+ transition?: {
109
+ completedPhase: string;
110
+ suggestedNext: string;
111
+ reason: string;
112
+ artifacts: string[];
113
+ requiresConfirmation: boolean;
114
+ summary: string;
115
+ };
116
+ }
117
+ export declare function handleEmitInteraction(input: EmitInteractionInput): Promise<{
118
+ content: {
119
+ type: "text";
120
+ text: string;
121
+ }[];
122
+ isError: boolean;
123
+ } | {
124
+ content: {
125
+ type: "text";
126
+ text: string;
127
+ }[];
128
+ isError?: undefined;
129
+ }>;
130
+ export {};