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
@@ -1,16 +1,25 @@
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 * as readline from 'node:readline';
5
6
  import { findModule, getDefaultSearchPaths, runModule } from '../modules/index.js';
7
+ import { ErrorCodes, attachContext, makeErrorEnvelope } from '../errors/index.js';
6
8
  export async function pipe(ctx, options) {
7
9
  const searchPaths = getDefaultSearchPaths(ctx.cwd);
8
10
  // Find module
9
11
  const module = await findModule(options.module, searchPaths);
10
12
  if (!module) {
13
+ const errorEnvelope = attachContext(makeErrorEnvelope({
14
+ code: ErrorCodes.MODULE_NOT_FOUND,
15
+ message: `Module not found: ${options.module}`,
16
+ suggestion: "Use 'cog list' to see installed modules",
17
+ }), { module: options.module, provider: ctx.provider.name });
18
+ console.log(JSON.stringify(errorEnvelope));
11
19
  return {
12
20
  success: false,
13
- error: `Module not found: ${options.module}`,
21
+ error: errorEnvelope.error.message,
22
+ data: errorEnvelope,
14
23
  };
15
24
  }
16
25
  // Read from stdin
@@ -33,27 +42,38 @@ export async function pipe(ctx, options) {
33
42
  catch {
34
43
  // Not JSON, use as args
35
44
  }
45
+ // Run module with v2.2 envelope format
36
46
  const result = await runModule(module, ctx.provider, {
37
47
  args: inputData ? undefined : input,
38
48
  input: inputData,
49
+ validateInput: !options.noValidate,
50
+ validateOutput: !options.noValidate,
51
+ useV22: true, // Always use v2.2 envelope
39
52
  });
40
- // Output envelope format to stdout
41
- console.log(JSON.stringify(result));
53
+ const output = attachContext(result, {
54
+ module: options.module,
55
+ provider: ctx.provider.name,
56
+ });
57
+ // Output v2.2 envelope format to stdout
58
+ console.log(JSON.stringify(output));
42
59
  return {
43
60
  success: result.ok,
44
- data: result,
61
+ data: output,
45
62
  };
46
63
  }
47
64
  catch (e) {
48
- const error = e instanceof Error ? e.message : String(e);
49
- // Output error in envelope format
50
- console.log(JSON.stringify({
51
- ok: false,
52
- error: { code: 'RUNTIME_ERROR', message: error }
53
- }));
65
+ const message = e instanceof Error ? e.message : String(e);
66
+ // Output v2.2 compliant error envelope
67
+ const errorEnvelope = attachContext(makeErrorEnvelope({
68
+ code: ErrorCodes.INTERNAL_ERROR,
69
+ message,
70
+ recoverable: false,
71
+ }), { module: options.module, provider: ctx.provider.name });
72
+ console.log(JSON.stringify(errorEnvelope));
54
73
  return {
55
74
  success: false,
56
- error,
75
+ error: message,
76
+ data: errorEnvelope,
57
77
  };
58
78
  }
59
79
  }
@@ -5,15 +5,46 @@
5
5
  */
6
6
  import { existsSync, rmSync } from 'node:fs';
7
7
  import { readFile, writeFile } from 'node:fs/promises';
8
- import { join } from 'node:path';
8
+ import { join, resolve, sep, isAbsolute } from 'node:path';
9
9
  import { homedir } from 'node:os';
10
10
  const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
11
11
  const INSTALLED_MANIFEST = join(homedir(), '.cognitive', 'installed.json');
12
+ function assertSafeModuleName(name) {
13
+ const trimmed = name.trim();
14
+ if (!trimmed) {
15
+ throw new Error('Invalid module name: empty');
16
+ }
17
+ if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
18
+ throw new Error(`Invalid module name: ${name}`);
19
+ }
20
+ if (isAbsolute(trimmed)) {
21
+ throw new Error(`Invalid module name (absolute path not allowed): ${name}`);
22
+ }
23
+ return trimmed;
24
+ }
25
+ function resolveModuleTarget(moduleName) {
26
+ const safeName = assertSafeModuleName(moduleName);
27
+ const targetPath = resolve(USER_MODULES_DIR, safeName);
28
+ const root = resolve(USER_MODULES_DIR) + sep;
29
+ if (!targetPath.startsWith(root)) {
30
+ throw new Error(`Invalid module name (path traversal): ${moduleName}`);
31
+ }
32
+ return targetPath;
33
+ }
12
34
  /**
13
35
  * Remove an installed module
14
36
  */
