@tienne/gestalt 0.12.2 → 0.13.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 (210) hide show
  1. package/CLAUDE.md +34 -428
  2. package/README.ko.md +51 -1
  3. package/README.md +51 -1
  4. package/dist/package.json +5 -1
  5. package/dist/schemas/gestalt.schema.json +41 -3
  6. package/dist/skills/interview/SKILL.md +18 -1
  7. package/dist/src/agent/passthrough-generator.d.ts.map +1 -1
  8. package/dist/src/agent/passthrough-generator.js +3 -1
  9. package/dist/src/agent/passthrough-generator.js.map +1 -1
  10. package/dist/src/agent/role-agent-registry.d.ts.map +1 -1
  11. package/dist/src/agent/role-agent-registry.js.map +1 -1
  12. package/dist/src/agent/role-match-engine.d.ts.map +1 -1
  13. package/dist/src/agent/role-match-engine.js.map +1 -1
  14. package/dist/src/cli/commands/graph-visualize.d.ts.map +1 -1
  15. package/dist/src/cli/commands/graph-visualize.js +3 -1
  16. package/dist/src/cli/commands/graph-visualize.js.map +1 -1
  17. package/dist/src/cli/commands/init.d.ts.map +1 -1
  18. package/dist/src/cli/commands/init.js.map +1 -1
  19. package/dist/src/cli/commands/interview.d.ts.map +1 -1
  20. package/dist/src/cli/commands/interview.js +2 -2
  21. package/dist/src/cli/commands/interview.js.map +1 -1
  22. package/dist/src/cli/commands/spec.d.ts.map +1 -1
  23. package/dist/src/cli/commands/spec.js +2 -2
  24. package/dist/src/cli/commands/spec.js.map +1 -1
  25. package/dist/src/cli/commands/status.d.ts.map +1 -1
  26. package/dist/src/cli/commands/status.js.map +1 -1
  27. package/dist/src/cli/index.d.ts.map +1 -1
  28. package/dist/src/cli/index.js.map +1 -1
  29. package/dist/src/code-graph/engine.d.ts.map +1 -1
  30. package/dist/src/code-graph/engine.js +16 -13
  31. package/dist/src/code-graph/engine.js.map +1 -1
  32. package/dist/src/code-graph/plugins/go.d.ts.map +1 -1
  33. package/dist/src/code-graph/plugins/go.js +28 -4
  34. package/dist/src/code-graph/plugins/go.js.map +1 -1
  35. package/dist/src/code-graph/plugins/java.d.ts.map +1 -1
  36. package/dist/src/code-graph/plugins/java.js +63 -11
  37. package/dist/src/code-graph/plugins/java.js.map +1 -1
  38. package/dist/src/code-graph/plugins/kotlin.d.ts.map +1 -1
  39. package/dist/src/code-graph/plugins/kotlin.js +50 -7
  40. package/dist/src/code-graph/plugins/kotlin.js.map +1 -1
  41. package/dist/src/code-graph/plugins/objc.d.ts.map +1 -1
  42. package/dist/src/code-graph/plugins/objc.js +70 -12
  43. package/dist/src/code-graph/plugins/objc.js.map +1 -1
  44. package/dist/src/code-graph/plugins/python.d.ts.map +1 -1
  45. package/dist/src/code-graph/plugins/python.js +28 -4
  46. package/dist/src/code-graph/plugins/python.js.map +1 -1
  47. package/dist/src/code-graph/plugins/rust.d.ts.map +1 -1
  48. package/dist/src/code-graph/plugins/rust.js +72 -12
  49. package/dist/src/code-graph/plugins/rust.js.map +1 -1
  50. package/dist/src/code-graph/plugins/swift.d.ts.map +1 -1
  51. package/dist/src/code-graph/plugins/swift.js +62 -8
  52. package/dist/src/code-graph/plugins/swift.js.map +1 -1
  53. package/dist/src/code-graph/plugins/typescript.d.ts.map +1 -1
  54. package/dist/src/code-graph/plugins/typescript.js +2 -2
  55. package/dist/src/code-graph/plugins/typescript.js.map +1 -1
  56. package/dist/src/code-graph/providers/local-embedding.js +1 -1
  57. package/dist/src/code-graph/providers/local-embedding.js.map +1 -1
  58. package/dist/src/code-graph/storage.d.ts.map +1 -1
  59. package/dist/src/code-graph/storage.js +1 -3
  60. package/dist/src/code-graph/storage.js.map +1 -1
  61. package/dist/src/core/config.d.ts +138 -1
  62. package/dist/src/core/config.d.ts.map +1 -1
  63. package/dist/src/core/config.js +36 -3
  64. package/dist/src/core/config.js.map +1 -1
  65. package/dist/src/core/constants.d.ts.map +1 -1
  66. package/dist/src/core/constants.js +6 -6
  67. package/dist/src/core/constants.js.map +1 -1
  68. package/dist/src/core/result.d.ts.map +1 -1
  69. package/dist/src/core/result.js.map +1 -1
  70. package/dist/src/core/types.d.ts.map +1 -1
  71. package/dist/src/execute/audit-engine.d.ts.map +1 -1
  72. package/dist/src/execute/audit-engine.js +1 -3
  73. package/dist/src/execute/audit-engine.js.map +1 -1
  74. package/dist/src/execute/dag-validator.d.ts.map +1 -1
  75. package/dist/src/execute/dag-validator.js.map +1 -1
  76. package/dist/src/execute/parallel-groups.d.ts.map +1 -1
  77. package/dist/src/execute/parallel-groups.js.map +1 -1
  78. package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
  79. package/dist/src/execute/passthrough-engine.js +34 -14
  80. package/dist/src/execute/passthrough-engine.js.map +1 -1
  81. package/dist/src/execute/prompts.d.ts.map +1 -1
  82. package/dist/src/execute/prompts.js +2 -4
  83. package/dist/src/execute/prompts.js.map +1 -1
  84. package/dist/src/execute/repository.d.ts.map +1 -1
  85. package/dist/src/execute/repository.js.map +1 -1
  86. package/dist/src/execute/session.d.ts.map +1 -1
  87. package/dist/src/execute/session.js +2 -1
  88. package/dist/src/execute/session.js.map +1 -1
  89. package/dist/src/execute/termination-detector.d.ts.map +1 -1
  90. package/dist/src/execute/termination-detector.js +3 -2
  91. package/dist/src/execute/termination-detector.js.map +1 -1
  92. package/dist/src/gestalt/analyzer.d.ts.map +1 -1
  93. package/dist/src/gestalt/analyzer.js +1 -2
  94. package/dist/src/gestalt/analyzer.js.map +1 -1
  95. package/dist/src/gestalt/principles.d.ts.map +1 -1
  96. package/dist/src/gestalt/principles.js +1 -3
  97. package/dist/src/gestalt/principles.js.map +1 -1
  98. package/dist/src/interview/engine.d.ts.map +1 -1
  99. package/dist/src/interview/engine.js +4 -1
  100. package/dist/src/interview/engine.js.map +1 -1
  101. package/dist/src/interview/mini-interview-engine.d.ts.map +1 -1
  102. package/dist/src/interview/mini-interview-engine.js +1 -3
  103. package/dist/src/interview/mini-interview-engine.js.map +1 -1
  104. package/dist/src/interview/passthrough-engine.d.ts.map +1 -1
  105. package/dist/src/interview/passthrough-engine.js +7 -3
  106. package/dist/src/interview/passthrough-engine.js.map +1 -1
  107. package/dist/src/interview/repository.d.ts.map +1 -1
  108. package/dist/src/interview/repository.js.map +1 -1
  109. package/dist/src/interview/resolution.js +1 -3
  110. package/dist/src/interview/resolution.js.map +1 -1
  111. package/dist/src/interview/session.d.ts.map +1 -1
  112. package/dist/src/interview/session.js.map +1 -1
  113. package/dist/src/llm/adapter.d.ts.map +1 -1
  114. package/dist/src/llm/adapter.js.map +1 -1
  115. package/dist/src/llm/factory.d.ts +24 -0
  116. package/dist/src/llm/factory.d.ts.map +1 -0
  117. package/dist/src/llm/factory.js +75 -0
  118. package/dist/src/llm/factory.js.map +1 -0
  119. package/dist/src/llm/openai-adapter.d.ts +1 -1
  120. package/dist/src/llm/openai-adapter.d.ts.map +1 -1
  121. package/dist/src/llm/openai-adapter.js +2 -2
  122. package/dist/src/llm/openai-adapter.js.map +1 -1
  123. package/dist/src/mcp/schemas.d.ts.map +1 -1
  124. package/dist/src/mcp/schemas.js +224 -88
  125. package/dist/src/mcp/schemas.js.map +1 -1
  126. package/dist/src/mcp/server.d.ts.map +1 -1
  127. package/dist/src/mcp/server.js +289 -109
  128. package/dist/src/mcp/server.js.map +1 -1
  129. package/dist/src/mcp/tools/benchmark-passthrough.js +1 -1
  130. package/dist/src/mcp/tools/benchmark-passthrough.js.map +1 -1
  131. package/dist/src/mcp/tools/create-agent-passthrough.d.ts.map +1 -1
  132. package/dist/src/mcp/tools/create-agent-passthrough.js.map +1 -1
  133. package/dist/src/mcp/tools/execute-passthrough.d.ts.map +1 -1
  134. package/dist/src/mcp/tools/execute-passthrough.js +39 -25
  135. package/dist/src/mcp/tools/execute-passthrough.js.map +1 -1
  136. package/dist/src/mcp/tools/graph-visualize-passthrough.d.ts.map +1 -1
  137. package/dist/src/mcp/tools/graph-visualize-passthrough.js.map +1 -1
  138. package/dist/src/mcp/tools/interview-passthrough.d.ts.map +1 -1
  139. package/dist/src/mcp/tools/interview-passthrough.js +7 -3
  140. package/dist/src/mcp/tools/interview-passthrough.js.map +1 -1
  141. package/dist/src/mcp/tools/interview.d.ts.map +1 -1
  142. package/dist/src/mcp/tools/interview.js +4 -2
  143. package/dist/src/mcp/tools/interview.js.map +1 -1
  144. package/dist/src/mcp/tools/review-passthrough.js +1 -1
  145. package/dist/src/mcp/tools/review-passthrough.js.map +1 -1
  146. package/dist/src/mcp/tools/spec-passthrough.d.ts.map +1 -1
  147. package/dist/src/mcp/tools/spec-passthrough.js.map +1 -1
  148. package/dist/src/mcp/tools/spec.d.ts.map +1 -1
  149. package/dist/src/mcp/tools/spec.js.map +1 -1
  150. package/dist/src/mcp/tools/status.d.ts.map +1 -1
  151. package/dist/src/mcp/tools/status.js +1 -1
  152. package/dist/src/mcp/tools/status.js.map +1 -1
  153. package/dist/src/memory/memory-context-injector.d.ts.map +1 -1
  154. package/dist/src/memory/memory-context-injector.js +2 -6
  155. package/dist/src/memory/memory-context-injector.js.map +1 -1
  156. package/dist/src/memory/user-profile-store.d.ts.map +1 -1
  157. package/dist/src/memory/user-profile-store.js +1 -4
  158. package/dist/src/memory/user-profile-store.js.map +1 -1
  159. package/dist/src/recording/agg-converter.d.ts.map +1 -1
  160. package/dist/src/recording/agg-converter.js.map +1 -1
  161. package/dist/src/recording/agg-installer.d.ts.map +1 -1
  162. package/dist/src/recording/agg-installer.js.map +1 -1
  163. package/dist/src/recording/asciinema-installer.d.ts.map +1 -1
  164. package/dist/src/recording/asciinema-installer.js.map +1 -1
  165. package/dist/src/recording/asciinema-recorder.d.ts.map +1 -1
  166. package/dist/src/recording/asciinema-recorder.js.map +1 -1
  167. package/dist/src/recording/cast-generator.d.ts.map +1 -1
  168. package/dist/src/recording/cast-generator.js +38 -13
  169. package/dist/src/recording/cast-generator.js.map +1 -1
  170. package/dist/src/recording/filename-generator.d.ts.map +1 -1
  171. package/dist/src/recording/filename-generator.js +2 -2
  172. package/dist/src/recording/filename-generator.js.map +1 -1
  173. package/dist/src/recording/recording-orchestrator.d.ts.map +1 -1
  174. package/dist/src/recording/recording-orchestrator.js.map +1 -1
  175. package/dist/src/registry/base-registry.d.ts.map +1 -1
  176. package/dist/src/registry/base-registry.js.map +1 -1
  177. package/dist/src/resilience/lateral.d.ts.map +1 -1
  178. package/dist/src/resilience/lateral.js +6 -1
  179. package/dist/src/resilience/lateral.js.map +1 -1
  180. package/dist/src/resilience/prompts.d.ts.map +1 -1
  181. package/dist/src/resilience/prompts.js.map +1 -1
  182. package/dist/src/review/agent-matcher.d.ts.map +1 -1
  183. package/dist/src/review/agent-matcher.js.map +1 -1
  184. package/dist/src/review/context-collector.d.ts.map +1 -1
  185. package/dist/src/review/context-collector.js.map +1 -1
  186. package/dist/src/review/passthrough-engine.d.ts.map +1 -1
  187. package/dist/src/review/passthrough-engine.js +1 -3
  188. package/dist/src/review/passthrough-engine.js.map +1 -1
  189. package/dist/src/review/report-generator.d.ts.map +1 -1
  190. package/dist/src/review/report-generator.js.map +1 -1
  191. package/dist/src/scripts/postinstall.js +3 -1
  192. package/dist/src/scripts/postinstall.js.map +1 -1
  193. package/dist/src/spec/generator.d.ts.map +1 -1
  194. package/dist/src/spec/generator.js.map +1 -1
  195. package/dist/src/spec/passthrough-generator.d.ts.map +1 -1
  196. package/dist/src/spec/passthrough-generator.js.map +1 -1
  197. package/dist/src/spec/schema.js +1 -1
  198. package/dist/src/spec/schema.js.map +1 -1
  199. package/dist/src/spec/text-based-spec-generator.d.ts.map +1 -1
  200. package/dist/src/spec/text-based-spec-generator.js +3 -1
  201. package/dist/src/spec/text-based-spec-generator.js.map +1 -1
  202. package/dist/src/tui/hooks/event-store-reader.d.ts.map +1 -1
  203. package/dist/src/tui/hooks/event-store-reader.js +1 -3
  204. package/dist/src/tui/hooks/event-store-reader.js.map +1 -1
  205. package/dist/src/tui/hooks/useEventStorePoller.d.ts.map +1 -1
  206. package/dist/src/tui/hooks/useEventStorePoller.js +1 -3
  207. package/dist/src/tui/hooks/useEventStorePoller.js.map +1 -1
  208. package/package.json +5 -1
  209. package/schemas/gestalt.schema.json +41 -3
  210. package/skills/interview/SKILL.md +18 -1
