cognitive-modules-cli 2.2.0 → 2.2.5

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 (94) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +35 -29
  4. package/dist/cli.js +572 -28
  5. package/dist/commands/add.d.ts +33 -14
  6. package/dist/commands/add.js +222 -13
  7. package/dist/commands/compose.d.ts +31 -0
  8. package/dist/commands/compose.js +185 -0
  9. package/dist/commands/index.d.ts +5 -0
  10. package/dist/commands/index.js +5 -0
  11. package/dist/commands/init.js +23 -1
  12. package/dist/commands/migrate.d.ts +30 -0
  13. package/dist/commands/migrate.js +650 -0
  14. package/dist/commands/pipe.d.ts +1 -0
  15. package/dist/commands/pipe.js +31 -11
  16. package/dist/commands/remove.js +33 -2
  17. package/dist/commands/run.d.ts +1 -0
  18. package/dist/commands/run.js +37 -27
  19. package/dist/commands/search.d.ts +28 -0
  20. package/dist/commands/search.js +143 -0
  21. package/dist/commands/test.d.ts +65 -0
  22. package/dist/commands/test.js +454 -0
  23. package/dist/commands/update.d.ts +1 -0
  24. package/dist/commands/update.js +106 -14
  25. package/dist/commands/validate.d.ts +36 -0
  26. package/dist/commands/validate.js +97 -0
  27. package/dist/errors/index.d.ts +218 -0
  28. package/dist/errors/index.js +412 -0
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.js +5 -1
  31. package/dist/mcp/server.js +84 -79
  32. package/dist/modules/composition.d.ts +251 -0
  33. package/dist/modules/composition.js +1330 -0
  34. package/dist/modules/index.d.ts +2 -0
  35. package/dist/modules/index.js +2 -0
  36. package/dist/modules/loader.d.ts +22 -2
  37. package/dist/modules/loader.js +171 -6
  38. package/dist/modules/runner.d.ts +422 -1
  39. package/dist/modules/runner.js +1472 -71
  40. package/dist/modules/subagent.d.ts +6 -1
  41. package/dist/modules/subagent.js +20 -13
  42. package/dist/modules/validator.d.ts +28 -0
  43. package/dist/modules/validator.js +637 -0
  44. package/dist/providers/anthropic.d.ts +15 -0
  45. package/dist/providers/anthropic.js +147 -5
  46. package/dist/providers/base.d.ts +11 -0
  47. package/dist/providers/base.js +18 -0
  48. package/dist/providers/gemini.d.ts +15 -0
  49. package/dist/providers/gemini.js +122 -5
  50. package/dist/providers/ollama.d.ts +15 -0
  51. package/dist/providers/ollama.js +111 -3
  52. package/dist/providers/openai.d.ts +11 -0
  53. package/dist/providers/openai.js +133 -0
  54. package/dist/registry/client.d.ts +204 -0
  55. package/dist/registry/client.js +356 -0
  56. package/dist/registry/index.d.ts +4 -0
  57. package/dist/registry/index.js +4 -0
  58. package/dist/server/http.js +173 -42
  59. package/dist/types.d.ts +123 -8
  60. package/dist/types.js +4 -1
  61. package/dist/version.d.ts +1 -0
  62. package/dist/version.js +4 -0
  63. package/package.json +32 -7
  64. package/src/cli.ts +0 -410
  65. package/src/commands/add.ts +0 -315
  66. package/src/commands/index.ts +0 -12
  67. package/src/commands/init.ts +0 -94
  68. package/src/commands/list.ts +0 -33
  69. package/src/commands/pipe.ts +0 -76
  70. package/src/commands/remove.ts +0 -57
  71. package/src/commands/run.ts +0 -80
  72. package/src/commands/update.ts +0 -130
  73. package/src/commands/versions.ts +0 -79
  74. package/src/index.ts +0 -55
  75. package/src/mcp/index.ts +0 -5
  76. package/src/mcp/server.ts +0 -403
  77. package/src/modules/index.ts +0 -7
  78. package/src/modules/loader.ts +0 -318
  79. package/src/modules/runner.ts +0 -495
  80. package/src/modules/subagent.ts +0 -275
  81. package/src/providers/anthropic.ts +0 -89
  82. package/src/providers/base.ts +0 -29
  83. package/src/providers/deepseek.ts +0 -83
  84. package/src/providers/gemini.ts +0 -117
  85. package/src/providers/index.ts +0 -78
  86. package/src/providers/minimax.ts +0 -81
  87. package/src/providers/moonshot.ts +0 -82
  88. package/src/providers/ollama.ts +0 -83
  89. package/src/providers/openai.ts +0 -84
  90. package/src/providers/qwen.ts +0 -82
  91. package/src/server/http.ts +0 -316
  92. package/src/server/index.ts +0 -6
  93. package/src/types.ts +0 -495
  94. package/tsconfig.json +0 -17
