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
package/src/cli.ts ADDED
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cognitive Modules CLI
4
+ *
5
+ * cog run <module> --args "..." - Run a module
6
+ * cog add <url> -m <name> - Add module from GitHub
7
+ * cog update <module> - Update to latest version
8
+ * cog remove <module> - Remove installed module
9
+ * cog versions <url> - List available versions
10
+ * cog list - List available modules
11
+ * cog pipe --module <name> - Pipe mode (stdin/stdout)
12
+ * cog doctor - Check configuration
13
+ *
14
+ * npx cognitive-modules add ziel-io/cognitive-modules -m code-simplifier
15
+ */
16
+
17
+ import { parseArgs } from 'node:util';
18
+ import { getProvider, listProviders } from './providers/index.js';
19
+ import { run, list, pipe, init, add, update, remove, versions } from './commands/index.js';
20
+ import type { CommandContext } from './types.js';
21
+
22
+ const VERSION = '0.6.0';
23
+
24
+ async function main() {
25
+ const args = process.argv.slice(2);
26
+ const command = args[0];
27
+
28
+ if (!command || command === '--help' || command === '-h') {
29
+ printHelp();
30
+ process.exit(0);
31
+ }
32
+
33
+ if (command === '--version' || command === '-v') {
34
+ console.log(`Cognitive Runtime v${VERSION}`);
35
+ process.exit(0);
36
+ }
37
+
38
+ // Parse common options
39
+ const { values } = parseArgs({
40
+ args: args.slice(1),
41
+ options: {
42
+ args: { type: 'string', short: 'a' },
43
+ input: { type: 'string', short: 'i' },
44
+ module: { type: 'string', short: 'm' },
45
+ model: { type: 'string', short: 'M' },
46
+ provider: { type: 'string', short: 'p' },
47
+ pretty: { type: 'boolean', default: false },
48
+ verbose: { type: 'boolean', short: 'V', default: false },
49
+ 'no-validate': { type: 'boolean', default: false },
50
+ // Add/update options
51
+ name: { type: 'string', short: 'n' },
52
+ tag: { type: 'string', short: 't' },
53
+ branch: { type: 'string', short: 'b' },
54
+ limit: { type: 'string', short: 'l' },
55
+ },
56
+ allowPositionals: true,
57
+ });
58
+
59
+ // Get provider
60
+ let provider;
61
+ try {
62
+ provider = getProvider(values.provider, values.model);
63
+ } catch (e) {
64
+ console.error(`Error: ${e instanceof Error ? e.message : e}`);
65
+ process.exit(1);
66
+ }
67
+
68
+ const ctx: CommandContext = {
69
+ cwd: process.cwd(),
70
+ provider,
71
+ verbose: values.verbose,
72
+ };
73
+
74
+ try {
75
+ switch (command) {
76
+ case 'run': {
77
+ const moduleName = args[1];
78
+ if (!moduleName || moduleName.startsWith('-')) {
79
+ console.error('Usage: cog run <module> [--args "..."]');
80
+ process.exit(1);
81
+ }
82
+
83
+ const result = await run(moduleName, ctx, {
84
+ args: values.args,
85
+ input: values.input,
86
+ noValidate: values['no-validate'],
87
+ pretty: values.pretty,
88
+ verbose: values.verbose,
89
+ });
90
+
91
+ if (!result.success) {
92
+ console.error(`Error: ${result.error}`);
93
+ process.exit(1);
94
+ }
95
+
96
+ console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
97
+ break;
98
+ }
99
+
100
+ case 'list': {
101
+ const result = await list(ctx);
102
+ if (!result.success) {
103
+ console.error(`Error: ${result.error}`);
104
+ process.exit(1);
105
+ }
106
+
107
+ const data = result.data as { modules: Array<{ name: string; version: string; responsibility: string; location: string }> };
108
+
109
+ if (data.modules.length === 0) {
110
+ console.log('No modules found.');
111
+ } else {
112
+ console.log('Available Modules:');
113
+ console.log('');
114
+ for (const m of data.modules) {
115
+ console.log(` ${m.name} (v${m.version})`);
116
+ console.log(` ${m.responsibility}`);
117
+ console.log(` ${m.location}`);
118
+ console.log('');
119
+ }
120
+ }
121
+ break;
122
+ }
123
+
124
+ case 'pipe': {
125
+ const moduleName = values.module || args[1];
126
+ if (!moduleName) {
127
+ console.error('Usage: cog pipe --module <name>');
128
+ process.exit(1);
129
+ }
130
+
131
+ await pipe(ctx, {
132
+ module: moduleName,
133
+ noValidate: values['no-validate'],
134
+ });
135
+ break;
136
+ }
137
+
138
+ case 'init': {
139
+ const moduleName = args[1];
140
+ const result = await init(ctx, moduleName);
141
+
142
+ if (!result.success) {
143
+ console.error(`Error: ${result.error}`);
144
+ process.exit(1);
145
+ }
146
+
147
+ const data = result.data as { message: string; location: string; files?: string[]; hint?: string };
148
+ console.log(data.message);
149
+ console.log(` Location: ${data.location}`);
150
+ if (data.files) {
151
+ console.log(` Files: ${data.files.join(', ')}`);
152
+ }
153
+ if (data.hint) {
154
+ console.log(` ${data.hint}`);
155
+ }
156
+ break;
157
+ }
158
+
159
+ case 'doctor': {
160
+ console.log('Cognitive Runtime - Environment Check\n');
161
+
162
+ console.log('Providers:');
163
+ for (const p of listProviders()) {
164
+ const status = p.configured ? '✓ configured' : '– not configured';
165
+ console.log(` ${p.name}: ${status} (${p.model})`);
166
+ }
167
+ console.log('');
168
+
169
+ try {
170
+ const provider = getProvider();
171
+ console.log(`Active provider: ${provider.name}`);
172
+ } catch {
173
+ console.log('Active provider: none (set an API key)');
174
+ }
175
+ break;
176
+ }
177
+
178
+ case 'add': {
179
+ const url = args[1];
180
+ if (!url || url.startsWith('-')) {
181
+ console.error('Usage: cog add <url> [--module <name>] [--tag <version>]');
182
+ console.error('');
183
+ console.error('Examples:');
184
+ console.error(' cog add ziel-io/cognitive-modules -m code-simplifier');
185
+ console.error(' cog add org/repo --module my-module --tag v1.0.0');
186
+ process.exit(1);
187
+ }
188
+
189
+ console.log(`→ Adding module from: ${url}`);
190
+ if (values.module) console.log(` Module path: ${values.module}`);
191
+ if (values.tag) console.log(` Version: ${values.tag}`);
192
+
193
+ const result = await add(url, ctx, {
194
+ module: values.module,
195
+ name: values.name,
196
+ tag: values.tag,
197
+ branch: values.branch,
198
+ });
199
+
200
+ if (!result.success) {
201
+ console.error(`✗ Failed to add module: ${result.error}`);
202
+ process.exit(1);
203
+ }
204
+
205
+ const data = result.data as { message: string; location: string; name: string };
206
+ console.log(`✓ ${data.message}`);
207
+ console.log(` Location: ${data.location}`);
208
+ console.log('');
209
+ console.log('Run with:');
210
+ console.log(` cog run ${data.name} --args "your input"`);
211
+ break;
212
+ }
213
+
214
+ case 'update': {
215
+ const moduleName = args[1];
216
+ if (!moduleName || moduleName.startsWith('-')) {
217
+ console.error('Usage: cog update <module> [--tag <version>]');
218
+ process.exit(1);
219
+ }
220
+
221
+ console.log(`→ Updating module: ${moduleName}`);
222
+
223
+ const result = await update(moduleName, ctx, {
224
+ tag: values.tag,
225
+ });
226
+
227
+ if (!result.success) {
228
+ console.error(`✗ ${result.error}`);
229
+ process.exit(1);
230
+ }
231
+
232
+ const data = result.data as { message: string };
233
+ console.log(`✓ ${data.message}`);
234
+ break;
235
+ }
236
+
237
+ case 'remove': {
238
+ const moduleName = args[1];
239
+ if (!moduleName || moduleName.startsWith('-')) {
240
+ console.error('Usage: cog remove <module>');
241
+ process.exit(1);
242
+ }
243
+
244
+ const result = await remove(moduleName, ctx);
245
+
246
+ if (!result.success) {
247
+ console.error(`✗ ${result.error}`);
248
+ process.exit(1);
249
+ }
250
+
251
+ const data = result.data as { message: string };
252
+ console.log(`✓ ${data.message}`);
253
+ break;
254
+ }
255
+
256
+ case 'versions': {
257
+ const url = args[1];
258
+ if (!url || url.startsWith('-')) {
259
+ console.error('Usage: cog versions <url>');
260
+ console.error('');
261
+ console.error('Examples:');
262
+ console.error(' cog versions ziel-io/cognitive-modules');
263
+ process.exit(1);
264
+ }
265
+
266
+ console.log(`→ Fetching versions from: ${url}\n`);
267
+
268
+ const limit = values.limit ? parseInt(values.limit, 10) : 10;
269
+ const result = await versions(url, ctx, { limit });
270
+
271
+ if (!result.success) {
272
+ console.error(`✗ ${result.error}`);
273
+ process.exit(1);
274
+ }
275
+
276
+ const data = result.data as { tags: string[]; count: number };
277
+
278
+ if (data.tags.length === 0) {
279
+ console.log('No tags/versions found.');
280
+ } else {
281
+ console.log(`Available Versions (${data.count}):`);
282
+ console.log('');
283
+ for (const tag of data.tags) {
284
+ console.log(` ${tag}`);
285
+ console.log(` cog add ${url} --tag ${tag}`);
286
+ }
287
+ }
288
+ break;
289
+ }
290
+
291
+ default:
292
+ console.error(`Unknown command: ${command}`);
293
+ console.error('Run "cog --help" for usage.');
294
+ process.exit(1);
295
+ }
296
+ } catch (e) {
297
+ console.error(`Error: ${e instanceof Error ? e.message : e}`);
298
+ if (values.verbose && e instanceof Error) {
299
+ console.error(e.stack);
300
+ }
301
+ process.exit(1);
302
+ }
303
+ }
304
+
305
+ function printHelp() {
306
+ console.log(`
307
+ Cognitive Runtime v${VERSION}
308
+ Structured AI Task Execution
309
+
310
+ USAGE:
311
+ cog <command> [options]
312
+
313
+ COMMANDS:
314
+ run <module> Run a Cognitive Module
315
+ list List available modules
316
+ add <url> Add module from GitHub
317
+ update <module> Update module to latest version
318
+ remove <module> Remove installed module
319
+ versions <url> List available versions
320
+ pipe Pipe mode (stdin/stdout)
321
+ init [name] Initialize project or create module
322
+ doctor Check configuration
323
+
324
+ OPTIONS:
325
+ -a, --args <str> Arguments to pass to module
326
+ -i, --input <json> JSON input for module
327
+ -m, --module <name> Module path within repo (for add)
328
+ -n, --name <name> Override module name (for add)
329
+ -t, --tag <version> Git tag/version (for add/update)
330
+ -b, --branch <name> Git branch (for add)
331
+ -M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
332
+ -p, --provider <name> LLM provider (gemini, openai, anthropic, deepseek, minimax, moonshot, qwen, ollama)
333
+ --pretty Pretty-print JSON output
334
+ -V, --verbose Verbose output
335
+ --no-validate Skip schema validation
336
+ -v, --version Show version
337
+ -h, --help Show this help
338
+
339
+ EXAMPLES:
340
+ # Add modules from GitHub
341
+ cog add ziel-io/cognitive-modules -m code-simplifier
342
+ cog add org/repo --module my-module --tag v1.0.0
343
+
344
+ # Version management
345
+ cog update code-simplifier
346
+ cog versions ziel-io/cognitive-modules
347
+ cog remove code-simplifier
348
+
349
+ # Run modules
350
+ cog run code-reviewer --args "def foo(): pass"
351
+ cog run code-reviewer --provider openai --model gpt-4o --args "..."
352
+ cog list
353
+
354
+ # Other
355
+ echo "review this code" | cog pipe --module code-reviewer
356
+ cog init my-module
357
+ cog doctor
358
+
359
+ ENVIRONMENT:
360
+ GEMINI_API_KEY Google Gemini
361
+ OPENAI_API_KEY OpenAI
362
+ ANTHROPIC_API_KEY Anthropic Claude
363
+ DEEPSEEK_API_KEY DeepSeek
364
+ MINIMAX_API_KEY MiniMax
365
+ MOONSHOT_API_KEY Moonshot (Kimi)
366
+ DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
367
+ OLLAMA_HOST Ollama local (default: localhost:11434)
368
+ COG_MODEL Override default model for any provider
369
+ `);
370
+ }
371
+
372
+ main().catch(e => {
373
+ console.error('Fatal error:', e);
374
+ process.exit(1);
375
+ });
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Add command - Install modules from GitHub
3
+ *
4
+ * cog add ziel-io/cognitive-modules -m code-simplifier
5
+ * cog add https://github.com/org/repo --module name --tag v1.0.0
6
+ */
7
+
8
+ import { createWriteStream, existsSync, mkdirSync, rmSync, readdirSync, statSync, copyFileSync } from 'node:fs';
9
+ import { writeFile, readFile, mkdir, rm } from 'node:fs/promises';
10
+ import { pipeline } from 'node:stream/promises';
11
+ import { Readable } from 'node:stream';
12
+ import { join, basename } from 'node:path';
13
+ import { homedir, tmpdir } from 'node:os';
14
+ import { createGunzip } from 'node:zlib';
15
+ import type { CommandContext, CommandResult } from '../types.js';
16
+
17
+ // Module storage paths
18
+ const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
19
+ const INSTALLED_MANIFEST = join(homedir(), '.cognitive', 'installed.json');
20
+
21
+ export interface AddOptions {
22
+ module?: string;
23
+ name?: string;
24
+ branch?: string;
25
+ tag?: string;
26
+ }
27
+
28
+ interface InstallManifest {
29
+ [moduleName: string]: {
30
+ source: string;
31
+ githubUrl: string;
32
+ modulePath?: string;
33
+ tag?: string;
34
+ branch?: string;
35
+ version?: string;
36
+ installedAt: string;
37
+ installedTime: string;
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Parse GitHub URL or shorthand
43
+ */
44
+ function parseGitHubUrl(url: string): { org: string; repo: string; fullUrl: string } {
45
+ // Handle shorthand: org/repo
46
+ if (!url.startsWith('http')) {
47
+ url = `https://github.com/${url}`;
48
+ }
49
+
50
+ const match = url.match(/https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/?/);
51
+ if (!match) {
52
+ throw new Error(`Invalid GitHub URL: ${url}`);
53
+ }
54
+
55
+ const org = match[1];
56
+ const repo = match[2].replace(/\.git$/, '');
57
+
58
+ return {
59
+ org,
60
+ repo,
61
+ fullUrl: `https://github.com/${org}/${repo}`,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Download and extract ZIP from GitHub
67
+ */
68
+ async function downloadAndExtract(
69
+ org: string,
70
+ repo: string,
71
+ ref: string,
72
+ isTag: boolean
73
+ ): Promise<string> {
74
+ const zipUrl = isTag
75
+ ? `https://github.com/${org}/${repo}/archive/refs/tags/${ref}.zip`
76
+ : `https://github.com/${org}/${repo}/archive/refs/heads/${ref}.zip`;
77
+
78
+ // Create temp directory
79
+ const tempDir = join(tmpdir(), `cog-${Date.now()}`);
80
+ mkdirSync(tempDir, { recursive: true });
81
+
82
+ const zipPath = join(tempDir, 'repo.zip');
83
+
84
+ // Download ZIP
85
+ const response = await fetch(zipUrl, {
86
+ headers: { 'User-Agent': 'cognitive-runtime/1.0' },
87
+ });
88
+
89
+ if (!response.ok) {
90
+ throw new Error(`Failed to download: ${response.status} ${response.statusText}`);
91
+ }
92
+
93
+ // Save to file
94
+ const fileStream = createWriteStream(zipPath);
95
+ await pipeline(Readable.fromWeb(response.body as any), fileStream);
96
+
97
+ // Extract using built-in unzip (available on most systems)
98
+ const { execSync } = await import('node:child_process');
99
+ execSync(`unzip -q "${zipPath}" -d "${tempDir}"`, { stdio: 'pipe' });
100
+
101
+ // Find extracted directory
102
+ const entries = readdirSync(tempDir).filter(
103
+ e => e !== 'repo.zip' && statSync(join(tempDir, e)).isDirectory()
104
+ );
105
+
106
+ if (entries.length === 0) {
107
+ throw new Error('ZIP file was empty');
108
+ }
109
+
110
+ return join(tempDir, entries[0]);
111
+ }
112
+
113
+ /**
114
+ * Check if a directory is a valid module
115
+ */
116
+ function isValidModule(path: string): boolean {
117
+ return (
118
+ existsSync(join(path, 'module.yaml')) ||
119
+ existsSync(join(path, 'MODULE.md')) ||
120
+ existsSync(join(path, 'module.md'))
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Find module within repository
126
+ */
127
+ function findModuleInRepo(repoRoot: string, modulePath: string): string {
128
+ const possiblePaths = [
129
+ join(repoRoot, modulePath),
130
+ join(repoRoot, 'cognitive', 'modules', modulePath),
131
+ join(repoRoot, 'modules', modulePath),
132
+ ];
133
+
134
+ for (const p of possiblePaths) {
135
+ if (existsSync(p) && isValidModule(p)) {
136
+ return p;
137
+ }
138
+ }
139
+
140
+ throw new Error(
141
+ `Module not found at: ${modulePath}\n` +
142
+ `Searched in: ${possiblePaths.map(p => p.replace(repoRoot, '.')).join(', ')}`
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Copy directory recursively
148
+ */
149
+ function copyDir(src: string, dest: string): void {
150
+ mkdirSync(dest, { recursive: true });
151
+
152
+ for (const entry of readdirSync(src)) {
153
+ const srcPath = join(src, entry);
154
+ const destPath = join(dest, entry);
155
+
156
+ if (statSync(srcPath).isDirectory()) {
157
+ copyDir(srcPath, destPath);
158
+ } else {
159
+ copyFileSync(srcPath, destPath);
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Get module version from module.yaml or MODULE.md
166
+ */
167
+ async function getModuleVersion(modulePath: string): Promise<string | undefined> {
168
+ const yaml = await import('js-yaml');
169
+
170
+ // Try v2 format
171
+ const yamlPath = join(modulePath, 'module.yaml');
172
+ if (existsSync(yamlPath)) {
173
+ const content = await readFile(yamlPath, 'utf-8');
174
+ const data = yaml.load(content) as { version?: string };
175
+ return data?.version;
176
+ }
177
+
178
+ // Try v1 format
179
+ const mdPath = existsSync(join(modulePath, 'MODULE.md'))
180
+ ? join(modulePath, 'MODULE.md')
181
+ : join(modulePath, 'module.md');
182
+
183
+ if (existsSync(mdPath)) {
184
+ const content = await readFile(mdPath, 'utf-8');
185
+ if (content.startsWith('---')) {
186
+ const parts = content.split('---');
187
+ if (parts.length >= 3) {
188
+ const meta = yaml.load(parts[1]) as { version?: string };
189
+ return meta?.version;
190
+ }
191
+ }
192
+ }
193
+
194
+ return undefined;
195
+ }
196
+
197
+ /**
198
+ * Record installation info
199
+ */
200
+ async function recordInstall(
201
+ moduleName: string,
202
+ info: InstallManifest[string]
203
+ ): Promise<void> {
204
+ let manifest: InstallManifest = {};
205
+
206
+ if (existsSync(INSTALLED_MANIFEST)) {
207
+ const content = await readFile(INSTALLED_MANIFEST, 'utf-8');
208
+ manifest = JSON.parse(content);
209
+ }
210
+
211
+ manifest[moduleName] = info;
212
+
213
+ await mkdir(join(homedir(), '.cognitive'), { recursive: true });
214
+ await writeFile(INSTALLED_MANIFEST, JSON.stringify(manifest, null, 2));
215
+ }
216
+
217
+ /**
218
+ * Get installation info for a module
219
+ */
220
+ export async function getInstallInfo(moduleName: string): Promise<InstallManifest[string] | null> {
221
+ if (!existsSync(INSTALLED_MANIFEST)) {
222
+ return null;
223
+ }
224
+
225
+ const content = await readFile(INSTALLED_MANIFEST, 'utf-8');
226
+ const manifest: InstallManifest = JSON.parse(content);
227
+ return manifest[moduleName] || null;
228
+ }
229
+
230
+ /**
231
+ * Add a module from GitHub
232
+ */
233
+ export async function add(
234
+ url: string,
235
+ ctx: CommandContext,
236
+ options: AddOptions = {}
237
+ ): Promise<CommandResult> {
238
+ const { org, repo, fullUrl } = parseGitHubUrl(url);
239
+ const { module: modulePath, name, branch = 'main', tag } = options;
240
+
241
+ // Determine ref (tag takes priority)
242
+ const ref = tag || branch;
243
+ const isTag = !!tag;
244
+
245
+ let repoRoot: string;
246
+ let sourcePath: string;
247
+ let moduleName: string;
248
+
249
+ try {
250
+ // Download repository
251
+ repoRoot = await downloadAndExtract(org, repo, ref, isTag);
252
+
253
+ // Find module
254
+ if (modulePath) {
255
+ sourcePath = findModuleInRepo(repoRoot, modulePath);
256
+ } else {
257
+ // Use repo root as module
258
+ if (!isValidModule(repoRoot)) {
259
+ throw new Error(
260
+ 'Repository root is not a valid module. Use --module to specify the module path.'
261
+ );
262
+ }
263
+ sourcePath = repoRoot;
264
+ }
265
+
266
+ // Determine module name
267
+ moduleName = name || basename(sourcePath);
268
+
269
+ // Get version
270
+ const version = await getModuleVersion(sourcePath);
271
+
272
+ // Install to user modules dir
273
+ const targetPath = join(USER_MODULES_DIR, moduleName);
274
+
275
+ // Remove existing
276
+ if (existsSync(targetPath)) {
277
+ rmSync(targetPath, { recursive: true, force: true });
278
+ }
279
+
280
+ // Copy module
281
+ await mkdir(USER_MODULES_DIR, { recursive: true });
282
+ copyDir(sourcePath, targetPath);
283
+
284
+ // Record installation info
285
+ await recordInstall(moduleName, {
286
+ source: sourcePath,
287
+ githubUrl: fullUrl,
288
+ modulePath,
289
+ tag,
290
+ branch,
291
+ version,
292
+ installedAt: targetPath,
293
+ installedTime: new Date().toISOString(),
294
+ });
295
+
296
+ // Cleanup temp directory
297
+ const tempDir = repoRoot.split('/').slice(0, -1).join('/');
298
+ rmSync(tempDir, { recursive: true, force: true });
299
+
300
+ return {
301
+ success: true,
302
+ data: {
303
+ message: `Added: ${moduleName}${version ? ` v${version}` : ''}`,
304
+ name: moduleName,
305
+ version,
306
+ location: targetPath,
307
+ },
308
+ };
309
+ } catch (error) {
310
+ return {
311
+ success: false,
312
+ error: error instanceof Error ? error.message : String(error),
313
+ };
314
+ }
315
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Commands - Re-export all commands
3
+ */
4
+
5
+ export * from './run.js';
6
+ export * from './list.js';
7
+ export * from './pipe.js';
8
+ export * from './init.js';
9
+ export * from './add.js';
10
+ export * from './update.js';
11
+ export * from './remove.js';
12
+ export * from './versions.js';