cognitive-modules-cli 1.2.0 → 1.3.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,185 @@
1
+ /**
2
+ * Subagent - Orchestrate module calls with isolated execution contexts.
3
+ *
4
+ * Supports:
5
+ * - @call:module-name - Call another module
6
+ * - @call:module-name(args) - Call with arguments
7
+ * - context: fork - Isolated execution (no shared state)
8
+ * - context: main - Shared execution (default)
9
+ */
10
+ import { findModule, getDefaultSearchPaths } from './loader.js';
11
+ import { runModule } from './runner.js';
12
+ // =============================================================================
13
+ // Context Management
14
+ // =============================================================================
15
+ /**
16
+ * Create a new root context
17
+ */
18
+ export function createContext(maxDepth = 5) {
19
+ return {
20
+ parentId: null,
21
+ depth: 0,
22
+ maxDepth,
23
+ results: {},
24
+ isolated: false
25
+ };
26
+ }
27
+ /**
28
+ * Fork context (isolated - no inherited results)
29
+ */
30
+ export function forkContext(ctx, moduleName) {
31
+ return {
32
+ parentId: moduleName,
33
+ depth: ctx.depth + 1,
34
+ maxDepth: ctx.maxDepth,
35
+ results: {},
36
+ isolated: true
37
+ };
38
+ }
39
+ /**
40
+ * Extend context (shared - inherits results)
41
+ */
42
+ export function extendContext(ctx, moduleName) {
43
+ return {
44
+ parentId: moduleName,
45
+ depth: ctx.depth + 1,
46
+ maxDepth: ctx.maxDepth,
47
+ results: { ...ctx.results },
48
+ isolated: false
49
+ };
50
+ }
51
+ // =============================================================================
52
+ // Call Parsing
53
+ // =============================================================================
54
+ // Pattern to match @call:module-name or @call:module-name(args)
55
+ const CALL_PATTERN = /@call:([a-zA-Z0-9_-]+)(?:\(([^)]*)\))?/g;
56
+ /**
57
+ * Parse @call directives from text
58
+ */
59
+ export function parseCalls(text) {
60
+ const calls = [];
61
+ let match;
62
+ // Reset regex state
63
+ CALL_PATTERN.lastIndex = 0;
64
+ while ((match = CALL_PATTERN.exec(text)) !== null) {
65
+ calls.push({
66
+ module: match[1],
67
+ args: match[2] || '',
68
+ match: match[0]
69
+ });
70
+ }
71
+ return calls;
72
+ }
73
+ /**
74
+ * Replace @call directives with their results
75
+ */
76
+ export function substituteCallResults(text, callResults) {
77
+ let result = text;
78
+ for (const [callStr, callResult] of Object.entries(callResults)) {
79
+ const resultStr = typeof callResult === 'object'
80
+ ? JSON.stringify(callResult, null, 2)
81
+ : String(callResult);
82
+ result = result.replace(callStr, `[Result from ${callStr}]:\n${resultStr}`);
83
+ }
84
+ return result;
85
+ }
86
+ // =============================================================================
87
+ // Orchestrator
88
+ // =============================================================================
89
+ export class SubagentOrchestrator {
90
+ provider;
91
+ running = new Set();
92
+ cwd;
93
+ constructor(provider, cwd = process.cwd()) {
94
+ this.provider = provider;
95
+ this.cwd = cwd;
96
+ }
97
+ /**
98
+ * Run a module with subagent support.
99
+ * Recursively resolves @call directives before final execution.
100
+ */
101
+ async run(moduleName, options = {}, context) {
102
+ const { input = {}, validateInput = true, validateOutput = true, maxDepth = 5 } = options;
103
+ // Initialize context
104
+ const ctx = context ?? createContext(maxDepth);
105
+ // Check depth limit
106
+ if (ctx.depth > ctx.maxDepth) {
107
+ throw new Error(`Max subagent depth (${ctx.maxDepth}) exceeded. Check for circular calls.`);
108
+ }
109
+ // Prevent circular calls
110
+ if (this.running.has(moduleName)) {
111
+ throw new Error(`Circular call detected: ${moduleName}`);
112
+ }
113
+ this.running.add(moduleName);
114
+ try {
115
+ // Find and load module
116
+ const searchPaths = getDefaultSearchPaths(this.cwd);
117
+ const module = await findModule(moduleName, searchPaths);
118
+ if (!module) {
119
+ throw new Error(`Module not found: ${moduleName}`);
120
+ }
121
+ // Check if this module wants isolated execution
122
+ const moduleContextMode = module.context ?? 'main';
123
+ // Parse @call directives from prompt
124
+ const calls = parseCalls(module.prompt);
125
+ const callResults = {};
126
+ // Resolve each @call directive
127
+ for (const call of calls) {
128
+ const childModule = call.module;
129
+ const childArgs = call.args;
130
+ // Prepare child input
131
+ const childInput = childArgs
132
+ ? { query: childArgs, code: childArgs }
133
+ : { ...input };
134
+ // Determine child context
135
+ const childContext = moduleContextMode === 'fork'
136
+ ? forkContext(ctx, moduleName)
137
+ : extendContext(ctx, moduleName);
138
+ // Recursively run child module
139
+ const childResult = await this.run(childModule, {
140
+ input: childInput,
141
+ validateInput: false, // Skip validation for @call args
142
+ validateOutput
143
+ }, childContext);
144
+ // Store result
145
+ if (childResult.ok && 'data' in childResult) {
146
+ callResults[call.match] = childResult.data;
147
+ }
148
+ else if ('error' in childResult) {
149
+ callResults[call.match] = { error: childResult.error };
150
+ }
151
+ }
152
+ // Substitute call results into prompt
153
+ let modifiedModule = module;
154
+ if (Object.keys(callResults).length > 0) {
155
+ const modifiedPrompt = substituteCallResults(module.prompt, callResults);
156
+ modifiedModule = {
157
+ ...module,
158
+ prompt: modifiedPrompt + '\n\n## Subagent Results Available\nThe @call results have been injected above. Use them in your response.\n'
159
+ };
160
+ }
161
+ // Run the module
162
+ const result = await runModule(modifiedModule, this.provider, {
163
+ input,
164
+ verbose: false,
165
+ useV22: true
166
+ });
167
+ // Store result in context
168
+ if (result.ok && 'data' in result) {
169
+ ctx.results[moduleName] = result.data;
170
+ }
171
+ return result;
172
+ }
173
+ finally {
174
+ this.running.delete(moduleName);
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * Convenience function to run a module with subagent support
180
+ */
181
+ export async function runWithSubagents(moduleName, provider, options = {}) {
182
+ const { cwd = process.cwd(), ...runOptions } = options;
183
+ const orchestrator = new SubagentOrchestrator(provider, cwd);
184
+ return orchestrator.run(moduleName, runOptions);
185
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cognitive Modules HTTP API Server
3
+ *
4
+ * Provides RESTful API interface for workflow platform integration.
5
+ *
6
+ * Start with:
7
+ * cog serve --port 8000
8
+ *
9
+ * Environment variables:
10
+ * COGNITIVE_API_KEY - API Key authentication (optional)
11
+ * OPENAI_API_KEY, ANTHROPIC_API_KEY, etc. - LLM provider keys
12
+ */
13
+ import http from 'node:http';
14
+ export interface ServeOptions {
15
+ host?: string;
16
+ port?: number;
17
+ cwd?: string;
18
+ }
19
+ export declare function createServer(options?: ServeOptions): http.Server;
20
+ export declare function serve(options?: ServeOptions): Promise<void>;
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Cognitive Modules HTTP API Server
3
+ *
4
+ * Provides RESTful API interface for workflow platform integration.
5
+ *
6
+ * Start with:
7
+ * cog serve --port 8000
8
+ *
9
+ * Environment variables:
10
+ * COGNITIVE_API_KEY - API Key authentication (optional)
11
+ * OPENAI_API_KEY, ANTHROPIC_API_KEY, etc. - LLM provider keys
12
+ */
13
+ import http from 'node:http';
14
+ import { URL } from 'node:url';
15
+ import { findModule, listModules, getDefaultSearchPaths } from '../modules/loader.js';
16
+ import { runModule } from '../modules/runner.js';
17
+ import { getProvider } from '../providers/index.js';
18
+ // =============================================================================
19
+ // Helpers
20
+ // =============================================================================
21
+ function jsonResponse(res, status, data) {
22
+ res.writeHead(status, {
23
+ 'Content-Type': 'application/json',
24
+ 'Access-Control-Allow-Origin': '*',
25
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
26
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
27
+ });
28
+ res.end(JSON.stringify(data, null, 2));
29
+ }
30
+ function parseBody(req) {
31
+ return new Promise((resolve, reject) => {
32
+ let body = '';
33
+ req.on('data', (chunk) => (body += chunk));
34
+ req.on('end', () => resolve(body));
35
+ req.on('error', reject);
36
+ });
37
+ }
38
+ function verifyApiKey(req) {
39
+ const expectedKey = process.env.COGNITIVE_API_KEY;
40
+ if (!expectedKey)
41
+ return true; // No auth required
42
+ const authHeader = req.headers.authorization;
43
+ if (!authHeader)
44
+ return false;
45
+ const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader;
46
+ return token === expectedKey;
47
+ }
48
+ // =============================================================================
49
+ // Request Handlers
50
+ // =============================================================================
51
+ async function handleRoot(res) {
52
+ jsonResponse(res, 200, {
53
+ name: 'Cognitive Modules API',
54
+ version: '1.2.0',
55
+ docs: '/docs',
56
+ endpoints: {
57
+ run: 'POST /run',
58
+ modules: 'GET /modules',
59
+ module_info: 'GET /modules/{name}',
60
+ health: 'GET /health',
61
+ },
62
+ });
63
+ }
64
+ async function handleHealth(res) {
65
+ const providers = {
66
+ openai: Boolean(process.env.OPENAI_API_KEY),
67
+ anthropic: Boolean(process.env.ANTHROPIC_API_KEY),
68
+ minimax: Boolean(process.env.MINIMAX_API_KEY),
69
+ deepseek: Boolean(process.env.DEEPSEEK_API_KEY),
70
+ gemini: Boolean(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY),
71
+ qwen: Boolean(process.env.QWEN_API_KEY || process.env.DASHSCOPE_API_KEY),
72
+ };
73
+ jsonResponse(res, 200, {
74
+ status: 'healthy',
75
+ version: '1.2.0',
76
+ providers,
77
+ });
78
+ }
79
+ async function handleModules(res, searchPaths) {
80
+ const modules = await listModules(searchPaths);
81
+ const moduleInfos = modules.map((m) => ({
82
+ name: m.name,
83
+ version: m.version,
84
+ description: m.responsibility,
85
+ format: m.format,
86
+ path: m.location,
87
+ responsibility: m.responsibility,
88
+ tier: m.tier,
89
+ }));
90
+ jsonResponse(res, 200, {
91
+ modules: moduleInfos,
92
+ count: moduleInfos.length,
93
+ });
94
+ }
95
+ async function handleModuleInfo(res, moduleName, searchPaths) {
96
+ const moduleData = await findModule(moduleName, searchPaths);
97
+ if (!moduleData) {
98
+ jsonResponse(res, 404, { error: `Module '${moduleName}' not found` });
99
+ return;
100
+ }
101
+ jsonResponse(res, 200, {
102
+ name: moduleData.name,
103
+ version: moduleData.version,
104
+ description: moduleData.responsibility,
105
+ format: moduleData.format,
106
+ path: moduleData.location,
107
+ responsibility: moduleData.responsibility,
108
+ tier: moduleData.tier,
109
+ inputSchema: moduleData.inputSchema,
110
+ outputSchema: moduleData.outputSchema,
111
+ });
112
+ }
113
+ async function handleRun(req, res, searchPaths) {
114
+ // Verify API key
115
+ if (!verifyApiKey(req)) {
116
+ jsonResponse(res, 401, {
117
+ error: 'Missing or invalid API Key. Use header: Authorization: Bearer <your-api-key>',
118
+ });
119
+ return;
120
+ }
121
+ // Parse request body
122
+ let request;
123
+ try {
124
+ const body = await parseBody(req);
125
+ request = JSON.parse(body);
126
+ }
127
+ catch {
128
+ jsonResponse(res, 400, { error: 'Invalid JSON body' });
129
+ return;
130
+ }
131
+ // Validate request
132
+ if (!request.module || !request.args) {
133
+ jsonResponse(res, 400, { error: 'Missing required fields: module, args' });
134
+ return;
135
+ }
136
+ // Find module
137
+ const moduleData = await findModule(request.module, searchPaths);
138
+ if (!moduleData) {
139
+ jsonResponse(res, 404, { error: `Module '${request.module}' not found` });
140
+ return;
141
+ }
142
+ try {
143
+ // Create provider
144
+ const provider = getProvider(request.provider, request.model);
145
+ // Run module
146
+ const result = await runModule(moduleData, provider, {
147
+ input: { query: request.args, code: request.args },
148
+ useV22: true,
149
+ });
150
+ const response = {
151
+ ok: result.ok,
152
+ module: request.module,
153
+ provider: request.provider || process.env.LLM_PROVIDER || 'openai',
154
+ };
155
+ if (result.ok) {
156
+ if ('meta' in result)
157
+ response.meta = result.meta;
158
+ if ('data' in result)
159
+ response.data = result.data;
160
+ }
161
+ else {
162
+ if ('error' in result)
163
+ response.error = result.error?.message;
164
+ }
165
+ jsonResponse(res, 200, response);
166
+ }
167
+ catch (error) {
168
+ jsonResponse(res, 500, {
169
+ ok: false,
170
+ error: error instanceof Error ? error.message : String(error),
171
+ module: request.module,
172
+ });
173
+ }
174
+ }
175
+ export function createServer(options = {}) {
176
+ const { cwd = process.cwd() } = options;
177
+ const searchPaths = getDefaultSearchPaths(cwd);
178
+ const server = http.createServer(async (req, res) => {
179
+ const url = new URL(req.url || '/', `http://${req.headers.host}`);
180
+ const path = url.pathname;
181
+ const method = req.method?.toUpperCase();
182
+ // Handle CORS preflight
183
+ if (method === 'OPTIONS') {
184
+ res.writeHead(204, {
185
+ 'Access-Control-Allow-Origin': '*',
186
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
187
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
188
+ });
189
+ res.end();
190
+ return;
191
+ }
192
+ try {
193
+ // Route requests
194
+ if (path === '/' && method === 'GET') {
195
+ await handleRoot(res);
196
+ }
197
+ else if (path === '/health' && method === 'GET') {
198
+ await handleHealth(res);
199
+ }
200
+ else if (path === '/modules' && method === 'GET') {
201
+ await handleModules(res, searchPaths);
202
+ }
203
+ else if (path.startsWith('/modules/') && method === 'GET') {
204
+ const moduleName = path.slice('/modules/'.length);
205
+ await handleModuleInfo(res, moduleName, searchPaths);
206
+ }
207
+ else if (path === '/run' && method === 'POST') {
208
+ await handleRun(req, res, searchPaths);
209
+ }
210
+ else {
211
+ jsonResponse(res, 404, { error: 'Not found' });
212
+ }
213
+ }
214
+ catch (error) {
215
+ console.error('Server error:', error);
216
+ jsonResponse(res, 500, {
217
+ error: error instanceof Error ? error.message : 'Internal server error',
218
+ });
219
+ }
220
+ });
221
+ return server;
222
+ }
223
+ export async function serve(options = {}) {
224
+ const { host = '0.0.0.0', port = 8000 } = options;
225
+ const server = createServer(options);
226
+ return new Promise((resolve, reject) => {
227
+ server.on('error', reject);
228
+ server.listen(port, host, () => {
229
+ console.log(`Cognitive Modules HTTP Server running at http://${host}:${port}`);
230
+ console.log('Endpoints:');
231
+ console.log(' GET / - API info');
232
+ console.log(' GET /health - Health check');
233
+ console.log(' GET /modules - List modules');
234
+ console.log(' GET /modules/:name - Module info');
235
+ console.log(' POST /run - Run module');
236
+ resolve();
237
+ });
238
+ });
239
+ }
240
+ // Allow running directly
241
+ if (import.meta.url === `file://${process.argv[1]}`) {
242
+ serve().catch(console.error);
243
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Server - Re-export all server functionality
3
+ */
4
+ export { serve, createServer } from './http.js';
5
+ export type { ServeOptions } from './http.js';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Server - Re-export all server functionality
3
+ */
4
+ export { serve, createServer } from './http.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognitive-modules-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Cognitive Modules - Structured AI Task Execution with version management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -39,6 +39,9 @@
39
39
  "dependencies": {
40
40
  "js-yaml": "^4.1.1"
41
41
  },
42
+ "optionalDependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.0.0"
44
+ },
42
45
  "devDependencies": {
43
46
  "@types/js-yaml": "^4.0.9",
44
47
  "@types/node": "^22.0.0",
package/src/cli.ts CHANGED
@@ -19,7 +19,7 @@ import { getProvider, listProviders } from './providers/index.js';
19
19
  import { run, list, pipe, init, add, update, remove, versions } from './commands/index.js';
20
20
  import type { CommandContext } from './types.js';
21
21
 
22
- const VERSION = '1.0.1';
22
+ const VERSION = '1.3.0';
23
23
 
24
24
  async function main() {
25
25
  const args = process.argv.slice(2);
@@ -52,6 +52,9 @@ async function main() {
52
52
  tag: { type: 'string', short: 't' },
53
53
  branch: { type: 'string', short: 'b' },
54
54
  limit: { type: 'string', short: 'l' },
55
+ // Server options
56
+ host: { type: 'string', short: 'H' },
57
+ port: { type: 'string', short: 'P' },
55
58
  },
56
59
  allowPositionals: true,
57
60
  });
@@ -288,6 +291,30 @@ async function main() {
288
291
  break;
289
292
  }
290
293
 
294
+ case 'serve': {
295
+ const { serve } = await import('./server/http.js');
296
+ const port = values.port ? parseInt(values.port as string, 10) : 8000;
297
+ const host = (values.host as string) || '0.0.0.0';
298
+ console.log('Starting Cognitive Modules HTTP Server...');
299
+ await serve({ host, port, cwd: ctx.cwd });
300
+ break;
301
+ }
302
+
303
+ case 'mcp': {
304
+ try {
305
+ const { serve: serveMcp } = await import('./mcp/server.js');
306
+ await serveMcp();
307
+ } catch (e) {
308
+ if (e instanceof Error && e.message.includes('Cannot find module')) {
309
+ console.error('MCP dependencies not installed.');
310
+ console.error('Install with: npm install @modelcontextprotocol/sdk');
311
+ process.exit(1);
312
+ }
313
+ throw e;
314
+ }
315
+ break;
316
+ }
317
+
291
318
  default:
292
319
  console.error(`Unknown command: ${command}`);
293
320
  console.error('Run "cog --help" for usage.');
@@ -319,6 +346,8 @@ COMMANDS:
319
346
  versions <url> List available versions
320
347
  pipe Pipe mode (stdin/stdout)
321
348
  init [name] Initialize project or create module
349
+ serve Start HTTP API server
350
+ mcp Start MCP server (for Claude Code, Cursor)
322
351
  doctor Check configuration
323
352
 
324
353
  OPTIONS:
@@ -333,6 +362,8 @@ OPTIONS:
333
362
  --pretty Pretty-print JSON output
334
363
  -V, --verbose Verbose output
335
364
  --no-validate Skip schema validation
365
+ -H, --host <host> Server host (default: 0.0.0.0)
366
+ -P, --port <port> Server port (default: 8000)
336
367
  -v, --version Show version
337
368
  -h, --help Show this help
338
369
 
@@ -351,6 +382,10 @@ EXAMPLES:
351
382
  cog run code-reviewer --provider openai --model gpt-4o --args "..."
352
383
  cog list
353
384
 
385
+ # Servers
386
+ cog serve --port 8080
387
+ cog mcp
388
+
354
389
  # Other
355
390
  echo "review this code" | cog pipe --module code-reviewer
356
391
  cog init my-module
@@ -0,0 +1,5 @@
1
+ /**
2
+ * MCP Server - Re-export all MCP functionality
3
+ */
4
+
5
+ export { serve } from './server.js';