cli4ai 1.1.5 → 1.2.1

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 (113) hide show
  1. package/README.md +39 -0
  2. package/dist/bin.d.ts +6 -0
  3. package/dist/bin.js +105 -0
  4. package/dist/cli.d.ts +5 -0
  5. package/dist/cli.js +335 -0
  6. package/dist/commands/add.d.ts +11 -0
  7. package/dist/commands/add.js +459 -0
  8. package/dist/commands/browse.d.ts +4 -0
  9. package/dist/commands/browse.js +379 -0
  10. package/dist/commands/config.d.ts +10 -0
  11. package/dist/commands/config.js +121 -0
  12. package/dist/commands/info.d.ts +9 -0
  13. package/dist/commands/info.js +122 -0
  14. package/dist/commands/init.d.ts +10 -0
  15. package/dist/commands/init.js +458 -0
  16. package/dist/commands/list.d.ts +10 -0
  17. package/dist/commands/list.js +76 -0
  18. package/dist/commands/mcp-config.d.ts +10 -0
  19. package/dist/commands/mcp-config.js +49 -0
  20. package/dist/commands/remotes.d.ts +22 -0
  21. package/dist/commands/remotes.js +196 -0
  22. package/dist/commands/remove.d.ts +8 -0
  23. package/dist/commands/remove.js +61 -0
  24. package/dist/commands/routines.d.ts +29 -0
  25. package/dist/commands/routines.js +363 -0
  26. package/dist/commands/run.d.ts +12 -0
  27. package/dist/commands/run.js +104 -0
  28. package/dist/commands/scheduler.d.ts +27 -0
  29. package/dist/commands/scheduler.js +350 -0
  30. package/dist/commands/search.d.ts +9 -0
  31. package/dist/commands/search.js +159 -0
  32. package/dist/commands/secrets.d.ts +28 -0
  33. package/dist/commands/secrets.js +236 -0
  34. package/dist/commands/serve.d.ts +13 -0
  35. package/dist/commands/serve.js +49 -0
  36. package/dist/commands/start.d.ts +8 -0
  37. package/dist/commands/start.js +27 -0
  38. package/dist/commands/update.d.ts +17 -0
  39. package/dist/commands/update.js +210 -0
  40. package/dist/core/config.d.ts +91 -0
  41. package/dist/core/config.js +738 -0
  42. package/dist/core/execute.d.ts +51 -0
  43. package/dist/core/execute.js +475 -0
  44. package/dist/core/link.d.ts +39 -0
  45. package/dist/core/link.js +214 -0
  46. package/dist/core/lockfile.d.ts +63 -0
  47. package/dist/core/lockfile.js +140 -0
  48. package/dist/core/manifest.d.ts +96 -0
  49. package/dist/core/manifest.js +224 -0
  50. package/dist/core/registry.d.ts +74 -0
  51. package/dist/core/registry.js +116 -0
  52. package/dist/core/remote-client.d.ts +98 -0
  53. package/dist/core/remote-client.js +252 -0
  54. package/dist/core/remotes.d.ts +88 -0
  55. package/dist/core/remotes.js +206 -0
  56. package/dist/core/routine-engine.d.ts +124 -0
  57. package/dist/core/routine-engine.js +699 -0
  58. package/dist/core/routines.d.ts +36 -0
  59. package/dist/core/routines.js +132 -0
  60. package/dist/core/scheduler-daemon.d.ts +10 -0
  61. package/dist/core/scheduler-daemon.js +77 -0
  62. package/dist/core/scheduler.d.ts +131 -0
  63. package/dist/core/scheduler.js +492 -0
  64. package/dist/core/secrets.d.ts +48 -0
  65. package/dist/core/secrets.js +384 -0
  66. package/dist/lib/cli.d.ts +84 -0
  67. package/dist/lib/cli.js +216 -0
  68. package/dist/mcp/adapter.d.ts +35 -0
  69. package/dist/mcp/adapter.js +94 -0
  70. package/dist/mcp/config-gen.d.ts +31 -0
  71. package/dist/mcp/config-gen.js +75 -0
  72. package/dist/mcp/server.d.ts +41 -0
  73. package/dist/mcp/server.js +296 -0
  74. package/dist/server/service.d.ts +85 -0
  75. package/dist/server/service.js +304 -0
  76. package/package.json +6 -3
  77. package/src/bin.ts +0 -118
  78. package/src/cli.ts +0 -409
  79. package/src/commands/add.ts +0 -562
  80. package/src/commands/browse.ts +0 -449
  81. package/src/commands/config.ts +0 -154
  82. package/src/commands/info.ts +0 -102
  83. package/src/commands/init.ts +0 -514
  84. package/src/commands/list.ts +0 -72
  85. package/src/commands/mcp-config.ts +0 -69
  86. package/src/commands/remotes.ts +0 -253
  87. package/src/commands/remove.ts +0 -78
  88. package/src/commands/routines.ts +0 -427
  89. package/src/commands/run.ts +0 -127
  90. package/src/commands/scheduler.ts +0 -438
  91. package/src/commands/search.ts +0 -148
  92. package/src/commands/secrets.ts +0 -292
  93. package/src/commands/serve.ts +0 -66
  94. package/src/commands/start.ts +0 -40
  95. package/src/commands/update.ts +0 -252
  96. package/src/core/config.ts +0 -845
  97. package/src/core/execute.ts +0 -569
  98. package/src/core/link.ts +0 -246
  99. package/src/core/lockfile.ts +0 -187
  100. package/src/core/manifest.ts +0 -327
  101. package/src/core/registry.ts +0 -165
  102. package/src/core/remote-client.ts +0 -419
  103. package/src/core/remotes.ts +0 -268
  104. package/src/core/routine-engine.ts +0 -895
  105. package/src/core/routines.ts +0 -171
  106. package/src/core/scheduler-daemon.ts +0 -94
  107. package/src/core/scheduler.ts +0 -606
  108. package/src/core/secrets.ts +0 -430
  109. package/src/lib/cli.ts +0 -261
  110. package/src/mcp/adapter.ts +0 -131
  111. package/src/mcp/config-gen.ts +0 -106
  112. package/src/mcp/server.ts +0 -365
  113. package/src/server/service.ts +0 -434
