cognitive-modules-cli 2.2.1 → 2.2.7

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 (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +35 -29
  4. package/dist/cli.js +519 -23
  5. package/dist/commands/add.d.ts +33 -14
  6. package/dist/commands/add.js +383 -16
  7. package/dist/commands/compose.js +60 -23
  8. package/dist/commands/index.d.ts +4 -0
  9. package/dist/commands/index.js +4 -0
  10. package/dist/commands/init.js +23 -1
  11. package/dist/commands/migrate.d.ts +30 -0
  12. package/dist/commands/migrate.js +650 -0
  13. package/dist/commands/pipe.d.ts +1 -0
  14. package/dist/commands/pipe.js +31 -11
  15. package/dist/commands/remove.js +33 -2
  16. package/dist/commands/run.d.ts +2 -0
  17. package/dist/commands/run.js +61 -28
  18. package/dist/commands/search.d.ts +28 -0
  19. package/dist/commands/search.js +143 -0
  20. package/dist/commands/test.d.ts +65 -0
  21. package/dist/commands/test.js +454 -0
  22. package/dist/commands/update.d.ts +1 -0
  23. package/dist/commands/update.js +106 -14
  24. package/dist/commands/validate.d.ts +36 -0
  25. package/dist/commands/validate.js +97 -0
  26. package/dist/errors/index.d.ts +225 -0
  27. package/dist/errors/index.js +420 -0
  28. package/dist/mcp/server.js +84 -79
  29. package/dist/modules/composition.js +97 -32
  30. package/dist/modules/loader.js +4 -2
  31. package/dist/modules/runner.d.ts +72 -5
  32. package/dist/modules/runner.js +306 -59
  33. package/dist/modules/subagent.d.ts +6 -1
  34. package/dist/modules/subagent.js +18 -13
  35. package/dist/modules/validator.js +14 -6
  36. package/dist/providers/anthropic.d.ts +15 -0
  37. package/dist/providers/anthropic.js +147 -5
  38. package/dist/providers/base.d.ts +11 -0
  39. package/dist/providers/base.js +18 -0
  40. package/dist/providers/gemini.d.ts +15 -0
  41. package/dist/providers/gemini.js +122 -5
  42. package/dist/providers/ollama.d.ts +15 -0
  43. package/dist/providers/ollama.js +111 -3
  44. package/dist/providers/openai.d.ts +11 -0
  45. package/dist/providers/openai.js +133 -0
  46. package/dist/registry/client.d.ts +212 -0
  47. package/dist/registry/client.js +359 -0
  48. package/dist/registry/index.d.ts +4 -0
  49. package/dist/registry/index.js +4 -0
  50. package/dist/registry/tar.d.ts +8 -0
  51. package/dist/registry/tar.js +353 -0
  52. package/dist/server/http.js +301 -45
  53. package/dist/server/index.d.ts +2 -0
  54. package/dist/server/index.js +1 -0
  55. package/dist/server/sse.d.ts +13 -0
  56. package/dist/server/sse.js +22 -0
  57. package/dist/types.d.ts +32 -1
  58. package/dist/types.js +4 -1
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +4 -0
  61. package/package.json +31 -7
  62. package/dist/modules/composition.test.d.ts +0 -11
  63. package/dist/modules/composition.test.js +0 -450
  64. package/dist/modules/policy.test.d.ts +0 -10
  65. package/dist/modules/policy.test.js +0 -369
  66. package/src/cli.ts +0 -471
  67. package/src/commands/add.ts +0 -315
  68. package/src/commands/compose.ts +0 -185
  69. package/src/commands/index.ts +0 -13
  70. package/src/commands/init.ts +0 -94
  71. package/src/commands/list.ts +0 -33
  72. package/src/commands/pipe.ts +0 -76
  73. package/src/commands/remove.ts +0 -57
  74. package/src/commands/run.ts +0 -80
  75. package/src/commands/update.ts +0 -130
  76. package/src/commands/versions.ts +0 -79
  77. package/src/index.ts +0 -90
  78. package/src/mcp/index.ts +0 -5
  79. package/src/mcp/server.ts +0 -403
  80. package/src/modules/composition.test.ts +0 -558
  81. package/src/modules/composition.ts +0 -1674
  82. package/src/modules/index.ts +0 -9
  83. package/src/modules/loader.ts +0 -508
  84. package/src/modules/policy.test.ts +0 -455
  85. package/src/modules/runner.ts +0 -1983
  86. package/src/modules/subagent.ts +0 -277
  87. package/src/modules/validator.ts +0 -700
  88. package/src/providers/anthropic.ts +0 -89
  89. package/src/providers/base.ts +0 -29
  90. package/src/providers/deepseek.ts +0 -83
  91. package/src/providers/gemini.ts +0 -117
  92. package/src/providers/index.ts +0 -78
  93. package/src/providers/minimax.ts +0 -81
  94. package/src/providers/moonshot.ts +0 -82
  95. package/src/providers/ollama.ts +0 -83
  96. package/src/providers/openai.ts +0 -84
  97. package/src/providers/qwen.ts +0 -82
  98. package/src/server/http.ts +0 -316
  99. package/src/server/index.ts +0 -6
  100. package/src/types.ts +0 -599
  101. package/tsconfig.json +0 -17
@@ -0,0 +1,650 @@
1
+ /**
2
+ * cog migrate - Migrate modules to v2.2 format
3
+ *
4
+ * Aligns with Python CLI's `cog migrate` command.
5
+ * Performs migration from v0/v1/v2.1 to v2.2 format.
6
+ */
7
+ import * as fs from 'node:fs/promises';
8
+ import * as path from 'node:path';
9
+ import yaml from 'js-yaml';
10
+ import { findModule, getDefaultSearchPaths } from '../modules/index.js';
11
+ /**
12
+ * Migrate a single module to v2.2 format.
13
+ */
14
+ export async function migrate(nameOrPath, ctx, options = {}) {
15
+ const dryRun = options.dryRun ?? false;
16
+ const backup = options.backup ?? true;
17
+ try {
18
+ let modulePath;
19
+ let moduleName;
20
+ // Try to find as a named module first
21
+ const searchPaths = getDefaultSearchPaths(ctx.cwd);
22
+ const module = await findModule(nameOrPath, searchPaths);
23
+ if (module) {
24
+ modulePath = module.location;
25
+ moduleName = module.name;
26
+ }
27
+ else {
28
+ // Treat as a path
29
+ modulePath = nameOrPath;
30
+ moduleName = path.basename(nameOrPath);
31
+ }
32
+ // Check if path exists
33
+ try {
34
+ await fs.access(modulePath);
35
+ }
36
+ catch {
37
+ return {
38
+ success: false,
39
+ error: `Module not found: ${modulePath}`,
40
+ };
41
+ }
42
+ // Detect format and migrate
43
+ const result = await migrateModule(modulePath, moduleName, dryRun, backup);
44
+ return {
45
+ success: result.success,
46
+ data: result,
47
+ error: result.success ? undefined : result.warnings.join('; '),
48
+ };
49
+ }
50
+ catch (e) {
51
+ return {
52
+ success: false,
53
+ error: e instanceof Error ? e.message : String(e),
54
+ };
55
+ }
56
+ }
57
+ /**
58
+ * Migrate all modules to v2.2 format.
59
+ */
60
+ export async function migrateAll(ctx, options = {}) {
61
+ const dryRun = options.dryRun ?? false;
62
+ const backup = options.backup ?? true;
63
+ try {
64
+ const { listModules } = await import('../modules/loader.js');
65
+ const searchPaths = getDefaultSearchPaths(ctx.cwd);
66
+ const modules = await listModules(searchPaths);
67
+ const results = [];
68
+ let allSuccess = true;
69
+ for (const module of modules) {
70
+ const result = await migrateModule(module.location, module.name, dryRun, backup);
71
+ results.push(result);
72
+ if (!result.success) {
73
+ allSuccess = false;
74
+ }
75
+ }
76
+ return {
77
+ success: allSuccess,
78
+ data: {
79
+ total: modules.length,
80
+ migrated: results.filter(r => r.success && r.changes.length > 0).length,
81
+ skipped: results.filter(r => r.success && r.changes.length === 0).length,
82
+ failed: results.filter(r => !r.success).length,
83
+ results,
84
+ },
85
+ };
86
+ }
87
+ catch (e) {
88
+ return {
89
+ success: false,
90
+ error: e instanceof Error ? e.message : String(e),
91
+ };
92
+ }
93
+ }
94
+ // =============================================================================
95
+ // Internal Migration Logic
96
+ // =============================================================================
97
+ async function migrateModule(modulePath, moduleName, dryRun, backup) {
98
+ const changes = [];
99
+ const warnings = [];
100
+ // Detect format
101
+ const format = await detectFormat(modulePath);
102
+ if (!format) {
103
+ return {
104
+ moduleName,
105
+ modulePath,
106
+ success: false,
107
+ changes: [],
108
+ warnings: ['Could not detect module format'],
109
+ };
110
+ }
111
+ // Check if already v2.2
112
+ if (format === 'v2') {
113
+ const moduleYamlPath = path.join(modulePath, 'module.yaml');
114
+ try {
115
+ const content = await fs.readFile(moduleYamlPath, 'utf-8');
116
+ const manifest = yaml.load(content);
117
+ if (manifest.tier !== undefined) {
118
+ warnings.push('Module appears to already be v2.2 format');
119
+ return { moduleName, modulePath, success: true, changes: [], warnings };
120
+ }
121
+ }
122
+ catch {
123
+ // Continue with migration
124
+ }
125
+ }
126
+ // Create backup if needed
127
+ if (backup && !dryRun) {
128
+ const backupPath = await createBackup(modulePath);
129
+ changes.push(`Created backup: ${backupPath}`);
130
+ }
131
+ // Perform migration based on format
132
+ switch (format) {
133
+ case 'v0':
134
+ return migrateFromV0(modulePath, moduleName, dryRun, changes, warnings);
135
+ case 'v1':
136
+ return migrateFromV1(modulePath, moduleName, dryRun, changes, warnings);
137
+ case 'v2':
138
+ return migrateFromV2(modulePath, moduleName, dryRun, changes, warnings);
139
+ default:
140
+ return {
141
+ moduleName,
142
+ modulePath,
143
+ success: false,
144
+ changes: [],
145
+ warnings: [`Unknown format: ${format}`],
146
+ };
147
+ }
148
+ }
149
+ async function detectFormat(modulePath) {
150
+ const hasModuleYaml = await fileExists(path.join(modulePath, 'module.yaml'));
151
+ const hasModuleMd = await fileExists(path.join(modulePath, 'MODULE.md'));
152
+ const hasOldModuleMd = await fileExists(path.join(modulePath, 'module.md'));
153
+ if (hasModuleYaml)
154
+ return 'v2';
155
+ if (hasModuleMd)
156
+ return 'v1';
157
+ if (hasOldModuleMd)
158
+ return 'v0';
159
+ return null;
160
+ }
161
+ async function fileExists(filepath) {
162
+ try {
163
+ await fs.access(filepath);
164
+ return true;
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
170
+ async function createBackup(modulePath) {
171
+ const timestamp = new Date().toISOString().replace(/[:-]/g, '').slice(0, 15);
172
+ const backupPath = `${modulePath}_backup_${timestamp}`;
173
+ await fs.cp(modulePath, backupPath, { recursive: true });
174
+ return backupPath;
175
+ }
176
+ // =============================================================================
177
+ // v0 Migration (6-file format)
178
+ // =============================================================================
179
+ async function migrateFromV0(modulePath, moduleName, dryRun, changes, warnings) {
180
+ warnings.push('v0 format migration requires manual review');
181
+ try {
182
+ // Load module.md
183
+ const moduleMdPath = path.join(modulePath, 'module.md');
184
+ const moduleMdContent = await fs.readFile(moduleMdPath, 'utf-8');
185
+ const frontmatter = parseFrontmatter(moduleMdContent);
186
+ // Load prompt
187
+ const promptPath = path.join(modulePath, 'prompt.txt');
188
+ const prompt = await fs.readFile(promptPath, 'utf-8');
189
+ // Load schemas
190
+ const inputSchemaPath = path.join(modulePath, 'input.schema.json');
191
+ const outputSchemaPath = path.join(modulePath, 'output.schema.json');
192
+ const inputSchema = JSON.parse(await fs.readFile(inputSchemaPath, 'utf-8'));
193
+ const outputSchema = JSON.parse(await fs.readFile(outputSchemaPath, 'utf-8'));
194
+ // Create v2.2 manifest
195
+ const manifest = createV22Manifest(frontmatter);
196
+ // Create combined schema
197
+ const schema = createV22Schema(inputSchema, outputSchema);
198
+ // Create prompt.md
199
+ const promptMd = createV22Prompt(frontmatter, prompt);
200
+ if (dryRun) {
201
+ changes.push('[DRY RUN] Would create module.yaml');
202
+ changes.push('[DRY RUN] Would create schema.json (combined)');
203
+ changes.push('[DRY RUN] Would create prompt.md');
204
+ }
205
+ else {
206
+ await writeYaml(path.join(modulePath, 'module.yaml'), manifest);
207
+ changes.push('Created module.yaml');
208
+ await writeJson(path.join(modulePath, 'schema.json'), schema);
209
+ changes.push('Created schema.json (combined)');
210
+ await fs.writeFile(path.join(modulePath, 'prompt.md'), promptMd);
211
+ changes.push('Created prompt.md');
212
+ }
213
+ return { moduleName, modulePath, success: true, changes, warnings };
214
+ }
215
+ catch (e) {
216
+ warnings.push(`Migration error: ${e instanceof Error ? e.message : e}`);
217
+ return { moduleName, modulePath, success: false, changes, warnings };
218
+ }
219
+ }
220
+ // =============================================================================
221
+ // v1 Migration (MODULE.md + schema.json)
222
+ // =============================================================================
223
+ async function migrateFromV1(modulePath, moduleName, dryRun, changes, warnings) {
224
+ try {
225
+ // Load MODULE.md
226
+ const moduleMdPath = path.join(modulePath, 'MODULE.md');
227
+ const moduleMdContent = await fs.readFile(moduleMdPath, 'utf-8');
228
+ const { frontmatter, body } = parseFrontmatterWithBody(moduleMdContent);
229
+ // Load schema.json if exists
230
+ const schemaPath = path.join(modulePath, 'schema.json');
231
+ let inputSchema = {};
232
+ let outputSchema = {};
233
+ if (await fileExists(schemaPath)) {
234
+ const schema = JSON.parse(await fs.readFile(schemaPath, 'utf-8'));
235
+ inputSchema = schema.input || {};
236
+ outputSchema = schema.output || {};
237
+ }
238
+ // Create v2.2 manifest
239
+ const manifest = createV22Manifest(frontmatter);
240
+ // Create/update schema with meta
241
+ const newSchema = createV22Schema(inputSchema, outputSchema);
242
+ // Create prompt.md
243
+ const promptMd = createV22Prompt(frontmatter, body);
244
+ if (dryRun) {
245
+ changes.push('[DRY RUN] Would create module.yaml');
246
+ changes.push('[DRY RUN] Would update schema.json (add meta)');
247
+ changes.push('[DRY RUN] Would create prompt.md');
248
+ }
249
+ else {
250
+ await writeYaml(path.join(modulePath, 'module.yaml'), manifest);
251
+ changes.push('Created module.yaml');
252
+ await writeJson(path.join(modulePath, 'schema.json'), newSchema);
253
+ changes.push('Updated schema.json (added meta)');
254
+ await fs.writeFile(path.join(modulePath, 'prompt.md'), promptMd);
255
+ changes.push('Created prompt.md');
256
+ changes.push('Preserved MODULE.md (backward compatibility)');
257
+ }
258
+ return { moduleName, modulePath, success: true, changes, warnings };
259
+ }
260
+ catch (e) {
261
+ warnings.push(`Migration error: ${e instanceof Error ? e.message : e}`);
262
+ return { moduleName, modulePath, success: false, changes, warnings };
263
+ }
264
+ }
265
+ // =============================================================================
266
+ // v2.0/v2.1 Migration
267
+ // =============================================================================
268
+ async function migrateFromV2(modulePath, moduleName, dryRun, changes, warnings) {
269
+ try {
270
+ // Load module.yaml
271
+ const moduleYamlPath = path.join(modulePath, 'module.yaml');
272
+ const manifest = yaml.load(await fs.readFile(moduleYamlPath, 'utf-8'));
273
+ // Load schema.json
274
+ const schemaPath = path.join(modulePath, 'schema.json');
275
+ let schema = {};
276
+ if (await fileExists(schemaPath)) {
277
+ schema = JSON.parse(await fs.readFile(schemaPath, 'utf-8'));
278
+ }
279
+ // Load prompt.md
280
+ const promptPath = path.join(modulePath, 'prompt.md');
281
+ let prompt = '';
282
+ if (await fileExists(promptPath)) {
283
+ prompt = await fs.readFile(promptPath, 'utf-8');
284
+ }
285
+ // Track changes
286
+ const manifestChanges = [];
287
+ const schemaChanges = [];
288
+ const promptChanges = [];
289
+ // Upgrade manifest to v2.2
290
+ if (!('tier' in manifest)) {
291
+ manifest.tier = 'decision';
292
+ manifestChanges.push('Added tier: decision');
293
+ }
294
+ if (!('schema_strictness' in manifest)) {
295
+ manifest.schema_strictness = 'medium';
296
+ manifestChanges.push('Added schema_strictness: medium');
297
+ }
298
+ if (!('overflow' in manifest)) {
299
+ const schemaStrictness = manifest.schema_strictness || 'medium';
300
+ const strictnessMaxItems = { high: 0, medium: 5, low: 20 };
301
+ const defaultMaxItems = strictnessMaxItems[schemaStrictness] ?? 5;
302
+ const defaultEnabled = schemaStrictness !== 'high';
303
+ manifest.overflow = {
304
+ enabled: defaultEnabled,
305
+ recoverable: true,
306
+ max_items: defaultMaxItems,
307
+ require_suggested_mapping: true,
308
+ };
309
+ manifestChanges.push(`Added overflow config (max_items=${defaultMaxItems})`);
310
+ }
311
+ if (!('enums' in manifest)) {
312
+ manifest.enums = { strategy: 'extensible' };
313
+ manifestChanges.push('Added enums config');
314
+ }
315
+ if (!('compat' in manifest)) {
316
+ manifest.compat = {
317
+ accepts_v21_payload: true,
318
+ runtime_auto_wrap: true,
319
+ schema_output_alias: 'data',
320
+ };
321
+ manifestChanges.push('Added compat config');
322
+ }
323
+ if (!('io' in manifest)) {
324
+ manifest.io = {
325
+ input: './schema.json#/input',
326
+ data: './schema.json#/data',
327
+ meta: './schema.json#/meta',
328
+ error: './schema.json#/error',
329
+ };
330
+ manifestChanges.push('Added io references');
331
+ }
332
+ // Upgrade schema to v2.2
333
+ if (!('meta' in schema)) {
334
+ schema.meta = createMetaSchema();
335
+ schemaChanges.push('Added meta schema');
336
+ }
337
+ if ('output' in schema && !('data' in schema)) {
338
+ schema.data = schema.output;
339
+ delete schema.output;
340
+ schemaChanges.push('Renamed output to data');
341
+ }
342
+ if ('data' in schema) {
343
+ const dataSchema = schema.data;
344
+ const dataRequired = dataSchema.required || [];
345
+ if (!dataRequired.includes('rationale')) {
346
+ dataRequired.push('rationale');
347
+ dataSchema.required = dataRequired;
348
+ schemaChanges.push('Added rationale to data.required');
349
+ }
350
+ }
351
+ const overflowConfig = manifest.overflow;
352
+ if (overflowConfig?.enabled && !('$defs' in schema)) {
353
+ schema.$defs = { extensions: createExtensionsSchema() };
354
+ schemaChanges.push('Added $defs.extensions');
355
+ }
356
+ // Update prompt if needed
357
+ if (!prompt.toLowerCase().includes('meta') || !prompt.toLowerCase().includes('envelope')) {
358
+ prompt = addV22InstructionsToPrompt(prompt, manifest);
359
+ promptChanges.push('Added v2.2 envelope instructions');
360
+ }
361
+ // Apply changes
362
+ if (dryRun) {
363
+ if (manifestChanges.length > 0) {
364
+ changes.push(`[DRY RUN] Would update module.yaml: ${manifestChanges.join(', ')}`);
365
+ }
366
+ if (schemaChanges.length > 0) {
367
+ changes.push(`[DRY RUN] Would update schema.json: ${schemaChanges.join(', ')}`);
368
+ }
369
+ if (promptChanges.length > 0) {
370
+ changes.push(`[DRY RUN] Would update prompt.md: ${promptChanges.join(', ')}`);
371
+ }
372
+ }
373
+ else {
374
+ if (manifestChanges.length > 0) {
375
+ await writeYaml(moduleYamlPath, manifest);
376
+ changes.push(`Updated module.yaml: ${manifestChanges.join(', ')}`);
377
+ }
378
+ if (schemaChanges.length > 0) {
379
+ await writeJson(schemaPath, schema);
380
+ changes.push(`Updated schema.json: ${schemaChanges.join(', ')}`);
381
+ }
382
+ if (promptChanges.length > 0) {
383
+ await fs.writeFile(promptPath, prompt);
384
+ changes.push(`Updated prompt.md: ${promptChanges.join(', ')}`);
385
+ }
386
+ }
387
+ return { moduleName, modulePath, success: true, changes, warnings };
388
+ }
389
+ catch (e) {
390
+ warnings.push(`Migration error: ${e instanceof Error ? e.message : e}`);
391
+ return { moduleName, modulePath, success: false, changes, warnings };
392
+ }
393
+ }
394
+ // =============================================================================
395
+ // Helper Functions
396
+ // =============================================================================
397
+ function parseFrontmatter(content) {
398
+ if (!content.startsWith('---')) {
399
+ return {};
400
+ }
401
+ const parts = content.split('---');
402
+ if (parts.length < 3) {
403
+ return {};
404
+ }
405
+ try {
406
+ return yaml.load(parts[1]);
407
+ }
408
+ catch {
409
+ return {};
410
+ }
411
+ }
412
+ function parseFrontmatterWithBody(content) {
413
+ if (!content.startsWith('---')) {
414
+ return { frontmatter: {}, body: content };
415
+ }
416
+ const parts = content.split('---');
417
+ if (parts.length < 3) {
418
+ return { frontmatter: {}, body: content };
419
+ }
420
+ try {
421
+ return {
422
+ frontmatter: yaml.load(parts[1]),
423
+ body: parts.slice(2).join('---').trim(),
424
+ };
425
+ }
426
+ catch {
427
+ return { frontmatter: {}, body: content };
428
+ }
429
+ }
430
+ function createV22Manifest(frontmatter) {
431
+ return {
432
+ name: frontmatter.name || 'unknown',
433
+ version: frontmatter.version || '2.2.0',
434
+ responsibility: frontmatter.responsibility || '',
435
+ tier: 'decision',
436
+ schema_strictness: 'medium',
437
+ excludes: frontmatter.excludes || [],
438
+ policies: {
439
+ network: 'deny',
440
+ filesystem_write: 'deny',
441
+ side_effects: 'deny',
442
+ code_execution: 'deny',
443
+ },
444
+ tools: {
445
+ policy: 'deny_by_default',
446
+ allowed: [],
447
+ denied: ['write_file', 'shell', 'network'],
448
+ },
449
+ overflow: {
450
+ enabled: true,
451
+ recoverable: true,
452
+ max_items: 5,
453
+ require_suggested_mapping: true,
454
+ },
455
+ enums: {
456
+ strategy: 'extensible',
457
+ },
458
+ failure: {
459
+ contract: 'error_union',
460
+ partial_allowed: true,
461
+ must_return_error_schema: true,
462
+ },
463
+ runtime_requirements: {
464
+ structured_output: true,
465
+ max_input_tokens: 8000,
466
+ preferred_capabilities: ['json_mode'],
467
+ },
468
+ io: {
469
+ input: './schema.json#/input',
470
+ data: './schema.json#/data',
471
+ meta: './schema.json#/meta',
472
+ error: './schema.json#/error',
473
+ },
474
+ compat: {
475
+ accepts_v21_payload: true,
476
+ runtime_auto_wrap: true,
477
+ schema_output_alias: 'data',
478
+ },
479
+ ...(frontmatter.constraints ? { constraints: frontmatter.constraints } : {}),
480
+ ...(frontmatter.context ? { context: frontmatter.context } : {}),
481
+ };
482
+ }
483
+ function createV22Schema(inputSchema, outputSchema) {
484
+ return {
485
+ $schema: 'https://ziel-io.github.io/cognitive-modules/schema/v2.2.json',
486
+ meta: createMetaSchema(),
487
+ input: inputSchema,
488
+ data: addRationaleToOutput(outputSchema),
489
+ error: {
490
+ type: 'object',
491
+ required: ['code', 'message'],
492
+ properties: {
493
+ code: { type: 'string' },
494
+ message: { type: 'string' },
495
+ },
496
+ },
497
+ $defs: {
498
+ extensions: createExtensionsSchema(),
499
+ },
500
+ };
501
+ }
502
+ function createMetaSchema() {
503
+ return {
504
+ type: 'object',
505
+ required: ['confidence', 'risk', 'explain'],
506
+ properties: {
507
+ confidence: {
508
+ type: 'number',
509
+ minimum: 0,
510
+ maximum: 1,
511
+ description: 'Confidence score, unified across all modules',
512
+ },
513
+ risk: {
514
+ type: 'string',
515
+ enum: ['none', 'low', 'medium', 'high'],
516
+ description: 'Aggregated risk level',
517
+ },
518
+ explain: {
519
+ type: 'string',
520
+ maxLength: 280,
521
+ description: 'Short explanation for control plane',
522
+ },
523
+ trace_id: { type: 'string' },
524
+ model: { type: 'string' },
525
+ latency_ms: { type: 'number', minimum: 0 },
526
+ },
527
+ };
528
+ }
529
+ function createExtensionsSchema() {
530
+ return {
531
+ type: 'object',
532
+ properties: {
533
+ insights: {
534
+ type: 'array',
535
+ maxItems: 5,
536
+ items: {
537
+ type: 'object',
538
+ required: ['text', 'suggested_mapping'],
539
+ properties: {
540
+ text: { type: 'string' },
541
+ suggested_mapping: { type: 'string' },
542
+ evidence: { type: 'string' },
543
+ },
544
+ },
545
+ },
546
+ },
547
+ };
548
+ }
549
+ function addRationaleToOutput(outputSchema) {
550
+ const schema = { ...outputSchema };
551
+ // Ensure required includes rationale
552
+ const required = schema.required || [];
553
+ if (!required.includes('rationale')) {
554
+ required.push('rationale');
555
+ }
556
+ schema.required = required;
557
+ // Ensure properties includes rationale
558
+ const properties = schema.properties || {};
559
+ if (!properties.rationale) {
560
+ properties.rationale = {
561
+ type: 'string',
562
+ description: 'Detailed explanation for audit and human review',
563
+ };
564
+ }
565
+ // Add extensions reference
566
+ if (!properties.extensions) {
567
+ properties.extensions = { $ref: '#/$defs/extensions' };
568
+ }
569
+ schema.properties = properties;
570
+ return schema;
571
+ }
572
+ function createV22Prompt(frontmatter, promptBody) {
573
+ const name = frontmatter.name || 'Module';
574
+ const title = name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
575
+ return `# ${title}
576
+
577
+ ${promptBody}
578
+
579
+ ## Response Format (Envelope v2.2)
580
+
581
+ You MUST wrap your response in the v2.2 envelope format with separate meta and data sections.
582
+
583
+ ### Success Response
584
+
585
+ \`\`\`json
586
+ {
587
+ "ok": true,
588
+ "meta": {
589
+ "confidence": 0.9,
590
+ "risk": "low",
591
+ "explain": "Short summary (max 280 chars) for routing and UI display."
592
+ },
593
+ "data": {
594
+ "...your output fields...",
595
+ "rationale": "Detailed explanation for audit and human review..."
596
+ }
597
+ }
598
+ \`\`\`
599
+
600
+ ### Error Response
601
+
602
+ \`\`\`json
603
+ {
604
+ "ok": false,
605
+ "meta": {
606
+ "confidence": 0.0,
607
+ "risk": "high",
608
+ "explain": "Brief error summary."
609
+ },
610
+ "error": {
611
+ "code": "ERROR_CODE",
612
+ "message": "Detailed error description"
613
+ }
614
+ }
615
+ \`\`\`
616
+
617
+ ## Important
618
+
619
+ - \`meta.explain\` is for **quick decisions** (≤280 chars)
620
+ - \`data.rationale\` is for **audit and review** (no limit)
621
+ - Both must be present in successful responses
622
+ `;
623
+ }
624
+ function addV22InstructionsToPrompt(prompt, _manifest) {
625
+ const v22Section = `
626
+
627
+ ## Response Format (Envelope v2.2)
628
+
629
+ You MUST wrap your response in the v2.2 envelope format with separate meta and data sections:
630
+
631
+ - Success: \`{ "ok": true, "meta": { "confidence": 0.9, "risk": "low", "explain": "≤280 chars" }, "data": { ...payload... } }\`
632
+ - Error: \`{ "ok": false, "meta": { ... }, "error": { "code": "...", "message": "..." } }\`
633
+
634
+ Important:
635
+ - \`meta.explain\` is for quick routing (≤280 chars)
636
+ - \`data.rationale\` is for detailed audit (no limit)
637
+ `;
638
+ return prompt + v22Section;
639
+ }
640
+ async function writeYaml(filepath, data) {
641
+ const content = '# Cognitive Module Manifest v2.2\n' + yaml.dump(data, {
642
+ noRefs: true,
643
+ sortKeys: false,
644
+ lineWidth: 100,
645
+ });
646
+ await fs.writeFile(filepath, content);
647
+ }
648
+ async function writeJson(filepath, data) {
649
+ await fs.writeFile(filepath, JSON.stringify(data, null, 2) + '\n');
650
+ }
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * cog pipe - Pipe mode for stdin/stdout integration
3
+ * Always returns v2.2 envelope format for consistency
3
4
  */
4
5
  import type { CommandContext, CommandResult } from '../types.js';
5
6
  export interface PipeOptions {