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,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 {
@@ -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
5
  import { findModule, getDefaultSearchPaths, runModule } 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,48 @@ 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
43
+ // Run module with v2.2 envelope format
30
44
  const result = await runModule(module, ctx.provider, {
31
45
  args: options.args,
32
46
  input: inputData,
33
47
  verbose: options.verbose || ctx.verbose,
48
+ validateInput: !options.noValidate,
49
+ validateOutput: !options.noValidate,
50
+ useV22: true, // Always use v2.2 envelope
34
51
  });
35
- // Return envelope format or extracted data
36
- if (options.pretty) {
37
- return {
38
- success: result.ok,
39
- data: result,
40
- };
41
- }
42
- 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
- }
57
- }
52
+ const output = attachContext(result, {
53
+ module: moduleName,
54
+ provider: ctx.provider.name,
55
+ });
56
+ // Always return full v2.2 envelope
57
+ return {
58
+ success: result.ok,
59
+ data: output,
60
+ };
58
61
  }
59
62
  catch (e) {
63
+ const message = e instanceof Error ? e.message : String(e);
64
+ const errorEnvelope = attachContext(makeErrorEnvelope({
65
+ code: ErrorCodes.INTERNAL_ERROR,
66
+ message,
67
+ recoverable: false,
68
+ }), { module: moduleName, provider: ctx.provider.name });
60
69
  return {
61
70
  success: false,
62
- error: e instanceof Error ? e.message : String(e),
71
+ error: message,
72
+ data: errorEnvelope,
63
73
  };
64
74
  }
65
75
  }
@@ -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>;