cognitive-modules 0.6.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.
Files changed (78) hide show
  1. package/README.md +165 -0
  2. package/dist/cli.d.ts +16 -0
  3. package/dist/cli.js +335 -0
  4. package/dist/commands/add.d.ts +34 -0
  5. package/dist/commands/add.js +229 -0
  6. package/dist/commands/index.d.ts +11 -0
  7. package/dist/commands/index.js +11 -0
  8. package/dist/commands/init.d.ts +5 -0
  9. package/dist/commands/init.js +78 -0
  10. package/dist/commands/list.d.ts +5 -0
  11. package/dist/commands/list.js +28 -0
  12. package/dist/commands/pipe.d.ts +9 -0
  13. package/dist/commands/pipe.js +59 -0
  14. package/dist/commands/remove.d.ts +10 -0
  15. package/dist/commands/remove.js +47 -0
  16. package/dist/commands/run.d.ts +12 -0
  17. package/dist/commands/run.js +65 -0
  18. package/dist/commands/update.d.ts +14 -0
  19. package/dist/commands/update.js +105 -0
  20. package/dist/commands/versions.d.ts +13 -0
  21. package/dist/commands/versions.js +60 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +11 -0
  24. package/dist/modules/index.d.ts +5 -0
  25. package/dist/modules/index.js +5 -0
  26. package/dist/modules/loader.d.ts +12 -0
  27. package/dist/modules/loader.js +197 -0
  28. package/dist/modules/runner.d.ts +12 -0
  29. package/dist/modules/runner.js +229 -0
  30. package/dist/providers/anthropic.d.ts +14 -0
  31. package/dist/providers/anthropic.js +70 -0
  32. package/dist/providers/base.d.ts +11 -0
  33. package/dist/providers/base.js +19 -0
  34. package/dist/providers/deepseek.d.ts +14 -0
  35. package/dist/providers/deepseek.js +66 -0
  36. package/dist/providers/gemini.d.ts +19 -0
  37. package/dist/providers/gemini.js +94 -0
  38. package/dist/providers/index.d.ts +19 -0
  39. package/dist/providers/index.js +74 -0
  40. package/dist/providers/minimax.d.ts +14 -0
  41. package/dist/providers/minimax.js +64 -0
  42. package/dist/providers/moonshot.d.ts +14 -0
  43. package/dist/providers/moonshot.js +65 -0
  44. package/dist/providers/ollama.d.ts +13 -0
  45. package/dist/providers/ollama.js +64 -0
  46. package/dist/providers/openai.d.ts +14 -0
  47. package/dist/providers/openai.js +67 -0
  48. package/dist/providers/qwen.d.ts +14 -0
  49. package/dist/providers/qwen.js +65 -0
  50. package/dist/types.d.ts +136 -0
  51. package/dist/types.js +5 -0
  52. package/package.json +48 -0
  53. package/src/cli.ts +375 -0
  54. package/src/commands/add.ts +315 -0
  55. package/src/commands/index.ts +12 -0
  56. package/src/commands/init.ts +94 -0
  57. package/src/commands/list.ts +33 -0
  58. package/src/commands/pipe.ts +76 -0
  59. package/src/commands/remove.ts +57 -0
  60. package/src/commands/run.ts +80 -0
  61. package/src/commands/update.ts +130 -0
  62. package/src/commands/versions.ts +79 -0
  63. package/src/index.ts +44 -0
  64. package/src/modules/index.ts +6 -0
  65. package/src/modules/loader.ts +219 -0
  66. package/src/modules/runner.ts +278 -0
  67. package/src/providers/anthropic.ts +89 -0
  68. package/src/providers/base.ts +29 -0
  69. package/src/providers/deepseek.ts +83 -0
  70. package/src/providers/gemini.ts +117 -0
  71. package/src/providers/index.ts +78 -0
  72. package/src/providers/minimax.ts +81 -0
  73. package/src/providers/moonshot.ts +82 -0
  74. package/src/providers/ollama.ts +83 -0
  75. package/src/providers/openai.ts +84 -0
  76. package/src/providers/qwen.ts +82 -0
  77. package/src/types.ts +184 -0
  78. package/tsconfig.json +17 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Update command - Update installed modules to latest version