@@ -5,7 +5,7 @@ import { loadConfig } from '../core/config.js';
5
5
  import { log } from '../core/log.js';
6
6
  import { getVersion, checkForUpdates, getCachedUpdateResult } from '../core/version.js';
7
7
  import { EventStore } from '../events/store.js';
8
- import { AnthropicAdapter } from '../llm/adapter.js';
8
+ import { createAdapter } from '../llm/factory.js';
9
9
  import { InterviewEngine } from '../interview/engine.js';
10
10
  import { PassthroughEngine } from '../interview/passthrough-engine.js';
11
11
  import { SpecGenerator } from '../spec/generator.js';
@@ -25,7 +25,7 @@ import { handleReviewPassthrough } from './tools/review-passthrough.js';
25
25
  import { handleCodeGraphPassthrough } from './tools/code-graph-passthrough.js';
26
26
  import { handleGraphVisualizePassthrough } from './tools/graph-visualize-passthrough.js';
27
27
  import { PassthroughReviewEngine } from '../review/passthrough-engine.js';
28
- import { interviewInputSchema, specInputSchema, executeInputSchema, agentCreateInputSchema, benchmarkInputSchema, statusInputSchema, codeGraphInputSchema, graphVisualizeInputSchema } from './schemas.js';
28
+ import { interviewInputSchema, specInputSchema, executeInputSchema, agentCreateInputSchema, benchmarkInputSchema, statusInputSchema, codeGraphInputSchema, graphVisualizeInputSchema, } from './schemas.js';
29
29
  import { PassthroughExecuteEngine } from '../execute/passthrough-engine.js';
