genesis-ai-cli 7.4.7 → 7.5.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.
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ /**
3
+ * Genesis MCP Tool Chaining Framework
4
+ *
5
+ * Automatic orchestration of dependent tool calls.
6
+ * Enables workflows like: generate image → open it → edit it
7
+ *
8
+ * Features:
9
+ * - Declarative chain definitions
10
+ * - Automatic output → input mapping
11
+ * - Conditional branching
12
+ * - Error recovery and rollback
13
+ * - Chain execution history
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ChainBuilder = exports.ToolChainExecutor = exports.CHAIN_TEMPLATES = void 0;
17
+ exports.getChainExecutor = getChainExecutor;
18
+ exports.chain = chain;
19
+ exports.executeChain = executeChain;
20
+ const index_js_1 = require("./index.js");
21
+ // ============================================================================
22
+ // Predefined Chain Templates
23
+ // ============================================================================
24
+ exports.CHAIN_TEMPLATES = {
25
+ // Generate image and open it
26
+ 'generate-and-display': {
27
+ id: 'generate-and-display',
28
+ name: 'Generate and Display Image',
29
+ description: 'Generate an image with Stability AI and open it in the default viewer',
30
+ steps: [
31
+ {
32
+ id: 'generate',
33
+ server: 'stability-ai',
34
+ tool: 'stability-ai-generate-image',
35
+ params: (ctx) => ({
36
+ prompt: ctx.data.prompt || 'a beautiful landscape',
37
+ outputImageFileName: ctx.data.filename || `genesis-${Date.now()}`,
38
+ }),
39
+ transform: (result) => ({
40
+ imagePath: result.imagePath || result.outputPath || result,
41
+ }),
42
+ },
43
+ {
44
+ id: 'open',
45
+ server: 'filesystem',
46
+ tool: 'read_file',
47
+ params: (ctx) => ({
48
+ path: ctx.results.get('generate')?.imagePath,
49
+ }),
50
+ condition: (ctx) => !!ctx.results.get('generate')?.imagePath,
51
+ },
52
+ ],
53
+ },
54
+ // Search and scrape
55
+ 'search-and-scrape': {
56
+ id: 'search-and-scrape',
57
+ name: 'Search and Scrape',
58
+ description: 'Search for a topic and scrape the top result',
59
+ steps: [
60
+ {
61
+ id: 'search',
62
+ server: 'brave-search',
63
+ tool: 'brave_web_search',
64
+ params: (ctx) => ({
65
+ query: ctx.data.query,
66
+ count: 3,
67
+ }),
68
+ transform: (result) => ({
69
+ url: result.results?.[0]?.url || result.web?.results?.[0]?.url,
70
+ title: result.results?.[0]?.title || result.web?.results?.[0]?.title,
71
+ }),
72
+ },
73
+ {
74
+ id: 'scrape',
75
+ server: 'firecrawl',
76
+ tool: 'firecrawl_scrape',
77
+ params: (ctx) => ({
78
+ url: ctx.results.get('search')?.url,
79
+ formats: ['markdown'],
80
+ }),
81
+ condition: (ctx) => !!ctx.results.get('search')?.url,
82
+ onError: 'skip',
83
+ },
84
+ ],
85
+ },
86
+ // Research paper workflow
87
+ 'research-paper': {
88
+ id: 'research-paper',
89
+ name: 'Research Paper Workflow',
90
+ description: 'Search arXiv, get citations, save to memory',
91
+ steps: [
92
+ {
93
+ id: 'search-arxiv',
94
+ server: 'arxiv',
95
+ tool: 'search_arxiv',
96
+ params: (ctx) => ({
97
+ query: ctx.data.query,
98
+ maxResults: 5,
99
+ }),
100
+ },
101
+ {
102
+ id: 'get-citations',
103
+ server: 'semantic-scholar',
104
+ tool: 'get_paper_citations',
105
+ params: (ctx) => {
106
+ const papers = ctx.results.get('search-arxiv')?.papers || [];
107
+ const firstPaper = papers[0];
108
+ return {
109
+ paperId: firstPaper?.id?.replace('arxiv:', '') || '',
110
+ maxResults: 10,
111
+ };
112
+ },
113
+ condition: (ctx) => {
114
+ const papers = ctx.results.get('search-arxiv')?.papers || [];
115
+ return papers.length > 0;
116
+ },
117
+ onError: 'skip',
118
+ },
119
+ {
120
+ id: 'save-to-memory',
121
+ server: 'memory',
122
+ tool: 'create_entities',
123
+ params: (ctx) => {
124
+ const papers = ctx.results.get('search-arxiv')?.papers || [];
125
+ return {
126
+ entities: papers.slice(0, 3).map((p) => ({
127
+ name: p.title || 'Unknown Paper',
128
+ entityType: 'research_paper',
129
+ observations: [
130
+ `Authors: ${p.authors?.join(', ') || 'Unknown'}`,
131
+ `Abstract: ${p.abstract?.slice(0, 200) || 'N/A'}...`,
132
+ ],
133
+ })),
134
+ };
135
+ },
136
+ condition: (ctx) => {
137
+ const papers = ctx.results.get('search-arxiv')?.papers || [];
138
+ return papers.length > 0;
139
+ },
140
+ },
141
+ ],
142
+ },
143
+ };
144
+ // ============================================================================
145
+ // Chain Executor
146
+ // ============================================================================
147
+ class ToolChainExecutor {
148
+ mcpClient = (0, index_js_1.getMCPClient)();
149
+ async execute(chain, initialData = {}) {
150
+ const context = {
151
+ results: new Map(),
152
+ currentStep: 0,
153
+ errors: [],
154
+ data: { ...chain.initialContext, ...initialData },
155
+ startTime: new Date(),
156
+ log: [],
157
+ };
158
+ let stepsExecuted = 0;
159
+ let stepsFailed = 0;
160
+ for (let i = 0; i < chain.steps.length; i++) {
161
+ const step = chain.steps[i];
162
+ context.currentStep = i;
163
+ // Check condition
164
+ if (step.condition && !step.condition(context)) {
165
+ this.log(context, step.id, 'skip', { reason: 'condition not met' });
166
+ continue;
167
+ }
168
+ const stepStart = Date.now();
169
+ this.log(context, step.id, 'start');
170
+ try {
171
+ // Resolve params (can be function)
172
+ const params = typeof step.params === 'function'
173
+ ? step.params(context)
174
+ : step.params;
175
+ // Execute the tool
176
+ const result = await this.executeStep(step, params, context);
177
+ if (!result.success) {
178
+ throw new Error(result.error || 'Tool call failed');
179
+ }
180
+ // Transform result if needed
181
+ const transformedResult = step.transform
182
+ ? step.transform(result.data)
183
+ : result.data;
184
+ context.results.set(step.id, transformedResult);
185
+ stepsExecuted++;
186
+ this.log(context, step.id, 'success', {
187
+ latency: Date.now() - stepStart,
188
+ resultKeys: Object.keys(transformedResult || {}),
189
+ });
190
+ }
191
+ catch (error) {
192
+ stepsFailed++;
193
+ const err = error instanceof Error ? error : new Error(String(error));
194
+ context.errors.push({ stepId: step.id, error: err });
195
+ this.log(context, step.id, 'error', {
196
+ message: err.message,
197
+ latency: Date.now() - stepStart,
198
+ });
199
+ // Handle error based on strategy
200
+ if (step.onError === 'stop') {
201
+ break;
202
+ }
203
+ else if (step.onError === 'retry') {
204
+ const retryResult = await this.retryStep(step, context, step.maxRetries || 3);
205
+ if (retryResult) {
206
+ context.results.set(step.id, retryResult);
207
+ stepsExecuted++;
208
+ this.log(context, step.id, 'retry', { success: true });
209
+ }
210
+ }
211
+ else if (typeof step.onError === 'function') {
212
+ const recoveryStep = step.onError(err, context);
213
+ if (recoveryStep) {
214
+ // Insert recovery step
215
+ chain.steps.splice(i + 1, 0, recoveryStep);
216
+ }
217
+ }
218
+ // 'skip' just continues to next step
219
+ }
220
+ }
221
+ const finalResult = chain.finalTransform
222
+ ? chain.finalTransform(context)
223
+ : context.results.get(chain.steps[chain.steps.length - 1]?.id);
224
+ return {
225
+ success: stepsFailed === 0,
226
+ finalResult,
227
+ context,
228
+ totalLatency: Date.now() - context.startTime.getTime(),
229
+ stepsExecuted,
230
+ stepsFailed,
231
+ };
232
+ }
233
+ async executeStep(step, params, context) {
234
+ return this.mcpClient.call(step.server, step.tool, params);
235
+ }
236
+ async retryStep(step, context, maxRetries) {
237
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
238
+ try {
239
+ const params = typeof step.params === 'function'
240
+ ? step.params(context)
241
+ : step.params;
242
+ const result = await this.executeStep(step, params, context);
243
+ if (result.success) {
244
+ return step.transform ? step.transform(result.data) : result.data;
245
+ }
246
+ }
247
+ catch {
248
+ // Continue retrying
249
+ }
250
+ // Exponential backoff
251
+ await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt)));
252
+ }
253
+ return null;
254
+ }
255
+ log(context, stepId, action, details) {
256
+ context.log.push({
257
+ timestamp: new Date(),
258
+ stepId,
259
+ action,
260
+ details,
261
+ });
262
+ }
263
+ }
264
+ exports.ToolChainExecutor = ToolChainExecutor;
265
+ // ============================================================================
266
+ // Chain Builder (Fluent API)
267
+ // ============================================================================
268
+ class ChainBuilder {
269
+ chain;
270
+ constructor(id, name) {
271
+ this.chain = {
272
+ id,
273
+ name,
274
+ steps: [],
275
+ };
276
+ }
277
+ description(desc) {
278
+ this.chain.description = desc;
279
+ return this;
280
+ }
281
+ initialContext(ctx) {
282
+ this.chain.initialContext = ctx;
283
+ return this;
284
+ }
285
+ step(step) {
286
+ this.chain.steps.push(step);
287
+ return this;
288
+ }
289
+ call(id, server, tool, params) {
290
+ return this.step({ id, server, tool, params });
291
+ }
292
+ finalTransform(fn) {
293
+ this.chain.finalTransform = fn;
294
+ return this;
295
+ }
296
+ build() {
297
+ return this.chain;
298
+ }
299
+ }
300
+ exports.ChainBuilder = ChainBuilder;
301
+ // ============================================================================
302
+ // Singleton & Factory
303
+ // ============================================================================
304
+ let executorInstance = null;
305
+ function getChainExecutor() {
306
+ if (!executorInstance) {
307
+ executorInstance = new ToolChainExecutor();
308
+ }
309
+ return executorInstance;
310
+ }
311
+ function chain(id, name) {
312
+ return new ChainBuilder(id, name);
313
+ }
314
+ async function executeChain(chainOrId, data = {}) {
315
+ const executor = getChainExecutor();
316
+ const chainDef = typeof chainOrId === 'string'
317
+ ? exports.CHAIN_TEMPLATES[chainOrId]
318
+ : chainOrId;
319
+ if (!chainDef) {
320
+ throw new Error(`Chain not found: ${chainOrId}`);
321
+ }
322
+ return executor.execute(chainDef, data);
323
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Genesis MCP Result Transformers
3
+ *
4
+ * Composable data transformations for MCP tool results.
5
+ * Pipe-style API for chaining transformations.
6
+ *
7
+ * Features:
8
+ * - Common transformers (extract, filter, map, reduce)
9
+ * - Domain-specific transformers (papers, search, code)
10
+ * - Type-safe pipeline composition
11
+ * - Error handling in pipelines
12
+ * - Custom transformer creation
13
+ */
14
+ export type Transformer<TIn, TOut> = (input: TIn) => TOut;
15
+ export type AsyncTransformer<TIn, TOut> = (input: TIn) => Promise<TOut>;
16
+ export interface TransformPipeline<T> {
17
+ pipe<TOut>(transformer: Transformer<T, TOut>): TransformPipeline<TOut>;
18
+ pipeAsync<TOut>(transformer: AsyncTransformer<T, TOut>): AsyncTransformPipeline<TOut>;
19
+ value(): T;
20
+ valueOr<TDefault>(fallback: TDefault): T | TDefault;
21
+ }
22
+ export interface AsyncTransformPipeline<T> {
23
+ pipe<TOut>(transformer: Transformer<T, TOut>): AsyncTransformPipeline<TOut>;
24
+ pipeAsync<TOut>(transformer: AsyncTransformer<T, TOut>): AsyncTransformPipeline<TOut>;
25
+ value(): Promise<T>;
26
+ valueOr<TDefault>(fallback: TDefault): Promise<T | TDefault>;
27
+ }
28
+ export declare function transform<T>(data: T): TransformPipeline<T>;
29
+ export declare function transformAsync<T>(data: Promise<T>): AsyncTransformPipeline<T>;
30
+ /**
31
+ * Extract a nested property by path
32
+ */
33
+ export declare function extract<TIn, TOut = any>(path: string): Transformer<TIn, TOut | undefined>;
34
+ /**
35
+ * Filter an array
36
+ */
37
+ export declare function filter<T>(predicate: (item: T, index: number) => boolean): Transformer<T[], T[]>;
38
+ /**
39
+ * Map over an array
40
+ */
41
+ export declare function map<TIn, TOut>(mapper: (item: TIn, index: number) => TOut): Transformer<TIn[], TOut[]>;
42
+ /**
43
+ * Reduce an array
44
+ */
45
+ export declare function reduce<T, TOut>(reducer: (acc: TOut, item: T, index: number) => TOut, initial: TOut): Transformer<T[], TOut>;
46
+ /**
47
+ * Take first N items
48
+ */
49
+ export declare function take<T>(n: number): Transformer<T[], T[]>;
50
+ /**
51
+ * Sort an array
52
+ */
53
+ export declare function sort<T>(compareFn?: (a: T, b: T) => number): Transformer<T[], T[]>;
54
+ /**
55
+ * Get unique items by key
56
+ */
57
+ export declare function unique<T>(keyFn?: (item: T) => any): Transformer<T[], T[]>;
58
+ /**
59
+ * Group by key
60
+ */
61
+ export declare function groupBy<T>(keyFn: (item: T) => string): Transformer<T[], Record<string, T[]>>;
62
+ /**
63
+ * Default value if null/undefined
64
+ */
65
+ export declare function defaultTo<T>(fallback: T): Transformer<T | null | undefined, T>;
66
+ /**
67
+ * Pluck a property from each item
68
+ */
69
+ export declare function pluck<T, K extends keyof T>(key: K): Transformer<T[], T[K][]>;
70
+ /**
71
+ * Flatten nested arrays
72
+ */
73
+ export declare function flatten<T>(): Transformer<T[][], T[]>;
74
+ /**
75
+ * Extract papers from arXiv/semantic-scholar results
76
+ */
77
+ export declare function extractPapers(): Transformer<any, any[]>;
78
+ /**
79
+ * Extract search results from brave/exa/firecrawl
80
+ */
81
+ export declare function extractSearchResults(): Transformer<any, any[]>;
82
+ /**
83
+ * Normalize search result to common format
84
+ */
85
+ export interface NormalizedSearchResult {
86
+ title: string;
87
+ url: string;
88
+ description: string;
89
+ source: string;
90
+ }
91
+ export declare function normalizeSearchResult(): Transformer<any, NormalizedSearchResult>;
92
+ /**
93
+ * Normalize paper to common format
94
+ */
95
+ export interface NormalizedPaper {
96
+ id: string;
97
+ title: string;
98
+ authors: string[];
99
+ abstract: string;
100
+ url: string;
101
+ year?: number;
102
+ citations?: number;
103
+ }
104
+ export declare function normalizePaper(): Transformer<any, NormalizedPaper>;
105
+ /**
106
+ * Filter by minimum citation count
107
+ */
108
+ export declare function minCitations(min: number): Transformer<NormalizedPaper[], NormalizedPaper[]>;
109
+ /**
110
+ * Sort papers by citations (descending)
111
+ */
112
+ export declare function sortByCitations(): Transformer<NormalizedPaper[], NormalizedPaper[]>;
113
+ /**
114
+ * Extract code from firecrawl/exa results
115
+ */
116
+ export declare function extractCode(): Transformer<any, string[]>;
117
+ /**
118
+ * Extract URLs from content
119
+ */
120
+ export declare function extractUrls(): Transformer<any, string[]>;
121
+ /**
122
+ * Summarize content to max length
123
+ */
124
+ export declare function summarize(maxLength: number): Transformer<string, string>;
125
+ /**
126
+ * Format as markdown list
127
+ */
128
+ export declare function toMarkdownList(): Transformer<string[], string>;
129
+ /**
130
+ * Format as numbered list
131
+ */
132
+ export declare function toNumberedList(): Transformer<string[], string>;
133
+ /**
134
+ * Compose multiple transformers into one
135
+ */
136
+ export declare function compose<TIn, TOut>(...transformers: Transformer<any, any>[]): Transformer<TIn, TOut>;
137
+ /**
138
+ * Create a transformer that tries multiple paths and returns first success
139
+ */
140
+ export declare function tryPaths<TIn, TOut>(...paths: string[]): Transformer<TIn, TOut | undefined>;
141
+ /**
142
+ * Conditional transformer
143
+ */
144
+ export declare function when<T>(predicate: (input: T) => boolean, thenTransform: Transformer<T, T>, elseTransform?: Transformer<T, T>): Transformer<T, T>;
145
+ /**
146
+ * Process arXiv search results into normalized papers
147
+ */
148
+ export declare function processArxivResults(): Transformer<any, NormalizedPaper[]>;
149
+ /**
150
+ * Process web search results into normalized format
151
+ */
152
+ export declare function processSearchResults(): Transformer<any, NormalizedSearchResult[]>;
153
+ /**
154
+ * Extract and summarize key information
155
+ */
156
+ export declare function extractKeyInfo(maxItems?: number): Transformer<any, string[]>;