3
+ *
4
+ * cog update code-simplifier
5
+ * cog update code-simplifier --tag v2.0.0
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { readFile } from 'node:fs/promises';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ import { add, getInstallInfo } from './add.js';
12
+ const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
13
+ /**
14
+ * Get module version from installed module
15
+ */
16
+ async function getInstalledVersion(moduleName) {
17
+ const modulePath = join(USER_MODULES_DIR, moduleName);
18
+ if (!existsSync(modulePath)) {
19
+ return undefined;
20
+ }
21
+ const yaml = await import('js-yaml');
22
+ // Try v2 format
23
+ const yamlPath = join(modulePath, 'module.yaml');
24
+ if (existsSync(yamlPath)) {
25
+ const content = await readFile(yamlPath, 'utf-8');
26
+ const data = yaml.load(content);
27
+ return data?.version;
28
+ }
29
+ // Try v1 format
30
+ const mdPath = existsSync(join(modulePath, 'MODULE.md'))
31
+ ? join(modulePath, 'MODULE.md')
32
+ : join(modulePath, 'module.md');
33
+ if (existsSync(mdPath)) {
34
+ const content = await readFile(mdPath, 'utf-8');
35
+ if (content.startsWith('---')) {
36
+ const parts = content.split('---');
37
+ if (parts.length >= 3) {
38
+ const meta = yaml.load(parts[1]);
39
+ return meta?.version;
40
+ }
41
+ }
42
+ }
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Update an installed module
47
+ */
48
+ export async function update(moduleName, ctx, options = {}) {
49
+ // Get installation info
50
+ const info = await getInstallInfo(moduleName);
51
+ if (!info) {
52
+ return {
53
+ success: false,
54
+ error: `Module not found or not installed from GitHub: ${moduleName}. Only modules installed with 'cog add' can be updated.`,
55
+ };
56
+ }
57
+ if (!info.githubUrl) {
58
+ return {
59
+ success: false,
60
+ error: `Module was not installed from GitHub: ${moduleName}`,
61
+ };
62
+ }
63
+ // Get current version
64
+ const oldVersion = await getInstalledVersion(moduleName);
65
+ // Determine what ref to use
66
+ const tag = options.tag || info.tag;
67
+ const branch = info.branch || 'main';
68
+ // Re-install from source
69
+ const result = await add(info.githubUrl, ctx, {
70
+ module: info.modulePath,
71
+ name: moduleName,
72
+ tag,
73
+ branch: tag ? undefined : branch,
74
+ });
75
+ if (!result.success) {
76
+ return result;
77
+ }
78
+ const data = result.data;
79
+ const newVersion = data.version;
80
+ // Determine message
81
+ let message;
82
+ if (oldVersion && newVersion) {
83
+ if (oldVersion === newVersion) {
84
+ message = `Already up to date: ${moduleName} v${newVersion}`;
85
+ }
86
+ else {
87
+ message = `Updated: ${moduleName} v${oldVersion} → v${newVersion}`;
88
+ }
89
+ }
90
+ else if (newVersion) {
91
+ message = `Updated: ${moduleName} to v${newVersion}`;
92
+ }
93
+ else {
94
+ message = `Updated: ${moduleName}`;
95
+ }
96
+ return {
97
+ success: true,
98
+ data: {
99
+ message,
100
+ name: moduleName,
101
+ oldVersion,
102
+ newVersion,
103
+ },
104
+ };
105
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Versions command - List available versions from GitHub
3
+ *
4
+ * cog versions ziel-io/cognitive-modules
5
+ */
6
+ import type { CommandContext, CommandResult } from '../types.js';
7
+ export interface VersionsOptions {
8
+ limit?: number;
9
+ }
10
+ /**
11
+ * List available versions (tags) from GitHub
12
+ */
13
+ export declare function versions(url: string, ctx: CommandContext, options?: VersionsOptions): Promise<CommandResult>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Versions command - List available versions from GitHub
3
+ *
4
+ * cog versions ziel-io/cognitive-modules
5
+ */
6
+ /**
7
+ * Parse GitHub URL or shorthand
8
+ */
9
+ function parseGitHubUrl(url) {
10
+ if (!url.startsWith('http')) {
11
+ url = `https://github.com/${url}`;
12
+ }
13
+ const match = url.match(/https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/?/);
14
+ if (!match) {
15
+ throw new Error(`Invalid GitHub URL: ${url}`);
16
+ }
17
+ return {
18
+ org: match[1],
19
+ repo: match[2].replace(/\.git$/, ''),
20
+ };
21
+ }
22
+ /**
23
+ * List available versions (tags) from GitHub
24
+ */
25
+ export async function versions(url, ctx, options = {}) {
26
+ const { limit = 10 } = options;
27
+ try {
28
+ const { org, repo } = parseGitHubUrl(url);
29
+ // Fetch tags from GitHub API
30
+ const apiUrl = `https://api.github.com/repos/${org}/${repo}/tags?per_page=${limit}`;
31
+ const response = await fetch(apiUrl, {
32
+ headers: {
33
+ 'User-Agent': 'cognitive-runtime/1.0',
34
+ 'Accept': 'application/vnd.github.v3+json',
35
+ },
36
+ });
37
+ if (!response.ok) {
38
+ if (response.status === 404) {
39
+ throw new Error(`Repository not found: ${org}/${repo}`);
40
+ }
41
+ throw new Error(`GitHub API error: ${response.status}`);
42
+ }
43
+ const data = await response.json();
44
+ const tags = data.map(t => t.name);
45
+ return {
46
+ success: true,
47
+ data: {
48
+ repository: `${org}/${repo}`,
49
+ tags,
50
+ count: tags.length,
51
+ },
52
+ };
53
+ }
54
+ catch (error) {
55
+ return {
56
+ success: false,
57
+ error: error instanceof Error ? error.message : String(error),
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Cognitive Runtime - Main Entry Point
3
+ *
4
+ * Exports all public APIs for programmatic use.
5
+ */
6
+ export type { Provider, InvokeParams, InvokeResult, Message, CognitiveModule, ModuleResult, ModuleInput, ModuleConstraints, ToolsPolicy, OutputContract, FailureContract, CommandContext, CommandResult, } from './types.js';
7
+ export { getProvider, listProviders, GeminiProvider, OpenAIProvider, AnthropicProvider, BaseProvider, } from './providers/index.js';
8
+ export { loadModule, findModule, listModules, getDefaultSearchPaths, runModule, } from './modules/index.js';
9
+ export { run, list, pipe } from './commands/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cognitive Runtime - Main Entry Point
3
+ *
4
+ * Exports all public APIs for programmatic use.
5
+ */
6
+ // Providers
7
+ export { getProvider, listProviders, GeminiProvider, OpenAIProvider, AnthropicProvider, BaseProvider, } from './providers/index.js';
8
+ // Modules
9
+ export { loadModule, findModule, listModules, getDefaultSearchPaths, runModule, } from './modules/index.js';
10
+ // Commands
11
+ export { run, list, pipe } from './commands/index.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Modules - Re-export all module functionality
3
+ */
4
+ export * from './loader.js';
5
+ export * from './runner.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Modules - Re-export all module functionality
3
+ */
4
+ export * from './loader.js';
5
+ export * from './runner.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Module Loader - Load and parse Cognitive Modules
3
+ * Supports both v1 (MODULE.md) and v2 (module.yaml + prompt.md) formats
4
+ */
5
+ import type { CognitiveModule } from '../types.js';
6
+ /**
7
+ * Load a Cognitive Module (auto-detects format)
8
+ */
9
+ export declare function loadModule(modulePath: string): Promise<CognitiveModule>;
10
+ export declare function findModule(name: string, searchPaths: string[]): Promise<CognitiveModule | null>;
11
+ export declare function listModules(searchPaths: string[]): Promise<CognitiveModule[]>;
12
+ export declare function getDefaultSearchPaths(cwd: string): string[];
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Module Loader - Load and parse Cognitive Modules
3
+ * Supports both v1 (MODULE.md) and v2 (module.yaml + prompt.md) formats
4
+ */
5
+ import * as fs from 'node:fs/promises';
6
+ import * as path from 'node:path';
7
+ import yaml from 'js-yaml';
8
+ const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/;
9
+ /**
10
+ * Detect module format version
11
+ */
12
+ async function detectFormat(modulePath) {
13
+ const v2Manifest = path.join(modulePath, 'module.yaml');
14
+ try {
15
+ await fs.access(v2Manifest);
16
+ return 'v2';
17
+ }
18
+ catch {
19
+ return 'v1';
20
+ }
21
+ }
22
+ /**
23
+ * Load v2 format module (module.yaml + prompt.md)
24
+ */
25
+ async function loadModuleV2(modulePath) {
26
+ const manifestFile = path.join(modulePath, 'module.yaml');
27
+ const promptFile = path.join(modulePath, 'prompt.md');
28
+ const schemaFile = path.join(modulePath, 'schema.json');
29
+ // Read module.yaml
30
+ const manifestContent = await fs.readFile(manifestFile, 'utf-8');
31
+ const manifest = yaml.load(manifestContent);
32
+ // Read prompt.md
33
+ let prompt = '';
34
+ try {
35
+ prompt = await fs.readFile(promptFile, 'utf-8');
36
+ }
37
+ catch {
38
+ // prompt.md is optional, manifest may include inline prompt
39
+ }
40
+ // Read schema.json
41
+ let inputSchema;
42
+ let outputSchema;
43
+ let errorSchema;
44
+ try {
45
+ const schemaContent = await fs.readFile(schemaFile, 'utf-8');
46
+ const schema = JSON.parse(schemaContent);
47
+ inputSchema = schema.input;
48
+ outputSchema = schema.output;
49
+ errorSchema = schema.error;
50
+ }
51
+ catch {
52
+ // Schema file is optional but recommended
53
+ }
54
+ return {
55
+ name: manifest.name || path.basename(modulePath),
56
+ version: manifest.version || '1.0.0',
57
+ responsibility: manifest.responsibility || '',
58
+ excludes: manifest.excludes || [],
59
+ constraints: manifest.constraints,
60
+ policies: manifest.policies,
61
+ tools: manifest.tools,
62
+ output: manifest.output,
63
+ failure: manifest.failure,
64
+ runtimeRequirements: manifest.runtime_requirements,
65
+ context: manifest.context,
66
+ prompt,
67
+ inputSchema,
68
+ outputSchema,
69
+ errorSchema,
70
+ location: modulePath,
71
+ format: 'v2',
72
+ };
73
+ }
74
+ /**
75
+ * Load v1 format module (MODULE.md with frontmatter)
76
+ */
77
+ async function loadModuleV1(modulePath) {
78
+ const moduleFile = path.join(modulePath, 'MODULE.md');
79
+ const schemaFile = path.join(modulePath, 'schema.json');
80
+ // Read MODULE.md
81
+ const moduleContent = await fs.readFile(moduleFile, 'utf-8');
82
+ // Parse frontmatter
83
+ const match = moduleContent.match(FRONTMATTER_REGEX);
84
+ if (!match) {
85
+ throw new Error(`Invalid MODULE.md: missing YAML frontmatter in ${moduleFile}`);
86
+ }
87
+ const frontmatter = yaml.load(match[1]);
88
+ const prompt = (match[2] || '').trim();
89
+ // Read schema.json
90
+ let inputSchema;
91
+ let outputSchema;
92
+ try {
93
+ const schemaContent = await fs.readFile(schemaFile, 'utf-8');
94
+ const schema = JSON.parse(schemaContent);
95
+ inputSchema = schema.input;
96
+ outputSchema = schema.output;
97
+ }
98
+ catch {
99
+ // Schema file is optional
100
+ }
101
+ // Extract constraints from v1 format
102
+ const constraints = {};
103
+ const v1Constraints = frontmatter.constraints;
104
+ if (v1Constraints) {
105
+ constraints.no_network = v1Constraints.no_network;
106
+ constraints.no_side_effects = v1Constraints.no_side_effects;
107
+ constraints.no_inventing_data = v1Constraints.no_inventing_data;
108
+ }
109
+ return {
110
+ name: frontmatter.name || path.basename(modulePath),
111
+ version: frontmatter.version || '1.0.0',
112
+ responsibility: frontmatter.responsibility || '',
113
+ excludes: frontmatter.excludes || [],
114
+ constraints: Object.keys(constraints).length > 0 ? constraints : undefined,
115
+ context: frontmatter.context,
116
+ prompt,
117
+ inputSchema,
118
+ outputSchema,
119
+ location: modulePath,
120
+ format: 'v1',
121
+ };
122
+ }
123
+ /**
124
+ * Load a Cognitive Module (auto-detects format)
125
+ */
126
+ export async function loadModule(modulePath) {
127
+ const format = await detectFormat(modulePath);
128
+ if (format === 'v2') {
129
+ return loadModuleV2(modulePath);
130
+ }
131
+ else {
132
+ return loadModuleV1(modulePath);
133
+ }
134
+ }
135
+ /**
136
+ * Check if a directory contains a valid module
137
+ */
138
+ async function isValidModule(modulePath) {
139
+ const v2Manifest = path.join(modulePath, 'module.yaml');
140
+ const v1Module = path.join(modulePath, 'MODULE.md');
141
+ try {
142
+ await fs.access(v2Manifest);
143
+ return true;
144
+ }
145
+ catch {
146
+ try {
147
+ await fs.access(v1Module);
148
+ return true;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ }
155
+ export async function findModule(name, searchPaths) {
156
+ for (const basePath of searchPaths) {
157
+ const modulePath = path.join(basePath, name);
158
+ if (await isValidModule(modulePath)) {
159
+ return await loadModule(modulePath);
160
+ }
161
+ }
162
+ return null;
163
+ }
164
+ export async function listModules(searchPaths) {
165
+ const modules = [];
166
+ for (const basePath of searchPaths) {
167
+ try {
168
+ const entries = await fs.readdir(basePath, { withFileTypes: true });
169
+ for (const entry of entries) {
170
+ if (entry.isDirectory()) {
171
+ const modulePath = path.join(basePath, entry.name);
172
+ if (await isValidModule(modulePath)) {
173
+ try {
174
+ const module = await loadModule(modulePath);
175
+ modules.push(module);
176
+ }
177
+ catch {
178
+ // Skip invalid modules
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ catch {
185
+ // Path doesn't exist, skip
186
+ }
187
+ }
188
+ return modules;
189
+ }
190
+ export function getDefaultSearchPaths(cwd) {
191
+ const home = process.env.HOME || '';
192
+ return [
193
+ path.join(cwd, 'cognitive', 'modules'),
194
+ path.join(cwd, '.cognitive', 'modules'),
195
+ path.join(home, '.cognitive', 'modules'),
196
+ ];
197
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Module Runner - Execute Cognitive Modules
3
+ * v2.1: Envelope format support, clean input mapping
4
+ */
5
+ import type { Provider, CognitiveModule, ModuleResult, ModuleInput } from '../types.js';
6
+ export interface RunOptions {
7
+ input?: ModuleInput;
8
+ args?: string;
9
+ verbose?: boolean;
10
+ useEnvelope?: boolean;
11
+ }
12
+ export declare function runModule(module: CognitiveModule, provider: Provider, options?: RunOptions): Promise<ModuleResult>;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Module Runner - Execute Cognitive Modules
3
+ * v2.1: Envelope format support, clean input mapping
4
+ */
5
+ export async function runModule(module, provider, options = {}) {
6
+ const { args, input, verbose = false, useEnvelope } = options;
7
+ // Determine if we should use envelope format
8
+ const shouldUseEnvelope = useEnvelope ?? (module.output?.envelope === true || module.format === 'v2');
9
+ // Build clean input data (v2 style: no $ARGUMENTS pollution)
10
+ const inputData = input || {};
11
+ // Map legacy --args to clean input
12
+ if (args && !inputData.code && !inputData.query) {
13
+ // Determine if args looks like code or natural language
14
+ if (looksLikeCode(args)) {
15
+ inputData.code = args;
16
+ }
17
+ else {
18
+ inputData.query = args;
19
+ }
20
+ }
21
+ // Build prompt with clean substitution
22
+ const prompt = buildPrompt(module, inputData);
23
+ if (verbose) {
24
+ console.error('--- Module ---');
25
+ console.error(`Name: ${module.name} (${module.format})`);
26
+ console.error(`Responsibility: ${module.responsibility}`);
27
+ console.error(`Envelope: ${shouldUseEnvelope}`);
28
+ console.error('--- Input ---');
29
+ console.error(JSON.stringify(inputData, null, 2));
30
+ console.error('--- Prompt ---');
31
+ console.error(prompt);
32
+ console.error('--- End ---');
33
+ }
34
+ // Build system message based on module config
35
+ const systemParts = [
36
+ `You are executing the "${module.name}" Cognitive Module.`,
37
+ '',
38
+ `RESPONSIBILITY: ${module.responsibility}`,
39
+ ];
40
+ if (module.excludes.length > 0) {
41
+ systemParts.push('', 'YOU MUST NOT:');
42
+ module.excludes.forEach(e => systemParts.push(`- ${e}`));
43
+ }
44
+ if (module.constraints) {
45
+ systemParts.push('', 'CONSTRAINTS:');
46
+ if (module.constraints.no_network)
47
+ systemParts.push('- No network access');
48
+ if (module.constraints.no_side_effects)
49
+ systemParts.push('- No side effects');
50
+ if (module.constraints.no_file_write)
51
+ systemParts.push('- No file writes');
52
+ if (module.constraints.no_inventing_data)
53
+ systemParts.push('- Do not invent data');
54
+ }
55
+ if (module.output?.require_behavior_equivalence) {
56
+ systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
57
+ systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
58
+ systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
59
+ const maxConfidence = module.constraints?.behavior_equivalence_false_max_confidence ?? 0.7;
60
+ systemParts.push(`- If behavior_equivalence=false, confidence MUST be <= ${maxConfidence}`);
61
+ }
62
+ // Add envelope format instructions
63
+ if (shouldUseEnvelope) {
64
+ systemParts.push('', 'RESPONSE FORMAT (Envelope):');
65
+ systemParts.push('- Wrap your response in the envelope format');
66
+ systemParts.push('- Success: { "ok": true, "data": { ...your output... } }');
67
+ systemParts.push('- Error: { "ok": false, "error": { "code": "ERROR_CODE", "message": "..." } }');
68
+ systemParts.push('- Include "confidence" (0-1) and "rationale" in data');
69
+ if (module.output?.require_behavior_equivalence) {
70
+ systemParts.push('- Include "behavior_equivalence" (boolean) in data');
71
+ }
72
+ }
73
+ else {
74
+ systemParts.push('', 'OUTPUT FORMAT:');
75
+ systemParts.push('- Respond with ONLY valid JSON');
76
+ systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
77
+ if (module.output?.require_behavior_equivalence) {
78
+ systemParts.push('- Include "behavior_equivalence" (boolean) field');
79
+ }
80
+ }
81
+ const messages = [
82
+ { role: 'system', content: systemParts.join('\n') },
83
+ { role: 'user', content: prompt },
84
+ ];
85
+ // Invoke provider
86
+ const result = await provider.invoke({
87
+ messages,
88
+ jsonSchema: module.outputSchema,
89
+ temperature: 0.3,
90
+ });
91
+ if (verbose) {
92
+ console.error('--- Response ---');
93
+ console.error(result.content);
94
+ console.error('--- End Response ---');
95
+ }
96
+ // Parse response
97
+ let parsed;
98
+ try {
99
+ const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
100
+ const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
101
+ parsed = JSON.parse(jsonStr.trim());
102
+ }
103
+ catch {
104
+ throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
105
+ }
106
+ // Handle envelope format
107
+ if (shouldUseEnvelope && isEnvelopeResponse(parsed)) {
108
+ return parseEnvelopeResponse(parsed, result.content);
109
+ }
110
+ // Handle legacy format (non-envelope)
111
+ return parseLegacyResponse(parsed, result.content);
112
+ }
113
+ /**
114
+ * Check if response is in envelope format
115
+ */
116
+ function isEnvelopeResponse(obj) {
117
+ if (typeof obj !== 'object' || obj === null)
118
+ return false;
119
+ const o = obj;
120
+ return typeof o.ok === 'boolean';
121
+ }
122
+ /**
123
+ * Parse envelope format response
124
+ */
125
+ function parseEnvelopeResponse(response, raw) {
126
+ if (response.ok) {
127
+ const data = response.data;
128
+ return {
129
+ ok: true,
130
+ data: {
131
+ ...data,
132
+ confidence: typeof data.confidence === 'number' ? data.confidence : 0.5,
133
+ rationale: typeof data.rationale === 'string' ? data.rationale : '',
134
+ behavior_equivalence: data.behavior_equivalence,
135
+ },
136
+ raw,
137
+ };
138
+ }
139
+ else {
140
+ return {
141
+ ok: false,
142
+ error: response.error,
143
+ partial_data: response.partial_data,
144
+ raw,
145
+ };
146
+ }
147
+ }
148
+ /**
149
+ * Parse legacy (non-envelope) format response
150
+ */
151
+ function parseLegacyResponse(output, raw) {
152
+ const outputObj = output;
153
+ const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
154
+ const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
155
+ const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
156
+ ? outputObj.behavior_equivalence
157
+ : undefined;
158
+ // Check if this is an error response (has error.code)
159
+ if (outputObj.error && typeof outputObj.error === 'object') {
160
+ const errorObj = outputObj.error;
161
+ if (typeof errorObj.code === 'string') {
162
+ return {
163
+ ok: false,
164
+ error: {
165
+ code: errorObj.code,
166
+ message: typeof errorObj.message === 'string' ? errorObj.message : 'Unknown error',
167
+ },
168
+ raw,
169
+ };
170
+ }
171
+ }
172
+ return {
173
+ ok: true,
174
+ data: {
175
+ ...outputObj,
176
+ confidence,
177
+ rationale,
178
+ behavior_equivalence: behaviorEquivalence,
179
+ },
180
+ raw,
181
+ };
182
+ }
183
+ /**
184
+ * Build prompt with clean variable substitution
185
+ */
186
+ function buildPrompt(module, input) {
187
+ let prompt = module.prompt;
188
+ // v2 style: substitute ${variable} placeholders
189
+ for (const [key, value] of Object.entries(input)) {
190
+ const strValue = typeof value === 'string' ? value : JSON.stringify(value);
191
+ prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
192
+ }
193
+ // v1 compatibility: substitute $ARGUMENTS
194
+ const argsValue = input.code || input.query || '';
195
+ prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
196
+ // Substitute $N placeholders (v1 compatibility)
197
+ if (typeof argsValue === 'string') {
198
+ const argsList = argsValue.split(/\s+/);
199
+ argsList.forEach((arg, i) => {
200
+ prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
201
+ });
202
+ }
203
+ // Append input summary if not already in prompt
204
+ if (!prompt.includes(argsValue) && argsValue) {
205
+ prompt += '\n\n## Input\n\n';
206
+ if (input.code) {
207
+ prompt += '```\n' + input.code + '\n```\n';
208
+ }
209
+ if (input.query) {
210
+ prompt += input.query + '\n';
211
+ }
212
+ if (input.language) {
213
+ prompt += `\nLanguage: ${input.language}\n`;
214
+ }
215
+ }
216
+ return prompt;
217
+ }
218
+ /**
219
+ * Heuristic to detect if input looks like code
220
+ */
221
+ function looksLikeCode(str) {
222
+ const codeIndicators = [
223
+ /^(def|function|class|const|let|var|import|export|public|private)\s/,
224
+ /[{};()]/,
225
+ /=>/,
226
+ /\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
227
+ ];
228
+ return codeIndicators.some(re => re.test(str));
229
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Anthropic Provider - Claude API
3
+ */
4
+ import { BaseProvider } from './base.js';
5
+ import type { InvokeParams, InvokeResult } from '../types.js';
6
+ export declare class AnthropicProvider extends BaseProvider {
7
+ name: string;
8
+ private apiKey;
9
+ private model;
10
+ private baseUrl;
11
+ constructor(apiKey?: string, model?: string);
12
+ isConfigured(): boolean;
13
+ invoke(params: InvokeParams): Promise<InvokeResult>;
14
+ }