@fission-ai/openspec 0.16.0 → 0.17.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 (39) hide show
  1. package/README.md +21 -14
  2. package/dist/cli/index.js +67 -2
  3. package/dist/commands/change.js +4 -3
  4. package/dist/commands/completion.d.ts +72 -0
  5. package/dist/commands/completion.js +221 -0
  6. package/dist/commands/config.d.ts +8 -0
  7. package/dist/commands/config.js +198 -0
  8. package/dist/commands/show.js +3 -2
  9. package/dist/commands/spec.js +4 -3
  10. package/dist/commands/validate.js +21 -2
  11. package/dist/core/archive.js +4 -1
  12. package/dist/core/completions/command-registry.d.ts +7 -0
  13. package/dist/core/completions/command-registry.js +362 -0
  14. package/dist/core/completions/completion-provider.d.ts +60 -0
  15. package/dist/core/completions/completion-provider.js +102 -0
  16. package/dist/core/completions/factory.d.ts +51 -0
  17. package/dist/core/completions/factory.js +57 -0
  18. package/dist/core/completions/generators/zsh-generator.d.ts +58 -0
  19. package/dist/core/completions/generators/zsh-generator.js +319 -0
  20. package/dist/core/completions/installers/zsh-installer.d.ts +136 -0
  21. package/dist/core/completions/installers/zsh-installer.js +449 -0
  22. package/dist/core/completions/types.d.ts +78 -0
  23. package/dist/core/completions/types.js +2 -0
  24. package/dist/core/config-schema.d.ts +76 -0
  25. package/dist/core/config-schema.js +200 -0
  26. package/dist/core/configurators/slash/opencode.js +0 -3
  27. package/dist/core/global-config.d.ts +29 -0
  28. package/dist/core/global-config.js +87 -0
  29. package/dist/core/index.d.ts +1 -1
  30. package/dist/core/index.js +2 -1
  31. package/dist/utils/file-system.js +19 -3
  32. package/dist/utils/interactive.d.ts +12 -1
  33. package/dist/utils/interactive.js +7 -2
  34. package/dist/utils/item-discovery.d.ts +1 -0
  35. package/dist/utils/item-discovery.js +23 -0
  36. package/dist/utils/shell-detection.d.ts +20 -0
  37. package/dist/utils/shell-detection.js +41 -0
  38. package/package.json +7 -1
  39. package/scripts/postinstall.js +147 -0