@@ -0,0 +1,214 @@
1
+ /**
2
+ * PATH linking for global packages
3
+ *
4
+ * Creates executable symlinks in ~/.cli4ai/bin/ that can be added to PATH
5
+ */
6
+ import { existsSync, mkdirSync, unlinkSync, writeFileSync, chmodSync } from 'fs';
7
+ import { resolve, join } from 'path';
8
+ import { homedir } from 'os';
9
+ // ═══════════════════════════════════════════════════════════════════════════
10
+ // SECURITY: Shell escaping for safe script generation
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+ /**
13
+ * Validate that a string is safe for shell script embedding.
14
+ * Only allows alphanumeric, hyphens, underscores, and dots.
15
+ * This is a strict allowlist approach for defense in depth.
16
+ */
17
+ function isShellSafe(value) {
18
+ return /^[a-zA-Z0-9._-]+$/.test(value);
19
+ }
20
+ /**
21
+ * Escape a string for safe embedding in shell scripts.
22
+ * Uses single quotes which prevent all shell interpretation except for single quotes themselves.
23
+ */
24
+ function shellEscape(value) {
25
+ // Single quotes prevent shell interpretation; escape any single quotes in the value
26
+ // by ending the single-quoted string, adding an escaped single quote, and starting a new single-quoted string
27
+ return "'" + value.replace(/'/g, "'\\''") + "'";
28
+ }
29
+ // ═══════════════════════════════════════════════════════════════════════════
30
+ // PATHS
31
+ // ═══════════════════════════════════════════════════════════════════════════
32
+ export const C4AI_BIN = resolve(homedir(), '.cli4ai', 'bin');
33
+ // ═══════════════════════════════════════════════════════════════════════════
34
+ // FUNCTIONS
35
+ // ═══════════════════════════════════════════════════════════════════════════
36
+ /**
37
+ * Ensure bin directory exists
38
+ */
39
+ export function ensureBinDir() {
40
+ if (!existsSync(C4AI_BIN)) {
41
+ mkdirSync(C4AI_BIN, { recursive: true });
42
+ }
43
+ }
44
+ /**
45
+ * Create executable wrapper script for a package
46
+ *
47
+ * Creates a shell script that invokes `cli4ai run <package> [args]`
48
+ */
49
+ export function linkPackage(manifest, packagePath) {
50
+ ensureBinDir();
51
+ // SECURITY: Validate manifest values before embedding in shell script
52
+ if (!isShellSafe(manifest.name)) {
53
+ throw new Error(`Invalid package name for shell script: ${manifest.name}`);
54
+ }
55
+ if (!isShellSafe(manifest.version)) {
56
+ throw new Error(`Invalid package version for shell script: ${manifest.version}`);
57
+ }
58
+ const binPath = join(C4AI_BIN, manifest.name);
59
+ // Create wrapper script with escaped values for defense in depth
60
+ const safeName = shellEscape(manifest.name);
61
+ const safeVersion = shellEscape(manifest.version);
62
+ const script = `#!/bin/sh
63
+ # cli4ai wrapper for ${safeName}@${safeVersion}
64
+ # Auto-generated - do not edit
65
+
66
+ exec cli4ai run ${safeName} "$@"
67
+ `;
68
+ writeFileSync(binPath, script);
69
+ if (process.platform !== 'win32') {
70
+ chmodSync(binPath, 0o755);
71
+ }
72
+ // Windows compatibility: generate .cmd and .ps1 launchers
73
+ if (process.platform === 'win32') {
74
+ const cmdContent = `@echo off\r\ncli4ai run ${manifest.name} %*\r\nexit /b %errorlevel%\r\n`;
75
+ writeFileSync(binPath + '.cmd', cmdContent);
76
+ const ps1Content = `& cli4ai run ${manifest.name} @args\nexit $LASTEXITCODE\n`;
77
+ writeFileSync(binPath + '.ps1', ps1Content);
78
+ }
79
+ return binPath;
80
+ }
81
+ /**
82
+ * Create direct executable wrapper that runs the tool directly
83
+ *
84
+ * This is faster than going through cli4ai run
85
+ */
86
+ export function linkPackageDirect(manifest, packagePath) {
87
+ ensureBinDir();
88
+ // SECURITY: Validate manifest values before embedding in shell script
89
+ if (!isShellSafe(manifest.name)) {
90
+ throw new Error(`Invalid package name for shell script: ${manifest.name}`);
91
+ }
92
+ if (!isShellSafe(manifest.version)) {
93
+ throw new Error(`Invalid package version for shell script: ${manifest.version}`);
94
+ }
95
+ const binPath = join(C4AI_BIN, manifest.name);
96
+ const entryPath = resolve(packagePath, manifest.entry);
97
+ // SECURITY: Shell-escape the entry path to prevent injection
98
+ const safeEntryPath = shellEscape(entryPath);
99
+ const safeName = shellEscape(manifest.name);
100
+ const safeVersion = shellEscape(manifest.version);
101
+ // Build runtime command - use tsx for TypeScript, node for JavaScript
102
+ let execCommand;
103
+ if (entryPath.endsWith('.ts') || entryPath.endsWith('.tsx')) {
104
+ execCommand = `npx tsx ${safeEntryPath}`;
105
+ }
106
+ else {
107
+ execCommand = `node ${safeEntryPath}`;
108
+ }
109
+ // Create wrapper script that runs the tool directly
110
+ const script = `#!/bin/sh
111
+ # cli4ai wrapper for ${safeName}@${safeVersion}
112
+ # Auto-generated - do not edit
113
+
114
+ exec ${execCommand} "$@"
115
+ `;
116
+ writeFileSync(binPath, script);
117
+ if (process.platform !== 'win32') {
118
+ chmodSync(binPath, 0o755);
119
+ }
120
+ // Windows compatibility: generate .cmd and .ps1 launchers
121
+ if (process.platform === 'win32') {
122
+ const quotedEntry = `"${entryPath.replaceAll('"', '""')}"`;
123
+ let cmdContent;
124
+ let ps1Content;
125
+ if (entryPath.endsWith('.ts') || entryPath.endsWith('.tsx')) {
126
+ cmdContent = `@echo off\r\nnpx tsx ${quotedEntry} %*\r\nexit /b %errorlevel%\r\n`;
127
+ ps1Content = `& npx tsx ${quotedEntry} @args\nexit $LASTEXITCODE\n`;
128
+ }
129
+ else {
130
+ cmdContent = `@echo off\r\nnode ${quotedEntry} %*\r\nexit /b %errorlevel%\r\n`;
131
+ ps1Content = `& node ${quotedEntry} @args\nexit $LASTEXITCODE\n`;
132
+ }
133
+ writeFileSync(binPath + '.cmd', cmdContent);
134
+ writeFileSync(binPath + '.ps1', ps1Content);
135
+ }
136
+ return binPath;
137
+ }
138
+ /**
139
+ * Remove executable link for a package
140
+ */
141
+ export function unlinkPackage(packageName) {
142
+ // SECURITY: Validate package name to prevent path traversal
143
+ if (!isShellSafe(packageName) || packageName.includes('..')) {
144
+ throw new Error(`Invalid package name: ${packageName}`);
145
+ }
146
+ const basePath = join(C4AI_BIN, packageName);
147
+ const candidates = process.platform === 'win32'
148
+ ? [basePath, basePath + '.cmd', basePath + '.ps1']
149
+ : [basePath];
150
+ let removed = false;
151
+ for (const path of candidates) {
152
+ if (existsSync(path)) {
153
+ unlinkSync(path);
154
+ removed = true;
155
+ }
156
+ }
157
+ return removed;
158
+ }
159
+ /**
160
+ * Check if a package is linked
161
+ */
162
+ export function isPackageLinked(packageName) {
163
+ const basePath = join(C4AI_BIN, packageName);
164
+ if (existsSync(basePath))
165
+ return true;
166
+ if (process.platform === 'win32') {
167
+ if (existsSync(basePath + '.cmd'))
168
+ return true;
169
+ if (existsSync(basePath + '.ps1'))
170
+ return true;
171
+ }
172
+ return false;
173
+ }
174
+ /**
175
+ * Get PATH setup instructions
176
+ */
177
+ export function getPathInstructions() {
178
+ if (process.platform === 'win32') {
179
+ return `
180
+ To use globally installed cli4ai packages from anywhere, add the bin directory to your PATH:
181
+
182
+ ${C4AI_BIN}
183
+
184
+ On Windows, you can:
185
+ 1. Use System Settings > Edit environment variables (GUI)
186
+ 2. Or run in PowerShell (current session): $env:PATH += ";${C4AI_BIN}"
187
+ 3. Or run in PowerShell (permanent): [Environment]::SetEnvironmentVariable("PATH", $env:PATH + ";${C4AI_BIN}", "User")
188
+
189
+ Then restart your terminal.
190
+ `.trim();
191
+ }
192
+ const shell = process.env.SHELL || '/bin/bash';
193
+ const isZsh = shell.includes('zsh');
194
+ const rcFile = isZsh ? '~/.zshrc' : '~/.bashrc';
195
+ return `
196
+ To use globally installed cli4ai packages from anywhere, add this to your ${rcFile}:
197
+
198
+ export PATH="${C4AI_BIN}:$PATH"
199
+
200
+ Then reload your shell:
201
+
202
+ source ${rcFile}
203
+
204
+ Or start a new terminal session.
205
+ `.trim();
206
+ }
207
+ /**
208
+ * Check if bin directory is in PATH
209
+ */
210
+ export function isBinInPath() {
211
+ const pathEnv = process.env.PATH || '';
212
+ const separator = process.platform === 'win32' ? ';' : ':';
213
+ return pathEnv.split(separator).some(p => resolve(p) === C4AI_BIN);
214
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Lock file management (cli4ai.lock)
3
+ *
4
+ * Tracks installed packages with exact versions and sources
5
+ * for reproducible installations.
6
+ */
7
+ export interface LockedPackage {
8
+ name: string;
9
+ version: string;
10
+ resolved: string;
11
+ integrity?: string;
12
+ dependencies?: Record<string, string>;
13
+ }
14
+ export interface Lockfile {
15
+ lockfileVersion: number;
16
+ packages: Record<string, LockedPackage>;
17
+ }
18
+ export declare const LOCKFILE_NAME = "cli4ai.lock";
19
+ export declare const LOCKFILE_VERSION = 1;
20
+ /**
21
+ * Get lock file path for a project
22
+ */
23
+ export declare function getLockfilePath(projectDir: string): string;
24
+ /**
25
+ * Check if lock file exists
26
+ */
27
+ export declare function lockfileExists(projectDir: string): boolean;
28
+ /**
29
+ * Load lock file, returns empty lockfile if doesn't exist
30
+ */
31
+ export declare function loadLockfile(projectDir: string): Lockfile;
32
+ /**
33
+ * Save lock file
34
+ */
35
+ export declare function saveLockfile(projectDir: string, lockfile: Lockfile): void;
36
+ /**
37
+ * Add or update a package in the lock file
38
+ */
39
+ export declare function lockPackage(projectDir: string, pkg: LockedPackage): void;
40
+ /**
41
+ * Remove a package from the lock file
42
+ */
43
+ export declare function unlockPackage(projectDir: string, packageName: string): void;
44
+ /**
45
+ * Get a locked package by name
46
+ */
47
+ export declare function getLockedPackage(projectDir: string, packageName: string): LockedPackage | null;
48
+ /**
49
+ * Get all locked packages
50
+ */
51
+ export declare function getLockedPackages(projectDir: string): LockedPackage[];
52
+ /**
53
+ * Check if a package is locked
54
+ */
55
+ export declare function isPackageLocked(projectDir: string, packageName: string): boolean;
56
+ /**
57
+ * Clear all packages from lock file
58
+ */
59
+ export declare function clearLockfile(projectDir: string): void;
60
+ /**
61
+ * Format lock file as human-readable string
62
+ */
63
+ export declare function formatLockfile(lockfile: Lockfile): string;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Lock file management (cli4ai.lock)
3
+ *
4
+ * Tracks installed packages with exact versions and sources
5
+ * for reproducible installations.
6
+ */
7
+ import { readFileSync, writeFileSync, renameSync, existsSync } from 'fs';
8
+ import { resolve } from 'path';
9
+ // ═══════════════════════════════════════════════════════════════════════════
10
+ // CONSTANTS
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+ export const LOCKFILE_NAME = 'cli4ai.lock';
13
+ export const LOCKFILE_VERSION = 1;
14
+ // ═══════════════════════════════════════════════════════════════════════════
15
+ // FUNCTIONS
16
+ // ═══════════════════════════════════════════════════════════════════════════
17
+ /**
18
+ * Get lock file path for a project
19
+ */
20
+ export function getLockfilePath(projectDir) {
21
+ return resolve(projectDir, LOCKFILE_NAME);
22
+ }
23
+ /**
24
+ * Check if lock file exists
25
+ */
26
+ export function lockfileExists(projectDir) {
27
+ return existsSync(getLockfilePath(projectDir));
28
+ }
29
+ /**
30
+ * Load lock file, returns empty lockfile if doesn't exist
31
+ */
32
+ export function loadLockfile(projectDir) {
33
+ const lockfilePath = getLockfilePath(projectDir);
34
+ if (!existsSync(lockfilePath)) {
35
+ return {
36
+ lockfileVersion: LOCKFILE_VERSION,
37
+ packages: {}
38
+ };
39
+ }
40
+ try {
41
+ const content = readFileSync(lockfilePath, 'utf-8');
42
+ const data = JSON.parse(content);
43
+ // Validate version
44
+ if (data.lockfileVersion !== LOCKFILE_VERSION) {
45
+ console.error(`Warning: Lock file version mismatch (${data.lockfileVersion} vs ${LOCKFILE_VERSION})`);
46
+ }
47
+ return data;
48
+ }
49
+ catch (err) {
50
+ console.error('Warning: Could not parse lock file, starting fresh');
51
+ return {
52
+ lockfileVersion: LOCKFILE_VERSION,
53
+ packages: {}
54
+ };
55
+ }
56
+ }
57
+ /**
58
+ * Save lock file
59
+ */
60
+ export function saveLockfile(projectDir, lockfile) {
61
+ const lockfilePath = getLockfilePath(projectDir);
62
+ const content = JSON.stringify(lockfile, null, 2) + '\n';
63
+ const tmpPath = lockfilePath + '.tmp';
64
+ writeFileSync(tmpPath, content);
65
+ renameSync(tmpPath, lockfilePath);
66
+ }
67
+ /**
68
+ * Add or update a package in the lock file
69
+ */
70
+ export function lockPackage(projectDir, pkg) {
71
+ const lockfile = loadLockfile(projectDir);
72
+ lockfile.packages[pkg.name] = pkg;
73
+ saveLockfile(projectDir, lockfile);
74
+ }
75
+ /**
76
+ * Remove a package from the lock file
77
+ */
78
+ export function unlockPackage(projectDir, packageName) {
79
+ const lockfile = loadLockfile(projectDir);
80
+ if (lockfile.packages[packageName]) {
81
+ delete lockfile.packages[packageName];
82
+ saveLockfile(projectDir, lockfile);
83
+ }
84
+ }
85
+ /**
86
+ * Get a locked package by name
87
+ */
88
+ export function getLockedPackage(projectDir, packageName) {
89
+ const lockfile = loadLockfile(projectDir);
90
+ return lockfile.packages[packageName] || null;
91
+ }
92
+ /**
93
+ * Get all locked packages
94
+ */
95
+ export function getLockedPackages(projectDir) {
96
+ const lockfile = loadLockfile(projectDir);
97
+ return Object.values(lockfile.packages);
98
+ }
99
+ /**
100
+ * Check if a package is locked
101
+ */
102
+ export function isPackageLocked(projectDir, packageName) {
103
+ const lockfile = loadLockfile(projectDir);
104
+ return packageName in lockfile.packages;
105
+ }
106
+ /**
107
+ * Clear all packages from lock file
108
+ */
109
+ export function clearLockfile(projectDir) {
110
+ saveLockfile(projectDir, {
111
+ lockfileVersion: LOCKFILE_VERSION,
112
+ packages: {}
113
+ });
114
+ }
115
+ /**
116
+ * Format lock file as human-readable string
117
+ */
118
+ export function formatLockfile(lockfile) {
119
+ const lines = [
120
+ `# cli4ai.lock - Auto-generated, do not edit`,
121
+ `# lockfileVersion: ${lockfile.lockfileVersion}`,
122
+ ``
123
+ ];
124
+ const sortedPackages = Object.values(lockfile.packages).sort((a, b) => a.name.localeCompare(b.name));
125
+ for (const pkg of sortedPackages) {
126
+ lines.push(`${pkg.name}@${pkg.version}:`);
127
+ lines.push(` resolved: "${pkg.resolved}"`);
128
+ if (pkg.integrity) {
129
+ lines.push(` integrity: ${pkg.integrity}`);
130
+ }
131
+ if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
132
+ lines.push(` dependencies:`);
133
+ for (const [depName, depVersion] of Object.entries(pkg.dependencies)) {
134
+ lines.push(` ${depName}: "${depVersion}"`);
135
+ }
136
+ }
137
+ lines.push(``);
138
+ }
139
+ return lines.join('\n');
140
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * cli4ai.json manifest validation and parsing
3
+ */
4
+ export declare class ManifestValidationError extends Error {
5
+ details?: Record<string, unknown> | undefined;
6
+ constructor(message: string, details?: Record<string, unknown> | undefined);
7
+ }
8
+ export interface CommandArg {
9
+ name: string;
10
+ description?: string;
11
+ required?: boolean;
12
+ type?: 'string' | 'number' | 'boolean';
13
+ default?: string | number | boolean;
14
+ }
15
+ export interface CommandOption {
16
+ name: string;
17
+ short?: string;
18
+ description?: string;
19
+ type?: 'string' | 'number' | 'boolean';
20
+ default?: string | number | boolean;
21
+ }
22
+ export interface CommandDef {
23
+ description: string;
24
+ args?: CommandArg[];
25
+ options?: CommandOption[];
26
+ }
27
+ export interface EnvDef {
28
+ required?: boolean;
29
+ description?: string;
30
+ default?: string;
31
+ secret?: boolean;
32
+ }
33
+ export interface McpConfig {
34
+ enabled?: boolean;
35
+ transport?: 'stdio' | 'http';
36
+ port?: number;
37
+ }
38
+ export interface Manifest {
39
+ name: string;
40
+ version: string;
41
+ entry: string;
42
+ description?: string;
43
+ author?: string;
44
+ license?: string;
45
+ repository?: string;
46
+ homepage?: string;
47
+ keywords?: string[];
48
+ runtime?: 'node' | 'bun';
49
+ commands?: Record<string, CommandDef>;
50
+ env?: Record<string, EnvDef>;
51
+ dependencies?: Record<string, string>;
52
+ peerDependencies?: Record<string, string>;
53
+ mcp?: McpConfig;
54
+ scripts?: Record<string, string>;
55
+ files?: string[];
56
+ }
57
+ export declare function validateManifest(manifest: unknown, source?: string): Manifest;
58
+ export declare const MANIFEST_FILENAME = "cli4ai.json";
59
+ export declare class ManifestNotFoundError extends Error {
60
+ path: string;
61
+ constructor(path: string);
62
+ }
63
+ export declare class ManifestParseError extends Error {
64
+ path: string;
65
+ constructor(path: string, message: string);
66
+ }
67
+ /**
68
+ * Load and validate cli4ai.json from a directory (throws on error)
69
+ * Use this for programmatic/testable usage
70
+ */
71
+ export declare function loadManifestOrThrow(dir: string): Manifest;
72
+ /**
73
+ * Load manifest from package.json (fallback for npm packages)
74
+ */
75
+ export declare function loadFromPackageJson(dir: string): Manifest | null;
76
+ /**
77
+ * Load and validate cli4ai.json from a directory
78
+ * Falls back to package.json for npm packages
79
+ * Exits with error on failure (for CLI usage)
80
+ */
81
+ export declare function loadManifest(dir: string): Manifest;
82
+ /**
83
+ * Try to load manifest, return null if not found
84
+ */
85
+ export declare function tryLoadManifest(dir: string): Manifest | null;
86
+ /**
87
+ * Find manifest by walking up directory tree
88
+ */
89
+ export declare function findManifest(startDir?: string): {
90
+ manifest: Manifest;
91
+ dir: string;
92
+ } | null;
93
+ /**
94
+ * Create a minimal manifest
95
+ */
96
+ export declare function createManifest(name: string, options?: Partial<Manifest>): Manifest;