30
30
  import { PassthroughAgentGenerator } from '../agent/passthrough-generator.js';
31
31
  import { RoleAgentRegistry } from '../agent/role-agent-registry.js';
@@ -48,37 +48,67 @@ export async function createMcpServer(configOverrides) {
48
48
  const ptEngine = new PassthroughEngine(eventStore, agentRegistry);
49
49
  const ptSpecGen = new PassthroughSpecGenerator(eventStore, agentRegistry);
50
50
  server.tool('ges_interview', 'Conduct a Gestalt-driven interview (passthrough mode — returns prompts for caller LLM to generate questions/scores). Actions: start, respond, score, complete.', {
51
- action: z.enum(['start', 'respond', 'score', 'complete']).describe('start: begin interview, respond: answer a question, score: check resolution, complete: finish interview'),
51
+ action: z
52
+ .enum(['start', 'respond', 'score', 'complete'])
53
+ .describe('start: begin interview, respond: answer a question, score: check resolution, complete: finish interview'),
52
54
  topic: z.string().optional().describe('Topic for the interview (required for start)'),
53
- sessionId: z.string().optional().describe('Session ID (required for respond/score/complete)'),
54
- response: z.string().optional().describe('User response to the current question (required for respond)'),
55
+ sessionId: z
56
+ .string()
57
+ .optional()
58
+ .describe('Session ID (required for respond/score/complete)'),
59
+ response: z
60
+ .string()
61
+ .optional()
62
+ .describe('User response to the current question (required for respond)'),
55
63
  cwd: z.string().optional().describe('Working directory for brownfield detection'),
56
- generatedQuestion: z.string().optional().describe('Question generated by the caller LLM (required for respond in passthrough mode)'),
57
- resolutionScore: z.object({
64
+ generatedQuestion: z
65
+ .string()
66
+ .optional()
67
+ .describe('Question generated by the caller LLM (required for respond in passthrough mode)'),
68
+ resolutionScore: z
69
+ .object({
58
70
  goalClarity: z.number().min(0).max(1),
59
71
  constraintClarity: z.number().min(0).max(1),
60
72
  successCriteria: z.number().min(0).max(1),
61
73
  priorityClarity: z.number().min(0).max(1),
62
74
  contextClarity: z.number().min(0).max(1).optional(),
63
75
  contradictions: z.array(z.string()).optional(),
64
- }).optional().describe('Resolution scores computed by the caller LLM'),
65
- record: z.boolean().optional().describe('Generate a GIF recording of the interview session on complete'),
76
+ })
77
+ .optional()
78
+ .describe('Resolution scores computed by the caller LLM'),
79
+ record: z
80
+ .boolean()
81
+ .optional()
82
+ .describe('Generate a GIF recording of the interview session on complete'),
66
83
  }, (params) => {
67
84
  const input = interviewInputSchema.parse(params);
68
85
  const result = handleInterviewPassthrough(ptEngine, input);
69
86
  return { content: [{ type: 'text', text: result }] };
70
87
  });
71
88
  server.tool('ges_generate_spec', 'Generate a Spec specification (passthrough mode — returns prompt or validates externally generated spec).', {
72
- sessionId: z.string().optional().describe('Interview session ID (required when not using text input)'),
73
- text: z.string().optional().describe('Plain text description to generate spec directly without interview'),
74
- force: z.boolean().optional().default(false).describe('Force generation even if resolution threshold is not met'),
75
- spec: z.object({
89
+ sessionId: z
90
+ .string()
91
+ .optional()
92
+ .describe('Interview session ID (required when not using text input)'),
93
+ text: z
94
+ .string()
95
+ .optional()
96
+ .describe('Plain text description to generate spec directly without interview'),
97
+ force: z
98
+ .boolean()
99
+ .optional()
100
+ .default(false)
101
+ .describe('Force generation even if resolution threshold is not met'),
102
+ spec: z
103
+ .object({
76
104
  goal: z.string(),
77
105
  constraints: z.array(z.string()),
78
106
  acceptanceCriteria: z.array(z.string()),
79
107
  ontologySchema: z.object({ entities: z.array(z.any()), relations: z.array(z.any()) }),
80
108
  gestaltAnalysis: z.array(z.any()),
81
- }).optional().describe('Externally generated spec JSON to validate and store'),
109
+ })
110
+ .optional()
111
+ .describe('Externally generated spec JSON to validate and store'),
82
112
  }, (params) => {
83
113
  const input = specInputSchema.parse(params);
84
114
  const result = handleSpecPassthrough(ptEngine, ptSpecGen, input, agentRegistry);
@@ -89,14 +119,31 @@ export async function createMcpServer(configOverrides) {
89
119
  const ptExecuteEngine = new PassthroughExecuteEngine(eventStore, agentRegistry, roleAgentRegistry);
90
120
  const ptReviewEngine = new PassthroughReviewEngine(eventStore);
91
121
  server.tool('ges_execute', 'Execute a Spec specification using Gestalt principles (passthrough mode). Actions: start, plan_step, plan_complete, execute_start, execute_task, evaluate, status, evolve_fix, evolve, evolve_patch, evolve_re_execute, evolve_lateral, evolve_lateral_result, review_start, review_submit, review_consensus, review_fix.', {
92
- action: z.enum([
93
- 'start', 'plan_step', 'plan_complete', 'execute_start', 'execute_task', 'evaluate', 'status',
94
- 'evolve_fix', 'evolve', 'evolve_patch', 'evolve_re_execute',
95
- 'evolve_lateral', 'evolve_lateral_result',
96
- 'role_match', 'role_consensus',
97
- 'review_start', 'review_submit', 'review_consensus', 'review_fix',
98
- ]).describe('start: begin execution planning, plan_step: submit a planning step result, plan_complete: assemble final plan, execute_start: start task execution, execute_task: submit task result, evaluate: start/submit evaluation, status: check session status, evolve_fix: start/submit structural fix, evolve: start contextual evolution, evolve_patch: submit spec patch, evolve_re_execute: submit re-execution task result, evolve_lateral: request next lateral thinking persona, evolve_lateral_result: submit lateral thinking result, review_start: start code review, review_submit: submit agent review, review_consensus: submit merged review, review_fix: start auto-fix loop'),
99
- spec: z.object({
122
+ action: z
123
+ .enum([
124
+ 'start',
125
+ 'plan_step',
126
+ 'plan_complete',
127
+ 'execute_start',
128
+ 'execute_task',
129
+ 'evaluate',
130
+ 'status',
131
+ 'evolve_fix',
132
+ 'evolve',
133
+ 'evolve_patch',
134
+ 'evolve_re_execute',
135
+ 'evolve_lateral',
136
+ 'evolve_lateral_result',
137
+ 'role_match',
138
+ 'role_consensus',
139
+ 'review_start',
140
+ 'review_submit',
141
+ 'review_consensus',
142
+ 'review_fix',
143
+ ])
144
+ .describe('planning: start/plan_step/plan_complete, execution: execute_start/execute_task, evaluate: check results, status: session status, evolve_*: resilience loop, review_*: code review'),
145
+ spec: z
146
+ .object({
100
147
  version: z.string(),
101
148
  goal: z.string(),
102
149
  constraints: z.array(z.string()),
@@ -109,14 +156,21 @@ export async function createMcpServer(configOverrides) {
109
156
  resolutionScore: z.number(),
110
157
  generatedAt: z.string(),
111
158
  }),
112
- }).optional().describe('Spec specification (required for start)'),
113
- sessionId: z.string().optional().describe('Execute session ID (required for plan_step/plan_complete/status)'),
114
- stepResult: z.object({
159
+ })
160
+ .optional()
161
+ .describe('Spec specification (required for start)'),
162
+ sessionId: z
163
+ .string()
164
+ .optional()
165
+ .describe('Execute session ID (required for plan_step/plan_complete/status)'),
166
+ stepResult: z
167
+ .object({
115
168
  principle: z.enum(['figure_ground', 'closure', 'proximity', 'continuity']),
116
169
  classifiedACs: z.array(z.any()).optional(),
117
170
  atomicTasks: z.array(z.any()).optional(),
118
171
  taskGroups: z.array(z.any()).optional(),
119
- dagValidation: z.object({
172
+ dagValidation: z
173
+ .object({
120
174
  isValid: z.boolean(),
121
175
  hasCycles: z.boolean(),
122
176
  cycleDetails: z.array(z.string()).optional(),
@@ -124,15 +178,22 @@ export async function createMcpServer(configOverrides) {
124
178
  conflictDetails: z.array(z.string()).optional(),
125
179
  topologicalOrder: z.array(z.string()),
126
180
  criticalPath: z.array(z.string()),
127
- }).optional(),
128
- }).optional().describe('Planning step result (required for plan_step)'),
129
- taskResult: z.object({
181
+ })
182
+ .optional(),
183
+ })
184
+ .optional()
185
+ .describe('Planning step result (required for plan_step)'),
186
+ taskResult: z
187
+ .object({
130
188
  taskId: z.string(),
131
189
  status: z.enum(['pending', 'in_progress', 'completed', 'failed', 'skipped']),
132
190
  output: z.string(),
133
191
  artifacts: z.array(z.string()),
134
- }).optional().describe('Task execution result (required for execute_task)'),
135
- evaluationResult: z.object({
192
+ })
193
+ .optional()
194
+ .describe('Task execution result (required for execute_task)'),
195
+ evaluationResult: z
196
+ .object({
136
197
  verifications: z.array(z.object({
137
198
  acIndex: z.number(),
138
199
  satisfied: z.boolean(),
@@ -142,8 +203,11 @@ export async function createMcpServer(configOverrides) {
142
203
  overallScore: z.number().min(0).max(1),
143
204
  goalAlignment: z.number().min(0).max(1).optional().default(0),
144
205
  recommendations: z.array(z.string()),
145
- }).optional().describe('Evaluation result (optional for evaluate action)'),
146
- structuralResult: z.object({
206
+ })
207
+ .optional()
208
+ .describe('Evaluation result (optional for evaluate action)'),
209
+ structuralResult: z
210
+ .object({
147
211
  commands: z.array(z.object({
148
212
  name: z.string(),
149
213
  command: z.string(),
@@ -151,53 +215,78 @@ export async function createMcpServer(configOverrides) {
151
215
  output: z.string(),
152
216
  })),
153
217
  allPassed: z.boolean(),
154
- }).optional().describe('Structural check results (required for evaluate after structural stage)'),
155
- fixTasks: z.array(z.object({
218
+ })
219
+ .optional()
220
+ .describe('Structural check results (required for evaluate after structural stage)'),
221
+ fixTasks: z
222
+ .array(z.object({
156
223
  taskId: z.string(),
157
224
  failedCommand: z.string(),
158
225
  errorOutput: z.string(),
159
226
  fixDescription: z.string(),
160
227
  artifacts: z.array(z.string()),
161
- })).optional().describe('Fix task results (for evolve_fix)'),
162
- specPatch: z.object({
228
+ }))
229
+ .optional()
230
+ .describe('Fix task results (for evolve_fix)'),
231
+ specPatch: z
232
+ .object({
163
233
  acceptanceCriteria: z.array(z.string()).optional(),
164
234
  constraints: z.array(z.string()).optional(),
165
- ontologySchema: z.object({
235
+ ontologySchema: z
236
+ .object({
166
237
  entities: z.array(z.any()).optional(),
167
238
  relations: z.array(z.any()).optional(),
168
- }).optional(),
169
- }).optional().describe('Spec patch for contextual evolution (for evolve_patch)'),
170
- reExecuteTaskResult: z.object({
239
+ })
240
+ .optional(),
241
+ })
242
+ .optional()
243
+ .describe('Spec patch for contextual evolution (for evolve_patch)'),
244
+ reExecuteTaskResult: z
245
+ .object({
171
246
  taskId: z.string(),
172
247
  status: z.enum(['pending', 'in_progress', 'completed', 'failed', 'skipped']),
173
248
  output: z.string(),
174
249
  artifacts: z.array(z.string()),
175
- }).optional().describe('Re-execution task result (for evolve_re_execute)'),
250
+ })
251
+ .optional()
252
+ .describe('Re-execution task result (for evolve_re_execute)'),
176
253
  terminateReason: z.enum(['caller']).optional().describe('Caller-initiated termination'),
177
- lateralResult: z.object({
254
+ lateralResult: z
255
+ .object({
178
256
  persona: z.enum(['multistability', 'simplicity', 'reification', 'invariance']),
179
257
  specPatch: z.object({
180
258
  acceptanceCriteria: z.array(z.string()).optional(),
181
259
  constraints: z.array(z.string()).optional(),
182
- ontologySchema: z.object({
260
+ ontologySchema: z
261
+ .object({
183
262
  entities: z.array(z.any()).optional(),
184
263
  relations: z.array(z.any()).optional(),
185
- }).optional(),
264
+ })
265
+ .optional(),
186
266
  }),
187
267
  description: z.string(),
188
- }).optional().describe('Lateral thinking result (for evolve_lateral_result)'),
189
- matchResult: z.array(z.object({
268
+ })
269
+ .optional()
270
+ .describe('Lateral thinking result (for evolve_lateral_result)'),
271
+ matchResult: z
272
+ .array(z.object({
190
273
  agentName: z.string(),
191
274
  domain: z.array(z.string()),
192
275
  relevanceScore: z.number().min(0).max(1),
193
276
  reasoning: z.string(),
194
- })).optional().describe('Role match results (for role_match action)'),
195
- perspectives: z.array(z.object({
277
+ }))
278
+ .optional()
279
+ .describe('Role match results (for role_match action)'),
280
+ perspectives: z
281
+ .array(z.object({
196
282
  agentName: z.string(),
197
283
  perspective: z.string(),
198
284
  confidence: z.number().min(0).max(1),
199
- })).optional().describe('Role perspectives (for role_consensus action)'),
200
- consensus: z.object({
285
+ }))
286
+ .optional()
287
+ .describe('Role perspectives (for role_consensus action)'),
288
+ consensus: z
289
+ .object({
201
290
  consensus: z.string(),
202
291
  conflictResolutions: z.array(z.string()),
203
292
  perspectives: z.array(z.object({
@@ -205,7 +294,9 @@ export async function createMcpServer(configOverrides) {
205
294
  perspective: z.string(),
206
295
  confidence: z.number().min(0).max(1),
207
296
  })),
208
- }).optional().describe('Synthesized consensus (for role_consensus action)'),
297
+ })
298
+ .optional()
299
+ .describe('Synthesized consensus (for role_consensus action)'),
209
300
  }, async (params) => {
210
301
  const input = executeInputSchema.parse(params);
211
302
  // Route review actions to review engine
@@ -218,18 +309,31 @@ export async function createMcpServer(configOverrides) {
218
309
  });
219
310
  const ptAgentGen = new PassthroughAgentGenerator(eventStore, roleAgentRegistry);
220
311
  server.tool('ges_create_agent', 'Create a custom Role Agent from a completed interview session (passthrough mode). Actions: start, submit.', {
221
- action: z.enum(['start', 'submit']).describe('start: get agent creation context with prompts, submit: validate and save AGENT.md'),
312
+ action: z
313
+ .enum(['start', 'submit'])
314
+ .describe('start: get agent creation context with prompts, submit: validate and save AGENT.md'),
222
315
  sessionId: z.string().describe('The interview session ID'),
223
- agentContent: z.string().optional().describe('Complete AGENT.md content (frontmatter + body) to validate and save (required for submit)'),
224
- cwd: z.string().optional().describe('Working directory where agents/{name}/AGENT.md will be created (defaults to process.cwd())'),
316
+ agentContent: z
317
+ .string()
318
+ .optional()
319
+ .describe('Complete AGENT.md content (frontmatter + body) to validate and save (required for submit)'),
320
+ cwd: z
321
+ .string()
322
+ .optional()
323
+ .describe('Working directory where agents/{name}/AGENT.md will be created (defaults to process.cwd())'),
225
324
  }, (params) => {
226
325
  const input = agentCreateInputSchema.parse(params);
227
326
  const result = handleCreateAgentPassthrough(ptEngine, ptAgentGen, input);
228
327
  return { content: [{ type: 'text', text: result }] };
229
328
  });
230
- server.tool('ges_agent', 'List available agents or retrieve a specific agent\'s system prompt for standalone use — no pipeline required. Actions: list (get all role/review agents), get (retrieve agent by name).', {
231
- action: z.enum(['list', 'get']).describe('list: get all available agents grouped by type, get: retrieve a specific agent\'s systemPrompt'),
232
- name: z.string().optional().describe('Agent name (required for action=get, e.g. "architect", "security-reviewer")'),
329
+ server.tool('ges_agent', "List available agents or retrieve a specific agent's system prompt for standalone use — no pipeline required. Actions: list (get all role/review agents), get (retrieve agent by name).", {
330
+ action: z
331
+ .enum(['list', 'get'])
332
+ .describe("list: get all available agents grouped by type, get: retrieve a specific agent's systemPrompt"),
333
+ name: z
334
+ .string()
335
+ .optional()
336
+ .describe('Agent name (required for action=get, e.g. "architect", "security-reviewer")'),
233
337
  }, (params) => {
234
338
  const result = handleAgentPassthrough(roleAgentRegistry, {
235
339
  action: params.action,
@@ -238,22 +342,42 @@ export async function createMcpServer(configOverrides) {
238
342
  return { content: [{ type: 'text', text: result }] };
239
343
  });
240
344
  server.tool('ges_benchmark', 'Run Gestalt pipeline benchmarks in passthrough mode. Actions: start (begin a scenario), respond (submit LLM response), status (check progress). No API key required — caller acts as the LLM.', {
241
- action: z.enum(['start', 'respond', 'status']).describe('start: begin benchmark scenario, respond: submit LLM response for current step, status: check benchmark progress'),
242
- scenario: z.string().optional().describe('Scenario name for start: auth-system, dashboard, api-gateway'),
243
- benchmarkSessionId: z.string().optional().describe('Benchmark session ID (required for respond/status)'),
244
- response: z.string().optional().describe('JSON response from the caller LLM (required for respond)'),
245
- usage: z.object({
345
+ action: z
346
+ .enum(['start', 'respond', 'status'])
347
+ .describe('start: begin benchmark scenario, respond: submit LLM response for current step, status: check benchmark progress'),
348
+ scenario: z
349
+ .string()
350
+ .optional()
351
+ .describe('Scenario name for start: auth-system, dashboard, api-gateway'),
352
+ benchmarkSessionId: z
353
+ .string()
354
+ .optional()
355
+ .describe('Benchmark session ID (required for respond/status)'),
356
+ response: z
357
+ .string()
358
+ .optional()
359
+ .describe('JSON response from the caller LLM (required for respond)'),
360
+ usage: z
361
+ .object({
246
362
  inputTokens: z.number(),
247
363
  outputTokens: z.number(),
248
- }).optional().describe('Token usage from the caller LLM call (optional, for metrics)'),
364
+ })
365
+ .optional()
366
+ .describe('Token usage from the caller LLM call (optional, for metrics)'),
249
367
  }, (params) => {
250
368
  const input = benchmarkInputSchema.parse(params);
251
369
  const result = handleBenchmarkPassthrough(input);
252
370
  return { content: [{ type: 'text', text: result }] };
253
371
  });
254
372
  server.tool('ges_status', 'Check the status of interview and execute sessions.', {
255
- sessionId: z.string().optional().describe('Specific session ID to check (omit for all sessions)'),
256
- sessionType: z.enum(['interview', 'execute', 'all']).optional().default('all')
373
+ sessionId: z
374
+ .string()
375
+ .optional()
376
+ .describe('Specific session ID to check (omit for all sessions)'),
377
+ sessionType: z
378
+ .enum(['interview', 'execute', 'all'])
379
+ .optional()
380
+ .default('all')
257
381
  .describe('Filter by session type (default: all)'),
258
382
  }, (params) => {
259
383
  const input = statusInputSchema.parse(params);
@@ -261,17 +385,36 @@ export async function createMcpServer(configOverrides) {
261
385
  return { content: [{ type: 'text', text: result }] };
262
386
  });
263
387
  server.tool('ges_code_graph', 'Build and query the code knowledge graph for a repository. Actions: build (index codebase), blast_radius (find impacted files from changes), query (graph traversal), stats (show DB stats), db_exists (check if graph DB exists).', {
264
- action: z.enum(['build', 'blast_radius', 'query', 'stats', 'db_exists']).describe('build: index codebase into graph DB, blast_radius: find files impacted by changes, query: traverse graph, stats: show stats, db_exists: check if DB exists'),
388
+ action: z
389
+ .enum(['build', 'blast_radius', 'query', 'stats', 'db_exists'])
390
+ .describe('build: index codebase into graph DB, blast_radius: find files impacted by changes, query: traverse graph, stats: show stats, db_exists: check if DB exists'),
265
391
  repoRoot: z.string().describe('Absolute path to the repository root'),
266
- include: z.array(z.string()).optional().describe('Glob patterns to include (default: **/*)'),
392
+ include: z
393
+ .array(z.string())
394
+ .optional()
395
+ .describe('Glob patterns to include (default: **/*)'),
267
396
  exclude: z.array(z.string()).optional().describe('Glob patterns to exclude'),
268
- mode: z.enum(['full', 'incremental']).optional().describe('Build mode: full (default) or incremental (hash-based skip)'),
269
- changedFiles: z.array(z.string()).optional().describe('Changed file paths (auto-detected from git diff HEAD~1 if omitted)'),
397
+ mode: z
398
+ .enum(['full', 'incremental'])
399
+ .optional()
400
+ .describe('Build mode: full (default) or incremental (hash-based skip)'),
401
+ changedFiles: z
402
+ .array(z.string())
403
+ .optional()
404
+ .describe('Changed file paths (auto-detected from git diff HEAD~1 if omitted)'),
270
405
  base: z.string().optional().describe('Git base ref for diff detection (default: HEAD~1)'),
271
- maxDepth: z.number().optional().describe('BFS traversal depth for blast_radius (default: 2)'),
272
- pattern: z.enum(['callers_of', 'callees_of', 'tests_for', 'imports_of']).optional()
406
+ maxDepth: z
407
+ .number()
408
+ .optional()
409
+ .describe('BFS traversal depth for blast_radius (default: 2)'),
410
+ pattern: z
411
+ .enum(['callers_of', 'callees_of', 'tests_for', 'imports_of'])
412
+ .optional()
273
413
  .describe('Query pattern (required for query action)'),
274
- target: z.string().optional().describe('Target function or file path (required for query action)'),
414
+ target: z
415
+ .string()
416
+ .optional()
417
+ .describe('Target function or file path (required for query action)'),
275
418
  }, async (params) => {
276
419
  const input = codeGraphInputSchema.parse(params);
277
420
  const result = await handleCodeGraphPassthrough(input);
@@ -279,7 +422,10 @@ export async function createMcpServer(configOverrides) {
279
422
  });
280
423
  server.tool('ges_graph_visualize', 'Start a local HTTP server that renders an interactive D3.js force-directed graph visualization of the code knowledge graph. Opens the browser automatically. Requires an existing or auto-buildable code-graph.db.', {
281
424
  repoRoot: z.string().describe('Absolute path to the repository root'),
282
- port: z.number().optional().describe('Preferred server port (default: 7891, auto-increments on conflict)'),
425
+ port: z
426
+ .number()
427
+ .optional()
428
+ .describe('Preferred server port (default: 7891, auto-increments on conflict)'),
283
429
  }, async (params) => {
284
430
  const input = graphVisualizeInputSchema.parse(params);
285
431
  const result = await handleGraphVisualizePassthrough(input);
@@ -288,16 +434,27 @@ export async function createMcpServer(configOverrides) {
288
434
  }
289
435
  else {
290
436
  // ─── Normal mode: direct LLM calls ──────────────────────────
291
- const llm = new AnthropicAdapter(config.llm.apiKey, config.llm.model);
437
+ const llm = createAdapter(config.llm);
292
438
  const engine = new InterviewEngine(llm, eventStore);
293
439
  const specGenerator = new SpecGenerator(llm, eventStore);
294
440
  server.tool('ges_interview', 'Conduct a Gestalt-driven interview to clarify project requirements. Actions: start, respond, score, complete.', {
295
- action: z.enum(['start', 'respond', 'score', 'complete']).describe('start: begin interview, respond: answer a question, score: check resolution, complete: finish interview'),
441
+ action: z
442
+ .enum(['start', 'respond', 'score', 'complete'])
443
+ .describe('start: begin interview, respond: answer a question, score: check resolution, complete: finish interview'),
296
444
  topic: z.string().optional().describe('Topic for the interview (required for start)'),
297
- sessionId: z.string().optional().describe('Session ID (required for respond/score/complete)'),
298
- response: z.string().optional().describe('User response to the current question (required for respond)'),
445
+ sessionId: z
446
+ .string()
447
+ .optional()
448
+ .describe('Session ID (required for respond/score/complete)'),
449
+ response: z
450
+ .string()
451
+ .optional()
452
+ .describe('User response to the current question (required for respond)'),
299
453
  cwd: z.string().optional().describe('Working directory for brownfield detection'),
300
- record: z.boolean().optional().describe('Generate a GIF recording of the interview session on complete'),
454
+ record: z
455
+ .boolean()
456
+ .optional()
457
+ .describe('Generate a GIF recording of the interview session on complete'),
301
458
  }, async (params) => {
302
459
  const input = interviewInputSchema.parse(params);
303
460
  const result = await handleInterview(engine, input);
@@ -305,15 +462,25 @@ export async function createMcpServer(configOverrides) {
305
462
  });
306
463
  server.tool('ges_generate_spec', 'Generate a Spec specification from a completed interview session.', {
307
464
  sessionId: z.string().describe('The interview session ID'),
308
- force: z.boolean().optional().default(false).describe('Force generation even if resolution threshold is not met'),
465
+ force: z
466
+ .boolean()
467
+ .optional()
468
+ .default(false)
469
+ .describe('Force generation even if resolution threshold is not met'),
309
470
  }, async (params) => {
310
471
  const input = specInputSchema.parse(params);
311
472
  const result = await handleSpec(engine, specGenerator, input);
312
473
  return { content: [{ type: 'text', text: result }] };
313
474
  });
314
475
  server.tool('ges_status', 'Check the status of interview and execute sessions.', {
315
- sessionId: z.string().optional().describe('Specific session ID to check (omit for all sessions)'),
316
- sessionType: z.enum(['interview', 'execute', 'all']).optional().default('all')
476
+ sessionId: z
477
+ .string()
478
+ .optional()
479
+ .describe('Specific session ID to check (omit for all sessions)'),
480
+ sessionType: z
481
+ .enum(['interview', 'execute', 'all'])
482
+ .optional()
483
+ .default('all')
317
484
  .describe('Filter by session type (default: all)'),
318
485
  }, (params) => {
319
486
  const input = statusInputSchema.parse(params);
@@ -369,7 +536,7 @@ function handleStatusPassthrough(engine, executeEngine, input) {
369
536
  }
370
537
  }
371
538
  // List mode
372
- const interviewSessions = (sessionType === 'interview' || sessionType === 'all')
539
+ const interviewSessions = sessionType === 'interview' || sessionType === 'all'
373
540
  ? engine.listSessions().map((s) => ({
374
541
  sessionId: s.sessionId,
375
542
  topic: s.topic,
@@ -380,7 +547,7 @@ function handleStatusPassthrough(engine, executeEngine, input) {
380
547
  createdAt: s.createdAt,
381
548
  }))
382
549
  : [];
383
- const executeSessions = (sessionType === 'execute' || sessionType === 'all')
550
+ const executeSessions = sessionType === 'execute' || sessionType === 'all'
384
551
  ? executeEngine.listSessions().map((s) => formatExecuteSessionSummary(s))
385
552
  : [];
386
553
  return JSON.stringify({
@@ -434,38 +601,49 @@ function formatExecuteSession(session) {
434
601
  currentStep: session.currentStep,
435
602
  stepsCompleted: session.planningSteps.map((s) => s.principle),
436
603
  hasPlan: !!session.executionPlan,
437
- taskProgress: { total: totalTasks, completed: completedTasks, failed: failedTasks, pending: pendingTasks },
604
+ taskProgress: {
605
+ total: totalTasks,
606
+ completed: completedTasks,
607
+ failed: failedTasks,
608
+ pending: pendingTasks,
609
+ },
438
610
  satisfiedACs: `${satisfiedACs}/${totalACs}`,
439
611
  evaluateStage: session.evaluateStage ?? null,
440
612
  hasEvaluation: !!session.evaluationResult,
441
613
  evaluationScore: session.evaluationResult?.overallScore ?? null,
442
614
  goalAlignment: session.evaluationResult?.goalAlignment ?? null,
443
615
  driftAlerts: session.driftHistory.filter((d) => d.thresholdExceeded).length,
444
- evolution: session.evolutionHistory.length > 0 ? {
445
- scoreProgression: session.evolutionHistory.map((gen) => ({
446
- generation: gen.generation,
447
- score: gen.evaluationScore,
448
- goalAlignment: gen.goalAlignment,
449
- fieldsChanged: Object.keys(gen.delta).filter((k) => {
450
- const val = gen.delta[k];
451
- return Array.isArray(val) ? val.length > 0 : val !== undefined;
452
- }),
453
- })),
454
- summary: summarizeEvolution(session),
455
- } : null,
616
+ evolution: session.evolutionHistory.length > 0
617
+ ? {
618
+ scoreProgression: session.evolutionHistory.map((gen) => ({
619
+ generation: gen.generation,
620
+ score: gen.evaluationScore,
621
+ goalAlignment: gen.goalAlignment,
622
+ fieldsChanged: Object.keys(gen.delta).filter((k) => {
623
+ const val = gen.delta[k];
624
+ return Array.isArray(val) ? val.length > 0 : val !== undefined;
625
+ }),
626
+ })),
627
+ summary: summarizeEvolution(session),
628
+ }
629
+ : null,
456
630
  evolveStage: session.evolveStage ?? null,
457
631
  currentGeneration: session.currentGeneration,
458
632
  terminationReason: session.terminationReason ?? null,
459
- lateral: session.lateralTriedPersonas.length > 0 ? {
460
- triedPersonas: session.lateralTriedPersonas,
461
- totalAttempts: session.lateralAttempts,
462
- currentPersona: session.lateralCurrentPersona ?? null,
463
- currentPattern: session.lateralCurrentPattern ?? null,
464
- } : null,
465
- roleAgent: session.roleMatches ? {
466
- matchedAgents: session.roleMatches.map((m) => m.agentName),
467
- hasConsensus: !!session.roleConsensus,
468
- } : null,
633
+ lateral: session.lateralTriedPersonas.length > 0
634
+ ? {
635
+ triedPersonas: session.lateralTriedPersonas,
636
+ totalAttempts: session.lateralAttempts,
637
+ currentPersona: session.lateralCurrentPersona ?? null,
638
+ currentPattern: session.lateralCurrentPattern ?? null,
639
+ }
640
+ : null,
641
+ roleAgent: session.roleMatches
642
+ ? {
643
+ matchedAgents: session.roleMatches.map((m) => m.agentName),
644
+ hasConsensus: !!session.roleConsensus,
645
+ }
646
+ : null,
469
647
  createdAt: session.createdAt,
470
648
  updatedAt: session.updatedAt,
471
649
  };
@@ -497,12 +675,14 @@ export async function startMcpServer(configOverrides) {
497
675
  const transport = new StdioServerTransport();
498
676
  await server.connect(transport);
499
677
  log(`MCP server started on stdio${isPassthrough ? ' (passthrough mode)' : ''}`);
500
- checkForUpdates().then((result) => {
678
+ checkForUpdates()
679
+ .then((result) => {
501
680
  if (result?.updateAvailable) {
502
681
  log(`Update available: ${result.currentVersion} → ${result.latestVersion}`);
503
682
  log(`Run: npx -y @tienne/gestalt@latest`);
504
683
  }
505
- }).catch(() => { });
684
+ })
685
+ .catch(() => { });
506
686
  process.on('SIGINT', async () => {
507
687
  await skillRegistry.stopWatching();
508
688
  await agentRegistry.stopWatching();