@@ -0,0 +1,449 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { FileSystemUtils } from '../../../utils/file-system.js';
5
+ /**
6
+ * Installer for Zsh completion scripts.
7
+ * Supports both Oh My Zsh and standard Zsh configurations.
8
+ */
9
+ export class ZshInstaller {
10
+ homeDir;
11
+ /**
12
+ * Markers for .zshrc configuration management
13
+ */
14
+ ZSHRC_MARKERS = {
15
+ start: '# OPENSPEC:START',
16
+ end: '# OPENSPEC:END',
17
+ };
18
+ constructor(homeDir = os.homedir()) {
19
+ this.homeDir = homeDir;
20
+ }
21
+ /**
22
+ * Check if Oh My Zsh is installed
23
+ *
24
+ * @returns true if Oh My Zsh is detected via $ZSH env var or directory exists
25
+ */
26
+ async isOhMyZshInstalled() {
27
+ // First check for $ZSH environment variable (standard OMZ setup)
28
+ if (process.env.ZSH) {
29
+ return true;
30
+ }
31
+ // Fall back to checking for ~/.oh-my-zsh directory
32
+ const ohMyZshPath = path.join(this.homeDir, '.oh-my-zsh');
33
+ try {
34
+ const stat = await fs.stat(ohMyZshPath);
35
+ return stat.isDirectory();
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ /**
42
+ * Get the appropriate installation path for the completion script
43
+ *
44
+ * @returns Object with installation path and whether it's Oh My Zsh
45
+ */
46
+ async getInstallationPath() {
47
+ const isOhMyZsh = await this.isOhMyZshInstalled();
48
+ if (isOhMyZsh) {
49
+ // Oh My Zsh custom completions directory
50
+ return {
51
+ path: path.join(this.homeDir, '.oh-my-zsh', 'custom', 'completions', '_openspec'),
52
+ isOhMyZsh: true,
53
+ };
54
+ }
55
+ else {
56
+ // Standard Zsh completions directory
57
+ return {
58
+ path: path.join(this.homeDir, '.zsh', 'completions', '_openspec'),
59
+ isOhMyZsh: false,
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Backup an existing completion file if it exists
65
+ *
66
+ * @param targetPath - Path to the file to backup
67
+ * @returns Path to the backup file, or undefined if no backup was needed
68
+ */
69
+ async backupExistingFile(targetPath) {
70
+ try {
71
+ await fs.access(targetPath);
72
+ // File exists, create a backup
73
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
74
+ const backupPath = `${targetPath}.backup-${timestamp}`;
75
+ await fs.copyFile(targetPath, backupPath);
76
+ return backupPath;
77
+ }
78
+ catch {
79
+ // File doesn't exist, no backup needed
80
+ return undefined;
81
+ }
82
+ }
83
+ /**
84
+ * Get the path to .zshrc file
85
+ *
86
+ * @returns Path to .zshrc
87
+ */
88
+ getZshrcPath() {
89
+ return path.join(this.homeDir, '.zshrc');
90
+ }
91
+ /**
92
+ * Generate .zshrc configuration content
93
+ *
94
+ * @param completionsDir - Directory containing completion scripts
95
+ * @returns Configuration content
96
+ */
97
+ generateZshrcConfig(completionsDir) {
98
+ return [
99
+ '# OpenSpec shell completions configuration',
100
+ `fpath=("${completionsDir}" $fpath)`,
101
+ 'autoload -Uz compinit',
102
+ 'compinit',
103
+ ].join('\n');
104
+ }
105
+ /**
106
+ * Configure .zshrc to enable completions
107
+ * Only applies to standard Zsh (not Oh My Zsh)
108
+ *
109
+ * @param completionsDir - Directory containing completion scripts
110
+ * @returns true if configured successfully, false otherwise
111
+ */
112
+ async configureZshrc(completionsDir) {
113
+ // Check if auto-configuration is disabled
114
+ if (process.env.OPENSPEC_NO_AUTO_CONFIG === '1') {
115
+ return false;
116
+ }
117
+ try {
118
+ const zshrcPath = this.getZshrcPath();
119
+ const config = this.generateZshrcConfig(completionsDir);
120
+ // Check write permissions
121
+ const canWrite = await FileSystemUtils.canWriteFile(zshrcPath);
122
+ if (!canWrite) {
123
+ return false;
124
+ }
125
+ // Use marker-based update
126
+ await FileSystemUtils.updateFileWithMarkers(zshrcPath, config, this.ZSHRC_MARKERS.start, this.ZSHRC_MARKERS.end);
127
+ return true;
128
+ }
129
+ catch (error) {
130
+ // Fail gracefully - don't break installation
131
+ console.debug(`Unable to configure .zshrc for completions: ${error.message}`);
132
+ return false;
133
+ }
134
+ }
135
+ /**
136
+ * Check if .zshrc has OpenSpec configuration markers
137
+ *
138
+ * @returns true if .zshrc exists and has markers
139
+ */
140
+ async hasZshrcConfig() {
141
+ try {
142
+ const zshrcPath = this.getZshrcPath();
143
+ const content = await fs.readFile(zshrcPath, 'utf-8');
144
+ return content.includes(this.ZSHRC_MARKERS.start) && content.includes(this.ZSHRC_MARKERS.end);
145
+ }
146
+ catch {
147
+ return false;
148
+ }
149
+ }
150
+ /**
151
+ * Check if fpath configuration is needed for a given directory
152
+ * Used to verify if Oh My Zsh (or other) completions directory is already in fpath
153
+ *
154
+ * @param completionsDir - Directory to check for in fpath
155
+ * @returns true if configuration is needed, false if directory is already referenced
156
+ */
157
+ async needsFpathConfig(completionsDir) {
158
+ try {
159
+ const zshrcPath = this.getZshrcPath();
160
+ const content = await fs.readFile(zshrcPath, 'utf-8');
161
+ // Check if fpath already includes this directory
162
+ return !content.includes(completionsDir);
163
+ }
164
+ catch (error) {
165
+ // If we can't read .zshrc, assume config is needed
166
+ console.debug(`Unable to read .zshrc to check fpath config: ${error instanceof Error ? error.message : String(error)}`);
167
+ return true;
168
+ }
169
+ }
170
+ /**
171
+ * Remove .zshrc configuration
172
+ * Used during uninstallation
173
+ *
174
+ * @returns true if removed successfully, false otherwise
175
+ */
176
+ async removeZshrcConfig() {
177
+ try {
178
+ const zshrcPath = this.getZshrcPath();
179
+ // Check if file exists
180
+ try {
181
+ await fs.access(zshrcPath);
182
+ }
183
+ catch {
184
+ // File doesn't exist, nothing to remove
185
+ return true;
186
+ }
187
+ // Read file content
188
+ const content = await fs.readFile(zshrcPath, 'utf-8');
189
+ // Check if markers exist
190
+ if (!content.includes(this.ZSHRC_MARKERS.start) || !content.includes(this.ZSHRC_MARKERS.end)) {
191
+ // Markers don't exist, nothing to remove
192
+ return true;
193
+ }
194
+ // Remove content between markers (including markers)
195
+ const lines = content.split('\n');
196
+ const startIndex = lines.findIndex((line) => line.trim() === this.ZSHRC_MARKERS.start);
197
+ const endIndex = lines.findIndex((line) => line.trim() === this.ZSHRC_MARKERS.end);
198
+ if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
199
+ // Invalid marker placement
200
+ return false;
201
+ }
202
+ // Remove lines between markers (inclusive)
203
+ lines.splice(startIndex, endIndex - startIndex + 1);
204
+ // Remove trailing empty lines at the start if the markers were at the top
205
+ while (lines.length > 0 && lines[0].trim() === '') {
206
+ lines.shift();
207
+ }
208
+ // Write back
209
+ await fs.writeFile(zshrcPath, lines.join('\n'), 'utf-8');
210
+ return true;
211
+ }
212
+ catch (error) {
213
+ // Fail gracefully
214
+ console.debug(`Unable to remove .zshrc configuration: ${error.message}`);
215
+ return false;
216
+ }
217
+ }
218
+ /**
219
+ * Install the completion script
220
+ *
221
+ * @param completionScript - The completion script content to install
222
+ * @returns Installation result with status and instructions
223
+ */
224
+ async install(completionScript) {
225
+ try {
226
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
227
+ // Check if already installed with same content
228
+ let isUpdate = false;
229
+ try {
230
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
231
+ if (existingContent === completionScript) {
232
+ // Already installed and up to date
233
+ return {
234
+ success: true,
235
+ installedPath: targetPath,
236
+ isOhMyZsh,
237
+ message: 'Completion script is already installed (up to date)',
238
+ instructions: [
239
+ 'The completion script is already installed and up to date.',
240
+ 'If completions are not working, try: exec zsh',
241
+ ],
242
+ };
243
+ }
244
+ // File exists but content is different - this is an update
245
+ isUpdate = true;
246
+ }
247
+ catch (error) {
248
+ // File doesn't exist or can't be read, proceed with installation
249
+ console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
250
+ }
251
+ // Ensure the directory exists
252
+ const targetDir = path.dirname(targetPath);
253
+ await fs.mkdir(targetDir, { recursive: true });
254
+ // Backup existing file if updating
255
+ const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
256
+ // Write the completion script
257
+ await fs.writeFile(targetPath, completionScript, 'utf-8');
258
+ // Auto-configure .zshrc
259
+ let zshrcConfigured = false;
260
+ if (isOhMyZsh) {
261
+ // For Oh My Zsh, verify that custom/completions is in fpath
262
+ // If not, add it to .zshrc
263
+ const needsConfig = await this.needsFpathConfig(targetDir);
264
+ if (needsConfig) {
265
+ zshrcConfigured = await this.configureZshrc(targetDir);
266
+ }
267
+ }
268
+ else {
269
+ // Standard Zsh always needs .zshrc configuration
270
+ zshrcConfigured = await this.configureZshrc(targetDir);
271
+ }
272
+ // Generate instructions (only if .zshrc wasn't auto-configured)
273
+ let instructions = zshrcConfigured ? undefined : this.generateInstructions(isOhMyZsh, targetPath);
274
+ // Add fpath guidance for Oh My Zsh installations
275
+ if (isOhMyZsh) {
276
+ const fpathGuidance = this.generateOhMyZshFpathGuidance(targetDir);
277
+ if (fpathGuidance) {
278
+ instructions = instructions ? [...instructions, '', ...fpathGuidance] : fpathGuidance;
279
+ }
280
+ }
281
+ // Determine appropriate message based on update status
282
+ let message;
283
+ if (isUpdate) {
284
+ message = backupPath
285
+ ? 'Completion script updated successfully (previous version backed up)'
286
+ : 'Completion script updated successfully';
287
+ }
288
+ else {
289
+ message = isOhMyZsh
290
+ ? 'Completion script installed successfully for Oh My Zsh'
291
+ : zshrcConfigured
292
+ ? 'Completion script installed and .zshrc configured successfully'
293
+ : 'Completion script installed successfully for Zsh';
294
+ }
295
+ return {
296
+ success: true,
297
+ installedPath: targetPath,
298
+ backupPath,
299
+ isOhMyZsh,
300
+ zshrcConfigured,
301
+ message,
302
+ instructions,
303
+ };
304
+ }
305
+ catch (error) {
306
+ return {
307
+ success: false,
308
+ isOhMyZsh: false,
309
+ message: `Failed to install completion script: ${error instanceof Error ? error.message : String(error)}`,
310
+ };
311
+ }
312
+ }
313
+ /**
314
+ * Generate Oh My Zsh fpath verification guidance
315
+ *
316
+ * @param completionsDir - Custom completions directory path
317
+ * @returns Array of guidance strings, or undefined if not needed
318
+ */
319
+ generateOhMyZshFpathGuidance(completionsDir) {
320
+ return [
321
+ 'Note: Oh My Zsh typically auto-loads completions from custom/completions.',
322
+ `Verify that ${completionsDir} is in your fpath by running:`,
323
+ ' echo $fpath | grep "custom/completions"',
324
+ '',
325
+ 'If not found, completions may not work. Restart your shell to ensure changes take effect.',
326
+ ];
327
+ }
328
+ /**
329
+ * Generate user instructions for enabling completions
330
+ *
331
+ * @param isOhMyZsh - Whether Oh My Zsh is being used
332
+ * @param installedPath - Path where the script was installed
333
+ * @returns Array of instruction strings
334
+ */
335
+ generateInstructions(isOhMyZsh, installedPath) {
336
+ if (isOhMyZsh) {
337
+ return [
338
+ 'Completion script installed to Oh My Zsh completions directory.',
339
+ 'Restart your shell or run: exec zsh',
340
+ 'Completions should activate automatically.',
341
+ ];
342
+ }
343
+ else {
344
+ const completionsDir = path.dirname(installedPath);
345
+ const zshrcPath = path.join(this.homeDir, '.zshrc');
346
+ return [
347
+ 'Completion script installed to ~/.zsh/completions/',
348
+ '',
349
+ 'To enable completions, add the following to your ~/.zshrc file:',
350
+ '',
351
+ ` # Add completions directory to fpath`,
352
+ ` fpath=(${completionsDir} $fpath)`,
353
+ '',
354
+ ' # Initialize completion system',
355
+ ' autoload -Uz compinit',
356
+ ' compinit',
357
+ '',
358
+ 'Then restart your shell or run: exec zsh',
359
+ '',
360
+ `Check if these lines already exist in ${zshrcPath} before adding.`,
361
+ ];
362
+ }
363
+ }
364
+ /**
365
+ * Uninstall the completion script
366
+ *
367
+ * @returns true if uninstalled successfully, false otherwise
368
+ */
369
+ async uninstall() {
370
+ try {
371
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
372
+ // Try to remove completion script
373
+ let scriptRemoved = false;
374
+ try {
375
+ await fs.access(targetPath);
376
+ await fs.unlink(targetPath);
377
+ scriptRemoved = true;
378
+ }
379
+ catch {
380
+ // Script not installed
381
+ }
382
+ // Try to remove .zshrc configuration (only for standard Zsh)
383
+ let zshrcWasPresent = false;
384
+ let zshrcCleaned = false;
385
+ if (!isOhMyZsh) {
386
+ zshrcWasPresent = await this.hasZshrcConfig();
387
+ if (zshrcWasPresent) {
388
+ zshrcCleaned = await this.removeZshrcConfig();
389
+ }
390
+ }
391
+ if (!scriptRemoved && !zshrcWasPresent) {
392
+ return {
393
+ success: false,
394
+ message: 'Completion script is not installed',
395
+ };
396
+ }
397
+ const messages = [];
398
+ if (scriptRemoved) {
399
+ messages.push(`Completion script removed from ${targetPath}`);
400
+ }
401
+ if (zshrcCleaned && !isOhMyZsh) {
402
+ messages.push('Removed OpenSpec configuration from ~/.zshrc');
403
+ }
404
+ return {
405
+ success: true,
406
+ message: messages.join('. '),
407
+ };
408
+ }
409
+ catch (error) {
410
+ return {
411
+ success: false,
412
+ message: `Failed to uninstall completion script: ${error instanceof Error ? error.message : String(error)}`,
413
+ };
414
+ }
415
+ }
416
+ /**
417
+ * Check if completion script is currently installed
418
+ *
419
+ * @returns true if the completion script exists
420
+ */
421
+ async isInstalled() {
422
+ try {
423
+ const { path: targetPath } = await this.getInstallationPath();
424
+ await fs.access(targetPath);
425
+ return true;
426
+ }
427
+ catch {
428
+ return false;
429
+ }
430
+ }
431
+ /**
432
+ * Get information about the current installation
433
+ *
434
+ * @returns Installation status information
435
+ */
436
+ async getInstallationInfo() {
437
+ const installed = await this.isInstalled();
438
+ if (!installed) {
439
+ return { installed: false };
440
+ }
441
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
442
+ return {
443
+ installed: true,
444
+ path: targetPath,
445
+ isOhMyZsh,
446
+ };
447
+ }
448
+ }
449
+ //# sourceMappingURL=zsh-installer.js.map
@@ -0,0 +1,78 @@
1
+ import { SupportedShell } from '../../utils/shell-detection.js';
2
+ /**
3
+ * Definition of a command-line flag/option
4
+ */
5
+ export interface FlagDefinition {
6
+ /**
7
+ * Flag name without dashes (e.g., "json", "strict", "no-interactive")
8
+ */
9
+ name: string;
10
+ /**
11
+ * Short flag name without dash (e.g., "y" for "-y")
12
+ */
13
+ short?: string;
14
+ /**
15
+ * Human-readable description of what the flag does
16
+ */
17
+ description: string;
18
+ /**
19
+ * Whether the flag takes an argument value
20
+ */
21
+ takesValue?: boolean;
22
+ /**
23
+ * Possible values for the flag (for completion suggestions)
24
+ */
25
+ values?: string[];
26
+ }
27
+ /**
28
+ * Definition of a CLI command
29
+ */
30
+ export interface CommandDefinition {
31
+ /**
32
+ * Command name (e.g., "init", "validate", "show")
33
+ */
34
+ name: string;
35
+ /**
36
+ * Human-readable description of the command
37
+ */
38
+ description: string;
39
+ /**
40
+ * Flags/options supported by this command
41
+ */
42
+ flags: FlagDefinition[];
43
+ /**
44
+ * Subcommands (e.g., "change show", "spec validate")
45
+ */
46
+ subcommands?: CommandDefinition[];
47
+ /**
48
+ * Whether this command accepts a positional argument (e.g., item name, path)
49
+ */
50
+ acceptsPositional?: boolean;
51
+ /**
52
+ * Type of positional argument for dynamic completion
53
+ * - 'change-id': Complete with active change IDs
54
+ * - 'spec-id': Complete with spec IDs
55
+ * - 'change-or-spec-id': Complete with both changes and specs
56
+ * - 'path': Complete with file paths
57
+ * - 'shell': Complete with supported shell names
58
+ * - undefined: No specific completion
59
+ */
60
+ positionalType?: 'change-id' | 'spec-id' | 'change-or-spec-id' | 'path' | 'shell';
61
+ }
62
+ /**
63
+ * Interface for shell-specific completion script generators
64
+ */
65
+ export interface CompletionGenerator {
66
+ /**
67
+ * The shell type this generator targets
68
+ */
69
+ readonly shell: SupportedShell;
70
+ /**
71
+ * Generate the completion script content
72
+ *
73
+ * @param commands - Command definitions to generate completions for
74
+ * @returns The shell-specific completion script as a string
75
+ */
76
+ generate(commands: CommandDefinition[]): string;
77
+ }
78
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Zod schema for global OpenSpec configuration.
4
+ * Uses passthrough() to preserve unknown fields for forward compatibility.
5
+ */
6
+ export declare const GlobalConfigSchema: z.ZodObject<{
7
+ featureFlags: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>>;
8
+ }, z.core.$loose>;
9
+ export type GlobalConfigType = z.infer<typeof GlobalConfigSchema>;
10
+ /**
11
+ * Default configuration values.
12
+ */
13
+ export declare const DEFAULT_CONFIG: GlobalConfigType;
14
+ /**
15
+ * Validate a config key path for CLI set operations.
16
+ * Unknown top-level keys are rejected unless explicitly allowed by the caller.
17
+ */
18
+ export declare function validateConfigKeyPath(path: string): {
19
+ valid: boolean;
20
+ reason?: string;
21
+ };
22
+ /**
23
+ * Get a nested value from an object using dot notation.
24
+ *
25
+ * @param obj - The object to access
26
+ * @param path - Dot-separated path (e.g., "featureFlags.someFlag")
27
+ * @returns The value at the path, or undefined if not found
28
+ */
29
+ export declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
30
+ /**
31
+ * Set a nested value in an object using dot notation.
32
+ * Creates intermediate objects as needed.
33
+ *
34
+ * @param obj - The object to modify (mutated in place)
35
+ * @param path - Dot-separated path (e.g., "featureFlags.someFlag")
36
+ * @param value - The value to set
37
+ */
38
+ export declare function setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void;
39
+ /**
40
+ * Delete a nested value from an object using dot notation.
41
+ *
42
+ * @param obj - The object to modify (mutated in place)
43
+ * @param path - Dot-separated path (e.g., "featureFlags.someFlag")
44
+ * @returns true if the key existed and was deleted, false otherwise
45
+ */
46
+ export declare function deleteNestedValue(obj: Record<string, unknown>, path: string): boolean;
47
+ /**
48
+ * Coerce a string value to its appropriate type.
49
+ * - "true" / "false" -> boolean
50
+ * - Numeric strings -> number
51
+ * - Everything else -> string
52
+ *
53
+ * @param value - The string value to coerce
54
+ * @param forceString - If true, always return the value as a string
55
+ * @returns The coerced value
56
+ */
57
+ export declare function coerceValue(value: string, forceString?: boolean): string | number | boolean;
58
+ /**
59
+ * Format a value for YAML-like display.
60
+ *
61
+ * @param value - The value to format
62
+ * @param indent - Current indentation level
63
+ * @returns Formatted string
64
+ */
65
+ export declare function formatValueYaml(value: unknown, indent?: number): string;
66
+ /**
67
+ * Validate a configuration object against the schema.
68
+ *
69
+ * @param config - The configuration to validate
70
+ * @returns Validation result with success status and optional error message
71
+ */
72
+ export declare function validateConfig(config: unknown): {
73
+ success: boolean;
74
+ error?: string;
75
+ };
76
+ //# sourceMappingURL=config-schema.d.ts.map