15
37
  export async function remove(moduleName, ctx) {
16
- const modulePath = join(USER_MODULES_DIR, moduleName);
38
+ let modulePath;
39
+ try {
40
+ modulePath = resolveModuleTarget(moduleName);
41
+ }
42
+ catch (e) {
43
+ return {
44
+ success: false,
45
+ error: e instanceof Error ? e.message : String(e),
46
+ };
47
+ }
17
48
  if (!existsSync(modulePath)) {
18
49
  return {
19
50
  success: false,
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * cog run - Run a Cognitive Module
3
+ * Always returns v2.2 envelope format for consistency
3
4
  */
4
5
  import type { CommandContext, CommandResult } from '../types.js';
5
6
  export interface RunOptions {
@@ -8,5 +9,6 @@ export interface RunOptions {
8
9
  noValidate?: boolean;
9
10
  pretty?: boolean;
10
11
  verbose?: boolean;
12
+ stream?: boolean;
11
13
  }
12
14
  export declare function run(moduleName: string, ctx: CommandContext, options?: RunOptions): Promise<CommandResult>;
@@ -1,15 +1,23 @@
1
1
  /**
2
2
  * cog run - Run a Cognitive Module
3
+ * Always returns v2.2 envelope format for consistency
3
4
  */
4
- import { findModule, getDefaultSearchPaths, runModule } from '../modules/index.js';
5
+ import { findModule, getDefaultSearchPaths, runModule, runModuleStream } from '../modules/index.js';
6
+ import { ErrorCodes, attachContext, makeErrorEnvelope } from '../errors/index.js';
5
7
  export async function run(moduleName, ctx, options = {}) {
6
8
  const searchPaths = getDefaultSearchPaths(ctx.cwd);
7
9
  // Find module
8
10
  const module = await findModule(moduleName, searchPaths);
9
11
  if (!module) {
12
+ const errorEnvelope = attachContext(makeErrorEnvelope({
13
+ code: ErrorCodes.MODULE_NOT_FOUND,
14
+ message: `Module not found: ${moduleName}\nSearch paths: ${searchPaths.join(', ')}`,
15
+ suggestion: "Use 'cog list' to see installed modules or 'cog search' to find modules in registry",
16
+ }), { module: moduleName, provider: ctx.provider.name });
10
17
  return {
11
18
  success: false,
12
- error: `Module not found: ${moduleName}\nSearch paths: ${searchPaths.join(', ')}`,
19
+ error: errorEnvelope.error.message,
20
+ data: errorEnvelope,
13
21
  };
14
22
  }
15
23
  try {
@@ -20,46 +28,71 @@ export async function run(moduleName, ctx, options = {}) {
20
28
  inputData = JSON.parse(options.input);
21
29
  }
22
30
  catch {
31
+ const errorEnvelope = attachContext(makeErrorEnvelope({
32
+ code: ErrorCodes.INVALID_INPUT,
33
+ message: `Invalid JSON input: ${options.input}`,
34
+ suggestion: 'Ensure input is valid JSON format',
35
+ }), { module: moduleName, provider: ctx.provider.name });
23
36
  return {
24
37
  success: false,
25
- error: `Invalid JSON input: ${options.input}`,
38
+ error: errorEnvelope.error.message,
39
+ data: errorEnvelope,
26
40
  };
27
41
  }
28
42
  }
29
- // Run module
30
- const result = await runModule(module, ctx.provider, {
31
- args: options.args,
32
- input: inputData,
33
- verbose: options.verbose || ctx.verbose,
34
- });
35
- // Return envelope format or extracted data
36
- if (options.pretty) {
43
+ if (options.stream) {
44
+ // Stream NDJSON events to stdout. Final exit code is determined by the end event.
45
+ let finalOk = null;
46
+ for await (const ev of runModuleStream(module, ctx.provider, {
47
+ args: options.args,
48
+ input: inputData,
49
+ validateInput: !options.noValidate,
50
+ validateOutput: !options.noValidate,
51
+ useV22: true,
52
+ })) {
53
+ // Write each event as one JSON line (NDJSON).
54
+ process.stdout.write(JSON.stringify(ev) + '\n');
55
+ if (ev.type === 'end' && ev.result) {
56
+ finalOk = Boolean(ev.result.ok);
57
+ }
58
+ }
37
59
  return {
38
- success: result.ok,
39
- data: result,
60
+ success: finalOk === true,
61
+ data: { ok: finalOk === true },
40
62
  };
41
63
  }
42
64
  else {
43
- // For non-pretty mode, return data (success) or error (failure)
44
- if (result.ok) {
45
- return {
46
- success: true,
47
- data: result.data,
48
- };
49
- }
50
- else {
51
- return {
52
- success: false,
53
- error: `${result.error?.code}: ${result.error?.message}`,
54
- data: result.partial_data,
55
- };
56
- }
65
+ // Run module with v2.2 envelope format
66
+ const result = await runModule(module, ctx.provider, {
67
+ args: options.args,
68
+ input: inputData,
69
+ verbose: options.verbose || ctx.verbose,
70
+ validateInput: !options.noValidate,
71
+ validateOutput: !options.noValidate,
72
+ useV22: true, // Always use v2.2 envelope
73
+ });
74
+ const output = attachContext(result, {
75
+ module: moduleName,
76
+ provider: ctx.provider.name,
77
+ });
78
+ // Always return full v2.2 envelope
79
+ return {
80
+ success: result.ok,
81
+ data: output,
82
+ };
57
83
  }
58
84
  }
59
85
  catch (e) {
86
+ const message = e instanceof Error ? e.message : String(e);
87
+ const errorEnvelope = attachContext(makeErrorEnvelope({
88
+ code: ErrorCodes.INTERNAL_ERROR,
89
+ message,
90
+ recoverable: false,
91
+ }), { module: moduleName, provider: ctx.provider.name });
60
92
  return {
61
93
  success: false,
62
- error: e instanceof Error ? e.message : String(e),
94
+ error: message,
95
+ data: errorEnvelope,
63
96
  };
64
97
  }
65
98
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Search command - Search for modules in the registry
3
+ *
4
+ * cog search code review
5
+ * cog search --category code-quality
6
+ */
7
+ import type { CommandContext, CommandResult } from '../types.js';
8
+ export interface SearchOptions {
9
+ category?: string;
10
+ limit?: number;
11
+ registry?: string;
12
+ }
13
+ /**
14
+ * Search for modules in the registry
15
+ */
16
+ export declare function search(query: string, ctx: CommandContext, options?: SearchOptions): Promise<CommandResult>;
17
+ /**
18
+ * List all categories
19
+ */
20
+ export declare function listCategories(ctx: CommandContext, options?: {
21
+ registry?: string;
22
+ }): Promise<CommandResult>;
23
+ /**
24
+ * Get detailed info about a module from registry
25
+ */
26
+ export declare function info(moduleName: string, ctx: CommandContext, options?: {
27
+ registry?: string;
28
+ }): Promise<CommandResult>;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Search command - Search for modules in the registry
3
+ *
4
+ * cog search code review
5
+ * cog search --category code-quality
6
+ */
7
+ import { RegistryClient } from '../registry/client.js';
8
+ /**
9
+ * Search for modules in the registry
10
+ */
11
+ export async function search(query, ctx, options = {}) {
12
+ const { category, limit = 20, registry } = options;
13
+ try {
14
+ const client = new RegistryClient(registry);
15
+ let results;
16
+ if (category) {
17
+ // Search within category
18
+ const categories = await client.getCategories();
19
+ const cat = categories[category];
20
+ if (!cat) {
21
+ return {
22
+ success: false,
23
+ error: `Category not found: ${category}\nAvailable categories: ${Object.keys(categories).join(', ')}`,
24
+ };
25
+ }
26
+ // Get modules in category
27
+ const modules = [];
28
+ for (const name of cat.modules) {
29
+ const mod = await client.getModule(name);
30
+ if (mod) {
31
+ modules.push(mod);
32
+ }
33
+ }
34
+ // If query provided, filter by query
35
+ if (query) {
36
+ results = modules
37
+ .filter(m => m.name.toLowerCase().includes(query.toLowerCase()) ||
38
+ m.description.toLowerCase().includes(query.toLowerCase()))
39
+ .map(m => ({
40
+ name: m.name,
41
+ description: m.description,
42
+ version: m.version,
43
+ score: 1,
44
+ keywords: m.keywords,
45
+ }));
46
+ }
47
+ else {
48
+ results = modules.map(m => ({
49
+ name: m.name,
50
+ description: m.description,
51
+ version: m.version,
52
+ score: 1,
53
+ keywords: m.keywords,
54
+ }));
55
+ }
56
+ }
57
+ else if (query) {
58
+ // Search by query
59
+ results = await client.search(query);
60
+ }
61
+ else {
62
+ // List all modules
63
+ const modules = await client.listModules();
64
+ results = modules.map(m => ({
65
+ name: m.name,
66
+ description: m.description,
67
+ version: m.version,
68
+ score: 1,
69
+ keywords: m.keywords,
70
+ }));
71
+ }
72
+ // Apply limit
73
+ const limited = results.slice(0, limit);
74
+ return {
75
+ success: true,
76
+ data: {
77
+ query,
78
+ category,
79
+ total: results.length,
80
+ results: limited,
81
+ },
82
+ };
83
+ }
84
+ catch (error) {
85
+ return {
86
+ success: false,
87
+ error: error instanceof Error ? error.message : String(error),
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * List all categories
93
+ */
94
+ export async function listCategories(ctx, options = {}) {
95
+ try {
96
+ const client = new RegistryClient(options.registry);
97
+ const categories = await client.getCategories();
98
+ return {
99
+ success: true,
100
+ data: {
101
+ categories: Object.entries(categories).map(([key, cat]) => ({
102
+ key,
103
+ name: cat.name,
104
+ description: cat.description,
105
+ moduleCount: cat.modules.length,
106
+ })),
107
+ },
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ success: false,
113
+ error: error instanceof Error ? error.message : String(error),
114
+ };
115
+ }
116
+ }
117
+ /**
118
+ * Get detailed info about a module from registry
119
+ */
120
+ export async function info(moduleName, ctx, options = {}) {
121
+ try {
122
+ const client = new RegistryClient(options.registry);
123
+ const module = await client.getModule(moduleName);
124
+ if (!module) {
125
+ return {
126
+ success: false,
127
+ error: `Module not found in registry: ${moduleName}`,
128
+ };
129
+ }
130
+ return {
131
+ success: true,
132
+ data: {
133
+ module,
134
+ },
135
+ };
136
+ }
137
+ catch (error) {
138
+ return {
139
+ success: false,
140
+ error: error instanceof Error ? error.message : String(error),
141
+ };
142
+ }
143
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Test Command - Run golden tests for Cognitive Modules
3
+ *
4
+ * Usage:
5
+ * cog test <module> - Run tests for a specific module
6
+ * cog test --all - Run tests for all modules
7
+ * cog test <module> --update - Update expected outputs
8
+ *
9
+ * Tests are defined in module.yaml:
10
+ * tests:
11
+ * - tests/case1.input.json -> tests/case1.expected.json
12
+ */
13
+ import type { CommandContext, CommandResult, CognitiveModule } from '../types.js';
14
+ export interface TestCase {
15
+ name: string;
16
+ inputPath: string;
17
+ expectedPath: string;
18
+ input?: unknown;
19
+ expected?: unknown;
20
+ }
21
+ export interface TestResult {
22
+ name: string;
23
+ passed: boolean;
24
+ duration_ms: number;
25
+ error?: string;
26
+ diff?: {
27
+ field: string;
28
+ expected: unknown;
29
+ actual: unknown;
30
+ }[];
31
+ }
32
+ export interface ModuleTestResult {
33
+ moduleName: string;
34
+ modulePath: string;
35
+ total: number;
36
+ passed: number;
37
+ failed: number;
38
+ skipped: number;
39
+ duration_ms: number;
40
+ results: TestResult[];
41
+ }
42
+ export interface TestOptions {
43
+ verbose?: boolean;
44
+ update?: boolean;
45
+ filter?: string;
46
+ timeout?: number;
47
+ }
48
+ /**
49
+ * Parse test definitions from module.yaml tests field
50
+ *
51
+ * Format: "tests/case1.input.json -> tests/case1.expected.json"
52
+ */
53
+ export declare function parseTestDefinitions(module: CognitiveModule, modulePath: string): TestCase[];
54
+ /**
55
+ * Auto-discover test cases in tests/ directory
56
+ */
57
+ export declare function discoverTestCases(modulePath: string): TestCase[];
58
+ /**
59
+ * Run tests for a single module
60
+ */
61
+ export declare function test(moduleName: string, ctx: CommandContext, options?: TestOptions): Promise<CommandResult>;
62
+ /**
63
+ * Run tests for all modules
64
+ */
65
+ export declare function testAll(ctx: CommandContext, options?: TestOptions): Promise<CommandResult>;