@@ -1,495 +0,0 @@
1
- /**
2
- * Module Runner - Execute Cognitive Modules
3
- * v2.2: Envelope format with meta/data separation, risk_rule, repair pass
4
- */
5
-
6
- import type {
7
- Provider,
8
- CognitiveModule,
9
- ModuleResult,
10
- ModuleResultV21,
11
- ModuleResultV22,
12
- Message,
13
- ModuleInput,
14
- EnvelopeResponse,
15
- EnvelopeResponseV22,
16
- EnvelopeMeta,
17
- ModuleResultData,
18
- RiskLevel,
19
- RiskRule
20
- } from '../types.js';
21
- import { aggregateRisk, isV22Envelope } from '../types.js';
22
-
23
- export interface RunOptions {
24
- // Clean input (v2 style)
25
- input?: ModuleInput;
26
-
27
- // Legacy CLI args (v1 compatibility) - mapped to input.code or input.query
28
- args?: string;
29
-
30
- // Runtime options
31
- verbose?: boolean;
32
-
33
- // Force envelope format (default: auto-detect from module.output.envelope)
34
- useEnvelope?: boolean;
35
-
36
- // Force v2.2 format (default: auto-detect from module.tier)
37
- useV22?: boolean;
38
-
39
- // Enable repair pass for validation failures (default: true)
40
- enableRepair?: boolean;
41
- }
42
-
43
- // =============================================================================
44
- // Repair Pass (v2.2)
45
- // =============================================================================
46
-
47
- /**
48
- * Attempt to repair envelope format issues without changing semantics.
49
- *
50
- * Repairs (lossless only):
51
- * - Missing meta fields (fill with conservative defaults)
52
- * - Truncate explain if too long
53
- * - Trim whitespace from string fields
54
- *
55
- * Does NOT repair:
56
- * - Invalid enum values (treated as validation failure)
57
- */
58
- function repairEnvelope(
59
- response: Record<string, unknown>,
60
- riskRule: RiskRule = 'max_changes_risk',
61
- maxExplainLength: number = 280
62
- ): EnvelopeResponseV22<unknown> {
63
- const repaired = { ...response };
64
-
65
- // Ensure meta exists
66
- if (!repaired.meta || typeof repaired.meta !== 'object') {
67
- repaired.meta = {};
68
- }
69
-
70
- const meta = repaired.meta as Record<string, unknown>;
71
- const data = (repaired.data ?? {}) as Record<string, unknown>;
72
-
73
- // Repair confidence
74
- if (typeof meta.confidence !== 'number') {
75
- meta.confidence = (data.confidence as number) ?? 0.5;
76
- }
77
- meta.confidence = Math.max(0, Math.min(1, meta.confidence as number));
78
-
79
- // Repair risk using configurable aggregation rule
80
- if (!meta.risk) {
81
- meta.risk = aggregateRisk(data, riskRule);
82
- }
83
- // Trim whitespace only (lossless), validate is valid RiskLevel
84
- if (typeof meta.risk === 'string') {
85
- const trimmedRisk = meta.risk.trim().toLowerCase();
86
- const validRisks = ['none', 'low', 'medium', 'high'];
87
- meta.risk = validRisks.includes(trimmedRisk) ? trimmedRisk : 'medium';
88
- } else {
89
- meta.risk = 'medium'; // Default for invalid type
90
- }
91
-
92
- // Repair explain
93
- if (typeof meta.explain !== 'string') {
94
- const rationale = data.rationale as string | undefined;
95
- meta.explain = rationale ? String(rationale).slice(0, maxExplainLength) : 'No explanation provided';
96
- }
97
- // Trim whitespace (lossless)
98
- const explainStr = meta.explain as string;
99
- meta.explain = explainStr.trim();
100
- if ((meta.explain as string).length > maxExplainLength) {
101
- meta.explain = (meta.explain as string).slice(0, maxExplainLength - 3) + '...';
102
- }
103
-
104
- // Build proper v2.2 response
105
- const builtMeta: EnvelopeMeta = {
106
- confidence: meta.confidence as number,
107
- risk: meta.risk as RiskLevel,
108
- explain: meta.explain as string
109
- };
110
-
111
- const result: EnvelopeResponseV22<unknown> = repaired.ok === false ? {
112
- ok: false,
113
- meta: builtMeta,
114
- error: (repaired.error as { code: string; message: string }) ?? { code: 'UNKNOWN', message: 'Unknown error' },
115
- partial_data: repaired.partial_data
116
- } : {
117
- ok: true,
118
- meta: builtMeta,
119
- data: repaired.data
120
- };
121
-
122
- return result;
123
- }
124
-
125
- /**
126
- * Wrap v2.1 response to v2.2 format
127
- */
128
- function wrapV21ToV22(
129
- response: EnvelopeResponse<unknown>,
130
- riskRule: RiskRule = 'max_changes_risk'
131
- ): EnvelopeResponseV22<unknown> {
132
- if (isV22Envelope(response)) {
133
- return response;
134
- }
135
-
136
- if (response.ok) {
137
- const data = (response.data ?? {}) as Record<string, unknown>;
138
- const confidence = (data.confidence as number) ?? 0.5;
139
- const rationale = (data.rationale as string) ?? '';
140
-
141
- return {
142
- ok: true,
143
- meta: {
144
- confidence,
145
- risk: aggregateRisk(data, riskRule),
146
- explain: rationale.slice(0, 280) || 'No explanation provided'
147
- },
148
- data: data as ModuleResultData
149
- };
150
- } else {
151
- const errorMsg = response.error?.message ?? 'Unknown error';
152
- return {
153
- ok: false,
154
- meta: {
155
- confidence: 0,
156
- risk: 'high',
157
- explain: errorMsg.slice(0, 280)
158
- },
159
- error: response.error ?? { code: 'UNKNOWN', message: errorMsg },
160
- partial_data: response.partial_data
161
- };
162
- }
163
- }
164
-
165
- export async function runModule(
166
- module: CognitiveModule,
167
- provider: Provider,
168
- options: RunOptions = {}
169
- ): Promise<ModuleResult> {
170
- const { args, input, verbose = false, useEnvelope, useV22, enableRepair = true } = options;
171
-
172
- // Determine if we should use envelope format
173
- const shouldUseEnvelope = useEnvelope ?? (module.output?.envelope === true || module.format === 'v2');
174
-
175
- // Determine if we should use v2.2 format
176
- const isV22Module = module.tier !== undefined || module.formatVersion === 'v2.2';
177
- const shouldUseV22 = useV22 ?? (isV22Module || module.compat?.runtime_auto_wrap === true);
178
-
179
- // Get risk_rule from module config
180
- const riskRule: RiskRule = module.metaConfig?.risk_rule ?? 'max_changes_risk';
181
-
182
- // Build clean input data (v2 style: no $ARGUMENTS pollution)
183
- const inputData: ModuleInput = input || {};
184
-
185
- // Map legacy --args to clean input
186
- if (args && !inputData.code && !inputData.query) {
187
- // Determine if args looks like code or natural language
188
- if (looksLikeCode(args)) {
189
- inputData.code = args;
190
- } else {
191
- inputData.query = args;
192
- }
193
- }
194
-
195
- // Build prompt with clean substitution
196
- const prompt = buildPrompt(module, inputData);
197
-
198
- if (verbose) {
199
- console.error('--- Module ---');
200
- console.error(`Name: ${module.name} (${module.format})`);
201
- console.error(`Responsibility: ${module.responsibility}`);
202
- console.error(`Envelope: ${shouldUseEnvelope}`);
203
- console.error('--- Input ---');
204
- console.error(JSON.stringify(inputData, null, 2));
205
- console.error('--- Prompt ---');
206
- console.error(prompt);
207
- console.error('--- End ---');
208
- }
209
-
210
- // Build system message based on module config
211
- const systemParts: string[] = [
212
- `You are executing the "${module.name}" Cognitive Module.`,
213
- '',
214
- `RESPONSIBILITY: ${module.responsibility}`,
215
- ];
216
-
217
- if (module.excludes.length > 0) {
218
- systemParts.push('', 'YOU MUST NOT:');
219
- module.excludes.forEach(e => systemParts.push(`- ${e}`));
220
- }
221
-
222
- if (module.constraints) {
223
- systemParts.push('', 'CONSTRAINTS:');
224
- if (module.constraints.no_network) systemParts.push('- No network access');
225
- if (module.constraints.no_side_effects) systemParts.push('- No side effects');
226
- if (module.constraints.no_file_write) systemParts.push('- No file writes');
227
- if (module.constraints.no_inventing_data) systemParts.push('- Do not invent data');
228
- }
229
-
230
- if (module.output?.require_behavior_equivalence) {
231
- systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
232
- systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
233
- systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
234
-
235
- const maxConfidence = module.constraints?.behavior_equivalence_false_max_confidence ?? 0.7;
236
- systemParts.push(`- If behavior_equivalence=false, confidence MUST be <= ${maxConfidence}`);
237
- }
238
-
239
- // Add envelope format instructions
240
- if (shouldUseEnvelope) {
241
- if (shouldUseV22) {
242
- systemParts.push('', 'RESPONSE FORMAT (Envelope v2.2):');
243
- systemParts.push('- Wrap your response in the v2.2 envelope format with separate meta and data');
244
- systemParts.push('- Success: { "ok": true, "meta": { "confidence": 0.9, "risk": "low", "explain": "short summary" }, "data": { ...payload... } }');
245
- systemParts.push('- Error: { "ok": false, "meta": { "confidence": 0.0, "risk": "high", "explain": "error summary" }, "error": { "code": "ERROR_CODE", "message": "..." } }');
246
- systemParts.push('- meta.explain must be ≤280 characters. data.rationale can be longer for detailed reasoning.');
247
- systemParts.push('- meta.risk must be one of: "none", "low", "medium", "high"');
248
- } else {
249
- systemParts.push('', 'RESPONSE FORMAT (Envelope):');
250
- systemParts.push('- Wrap your response in the envelope format');
251
- systemParts.push('- Success: { "ok": true, "data": { ...your output... } }');
252
- systemParts.push('- Error: { "ok": false, "error": { "code": "ERROR_CODE", "message": "..." } }');
253
- systemParts.push('- Include "confidence" (0-1) and "rationale" in data');
254
- }
255
- if (module.output?.require_behavior_equivalence) {
256
- systemParts.push('- Include "behavior_equivalence" (boolean) in data');
257
- }
258
- } else {
259
- systemParts.push('', 'OUTPUT FORMAT:');
260
- systemParts.push('- Respond with ONLY valid JSON');
261
- systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
262
- if (module.output?.require_behavior_equivalence) {
263
- systemParts.push('- Include "behavior_equivalence" (boolean) field');
264
- }
265
- }
266
-
267
- const messages: Message[] = [
268
- { role: 'system', content: systemParts.join('\n') },
269
- { role: 'user', content: prompt },
270
- ];
271
-
272
- // Invoke provider
273
- const result = await provider.invoke({
274
- messages,
275
- jsonSchema: module.outputSchema,
276
- temperature: 0.3,
277
- });
278
-
279
- if (verbose) {
280
- console.error('--- Response ---');
281
- console.error(result.content);
282
- console.error('--- End Response ---');
283
- }
284
-
285
- // Parse response
286
- let parsed: unknown;
287
- try {
288
- const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
289
- const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
290
- parsed = JSON.parse(jsonStr.trim());
291
- } catch {
292
- throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
293
- }
294
-
295
- // Handle envelope format
296
- if (shouldUseEnvelope && isEnvelopeResponse(parsed)) {
297
- let response = parseEnvelopeResponse(parsed, result.content);
298
-
299
- // Upgrade to v2.2 if needed
300
- if (shouldUseV22 && response.ok && !('meta' in response && response.meta)) {
301
- const upgraded = wrapV21ToV22(parsed as EnvelopeResponse<unknown>, riskRule);
302
- response = {
303
- ok: true,
304
- meta: upgraded.meta as EnvelopeMeta,
305
- data: (upgraded as { data?: ModuleResultData }).data,
306
- raw: result.content
307
- } as ModuleResultV22;
308
- }
309
-
310
- // Apply repair pass if enabled and response needs it
311
- if (enableRepair && response.ok && shouldUseV22) {
312
- const repaired = repairEnvelope(
313
- response as unknown as Record<string, unknown>,
314
- riskRule
315
- );
316
- response = {
317
- ok: true,
318
- meta: repaired.meta as EnvelopeMeta,
319
- data: (repaired as { data?: ModuleResultData }).data,
320
- raw: result.content
321
- } as ModuleResultV22;
322
- }
323
-
324
- return response;
325
- }
326
-
327
- // Handle legacy format (non-envelope)
328
- const legacyResult = parseLegacyResponse(parsed, result.content);
329
-
330
- // Upgrade to v2.2 if requested
331
- if (shouldUseV22 && legacyResult.ok) {
332
- const data = (legacyResult.data ?? {}) as Record<string, unknown>;
333
- return {
334
- ok: true,
335
- meta: {
336
- confidence: (data.confidence as number) ?? 0.5,
337
- risk: aggregateRisk(data, riskRule),
338
- explain: ((data.rationale as string) ?? '').slice(0, 280) || 'No explanation provided'
339
- },
340
- data: legacyResult.data,
341
- raw: result.content
342
- } as ModuleResultV22;
343
- }
344
-
345
- return legacyResult;
346
- }
347
-
348
- /**
349
- * Check if response is in envelope format
350
- */
351
- function isEnvelopeResponse(obj: unknown): obj is EnvelopeResponse {
352
- if (typeof obj !== 'object' || obj === null) return false;
353
- const o = obj as Record<string, unknown>;
354
- return typeof o.ok === 'boolean';
355
- }
356
-
357
- /**
358
- * Parse envelope format response (supports both v2.1 and v2.2)
359
- */
360
- function parseEnvelopeResponse(response: EnvelopeResponse<unknown>, raw: string): ModuleResult {
361
- // Check if v2.2 format (has meta)
362
- if (isV22Envelope(response)) {
363
- if (response.ok) {
364
- return {
365
- ok: true,
366
- meta: response.meta,
367
- data: response.data as ModuleResultData,
368
- raw,
369
- } as ModuleResultV22;
370
- } else {
371
- return {
372
- ok: false,
373
- meta: response.meta,
374
- error: response.error,
375
- partial_data: response.partial_data,
376
- raw,
377
- } as ModuleResultV22;
378
- }
379
- }
380
-
381
- // v2.1 format
382
- if (response.ok) {
383
- const data = (response.data ?? {}) as ModuleResultData & { confidence?: number };
384
- return {
385
- ok: true,
386
- data: {
387
- ...data,
388
- confidence: typeof data.confidence === 'number' ? data.confidence : 0.5,
389
- rationale: typeof data.rationale === 'string' ? data.rationale : '',
390
- behavior_equivalence: data.behavior_equivalence,
391
- },
392
- raw,
393
- } as ModuleResultV21;
394
- } else {
395
- return {
396
- ok: false,
397
- error: response.error,
398
- partial_data: response.partial_data,
399
- raw,
400
- } as ModuleResultV21;
401
- }
402
- }
403
-
404
- /**
405
- * Parse legacy (non-envelope) format response
406
- */
407
- function parseLegacyResponse(output: unknown, raw: string): ModuleResult {
408
- const outputObj = output as Record<string, unknown>;
409
- const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
410
- const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
411
- const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
412
- ? outputObj.behavior_equivalence
413
- : undefined;
414
-
415
- // Check if this is an error response (has error.code)
416
- if (outputObj.error && typeof outputObj.error === 'object') {
417
- const errorObj = outputObj.error as Record<string, unknown>;
418
- if (typeof errorObj.code === 'string') {
419
- return {
420
- ok: false,
421
- error: {
422
- code: errorObj.code,
423
- message: typeof errorObj.message === 'string' ? errorObj.message : 'Unknown error',
424
- },
425
- raw,
426
- };
427
- }
428
- }
429
-
430
- // Return as v2.1 format (data includes confidence)
431
- return {
432
- ok: true,
433
- data: {
434
- ...outputObj,
435
- confidence,
436
- rationale,
437
- behavior_equivalence: behaviorEquivalence,
438
- },
439
- raw,
440
- } as ModuleResultV21;
441
- }
442
-
443
- /**
444
- * Build prompt with clean variable substitution
445
- */
446
- function buildPrompt(module: CognitiveModule, input: ModuleInput): string {
447
- let prompt = module.prompt;
448
-
449
- // v2 style: substitute ${variable} placeholders
450
- for (const [key, value] of Object.entries(input)) {
451
- const strValue = typeof value === 'string' ? value : JSON.stringify(value);
452
- prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
453
- }
454
-
455
- // v1 compatibility: substitute $ARGUMENTS
456
- const argsValue = input.code || input.query || '';
457
- prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
458
-
459
- // Substitute $N placeholders (v1 compatibility)
460
- if (typeof argsValue === 'string') {
461
- const argsList = argsValue.split(/\s+/);
462
- argsList.forEach((arg, i) => {
463
- prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
464
- });
465
- }
466
-
467
- // Append input summary if not already in prompt
468
- if (!prompt.includes(argsValue) && argsValue) {
469
- prompt += '\n\n## Input\n\n';
470
- if (input.code) {
471
- prompt += '```\n' + input.code + '\n```\n';
472
- }
473
- if (input.query) {
474
- prompt += input.query + '\n';
475
- }
476
- if (input.language) {
477
- prompt += `\nLanguage: ${input.language}\n`;
478
- }
479
- }
480
-
481
- return prompt;
482
- }
483
-
484
- /**
485
- * Heuristic to detect if input looks like code
486
- */
487
- function looksLikeCode(str: string): boolean {
488
- const codeIndicators = [
489
- /^(def|function|class|const|let|var|import|export|public|private)\s/,
490
- /[{};()]/,
491
- /=>/,
492
- /\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
493
- ];
494
- return codeIndicators.some(re => re.test(str));
495
- }