@fractary/faber-cli 1.4.4 → 1.5.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.
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Unified YAML Configuration Loader for FABER CLI
3
+ *
4
+ * Loads and parses `.fractary/config.yaml` with environment variable substitution.
5
+ * Provides access to shared configuration for FABER and fractary-core plugins.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as yaml from 'js-yaml';
10
+ import Ajv from 'ajv';
11
+ /**
12
+ * JSON Schema for validating UnifiedConfig structure
13
+ * Validates basic structure while allowing flexibility for plugin-specific sections
14
+ */
15
+ const unifiedConfigSchema = {
16
+ type: 'object',
17
+ properties: {
18
+ version: { type: 'string' },
19
+ anthropic: {
20
+ type: 'object',
21
+ properties: {
22
+ api_key: { type: 'string' },
23
+ model: { type: 'string' },
24
+ max_tokens: { type: 'integer', minimum: 1 },
25
+ },
26
+ additionalProperties: false,
27
+ },
28
+ github: {
29
+ type: 'object',
30
+ properties: {
31
+ token: { type: 'string' },
32
+ organization: { type: 'string' },
33
+ project: { type: 'string' },
34
+ repo: { type: 'string' },
35
+ app: {
36
+ type: 'object',
37
+ properties: {
38
+ id: { type: 'string' },
39
+ installation_id: { type: 'string' },
40
+ private_key_path: { type: 'string' },
41
+ private_key_env_var: { type: 'string' },
42
+ created_via: { type: 'string', enum: ['manifest-flow', 'manual'] },
43
+ created_at: { type: 'string' },
44
+ },
45
+ required: ['id', 'installation_id'],
46
+ additionalProperties: false,
47
+ },
48
+ },
49
+ additionalProperties: false,
50
+ },
51
+ faber: {
52
+ type: 'object',
53
+ properties: {
54
+ worktree: {
55
+ type: 'object',
56
+ properties: {
57
+ location: { type: 'string' },
58
+ inherit_from_claude: { type: 'boolean' },
59
+ },
60
+ additionalProperties: false,
61
+ },
62
+ workflow: {
63
+ type: 'object',
64
+ properties: {
65
+ default: { type: 'string' },
66
+ config_path: { type: 'string' },
67
+ },
68
+ additionalProperties: false,
69
+ },
70
+ backlog_management: {
71
+ type: 'object',
72
+ properties: {
73
+ default_limit: { type: 'integer', minimum: 1 },
74
+ default_order_by: { type: 'string', enum: ['priority', 'created', 'updated', 'none'] },
75
+ priority_config: {
76
+ type: 'object',
77
+ properties: {
78
+ label_prefix: { type: 'string' },
79
+ },
80
+ additionalProperties: false,
81
+ },
82
+ },
83
+ additionalProperties: false,
84
+ },
85
+ },
86
+ additionalProperties: false,
87
+ },
88
+ // Allow pass-through for other plugins
89
+ work: { type: 'object' },
90
+ repo: { type: 'object' },
91
+ logs: { type: 'object' },
92
+ file: { type: 'object' },
93
+ spec: { type: 'object' },
94
+ docs: { type: 'object' },
95
+ },
96
+ additionalProperties: true, // Allow unknown top-level keys for future extensibility
97
+ };
98
+ // Initialize Ajv validator
99
+ const ajv = new Ajv({ allErrors: true, strict: false });
100
+ const validateConfig = ajv.compile(unifiedConfigSchema);
101
+ /**
102
+ * Load and parse `.fractary/config.yaml` with environment variable substitution
103
+ *
104
+ * @param options Configuration loading options
105
+ * @returns Parsed configuration object or null if not found
106
+ * @throws Error if config is invalid or throwIfMissing is true and file doesn't exist
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const config = loadYamlConfig();
111
+ * if (config?.github) {
112
+ * console.log('GitHub config:', config.github);
113
+ * }
114
+ * ```
115
+ */
116
+ export function loadYamlConfig(options = {}) {
117
+ const { projectRoot, warnMissingEnvVars = true, throwIfMissing = false, } = options;
118
+ const root = projectRoot || findProjectRoot();
119
+ const configPath = path.join(root, '.fractary', 'config.yaml');
120
+ if (!fs.existsSync(configPath)) {
121
+ if (throwIfMissing) {
122
+ throw new Error(`Configuration file not found at: ${configPath}\n` +
123
+ `Run 'fractary-core:init' or 'fractary-faber migrate' to create it.`);
124
+ }
125
+ return null;
126
+ }
127
+ try {
128
+ const content = fs.readFileSync(configPath, 'utf-8');
129
+ const substituted = substituteEnvVars(content, warnMissingEnvVars);
130
+ const parsed = yaml.load(substituted);
131
+ // Validate basic structure
132
+ if (!parsed || typeof parsed !== 'object') {
133
+ throw new Error('Invalid configuration: must be a YAML object');
134
+ }
135
+ // Validate against JSON schema
136
+ const isValid = validateConfig(parsed);
137
+ if (!isValid && validateConfig.errors) {
138
+ const errorMessages = validateConfig.errors
139
+ .map(err => ` - ${err.instancePath || 'root'}: ${err.message}`)
140
+ .join('\n');
141
+ throw new Error(`Configuration validation failed:\n${errorMessages}`);
142
+ }
143
+ const config = parsed;
144
+ if (!config.version) {
145
+ console.warn(`Warning: Configuration missing version field in ${configPath}`);
146
+ }
147
+ return config;
148
+ }
149
+ catch (error) {
150
+ if (error instanceof Error) {
151
+ throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
152
+ }
153
+ throw error;
154
+ }
155
+ }
156
+ /**
157
+ * Write unified configuration to `.fractary/config.yaml`
158
+ *
159
+ * @param config Configuration object to write
160
+ * @param projectRoot Project root directory (auto-detected if not provided)
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * writeYamlConfig({
165
+ * version: '2.0',
166
+ * github: {
167
+ * organization: 'myorg',
168
+ * project: 'myrepo'
169
+ * }
170
+ * });
171
+ * ```
172
+ */
173
+ export function writeYamlConfig(config, projectRoot) {
174
+ const root = projectRoot || findProjectRoot();
175
+ const fractaryDir = path.join(root, '.fractary');
176
+ const configPath = path.join(fractaryDir, 'config.yaml');
177
+ // Ensure directory exists (atomic operation - no race condition)
178
+ // mkdirSync with recursive: true is safe even if directory already exists
179
+ fs.mkdirSync(fractaryDir, { recursive: true });
180
+ // Convert to YAML with proper formatting
181
+ const yamlContent = yaml.dump(config, {
182
+ indent: 2,
183
+ lineWidth: 100,
184
+ noRefs: true,
185
+ sortKeys: false,
186
+ });
187
+ fs.writeFileSync(configPath, yamlContent, 'utf-8');
188
+ }
189
+ /**
190
+ * Substitute ${ENV_VAR} placeholders with actual environment variables
191
+ *
192
+ * Supports:
193
+ * - ${VAR_NAME} - Replace with env var value
194
+ * - ${VAR_NAME:-default} - Replace with env var value or default if not set
195
+ *
196
+ * Security: Default values are limited to 1000 characters to prevent abuse.
197
+ * Variable names must match pattern: [A-Z_][A-Z0-9_]*
198
+ *
199
+ * @param content Content with environment variable placeholders
200
+ * @param warnMissing Whether to warn about missing environment variables
201
+ * @returns Content with substituted values
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * const content = 'token: ${GITHUB_TOKEN}';
206
+ * const result = substituteEnvVars(content);
207
+ * // result: 'token: ghp_xxxxx'
208
+ * ```
209
+ */
210
+ export function substituteEnvVars(content, warnMissing = true) {
211
+ // Input validation
212
+ if (typeof content !== 'string') {
213
+ throw new TypeError('Content must be a string');
214
+ }
215
+ // Maximum length for default values to prevent abuse
216
+ const MAX_DEFAULT_LENGTH = 1000;
217
+ return content.replace(/\$\{([A-Z_][A-Z0-9_]*)(:-([^}]+))?\}/g, (match, varName, _, defaultValue) => {
218
+ // Validate variable name format
219
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(varName)) {
220
+ console.warn(`Warning: Invalid environment variable name: ${varName}`);
221
+ return match;
222
+ }
223
+ const value = process.env[varName];
224
+ if (value !== undefined) {
225
+ return value;
226
+ }
227
+ if (defaultValue !== undefined) {
228
+ // Validate default value length
229
+ if (defaultValue.length > MAX_DEFAULT_LENGTH) {
230
+ console.warn(`Warning: Default value for ${varName} exceeds maximum length (${MAX_DEFAULT_LENGTH} chars). ` +
231
+ `Truncating to prevent abuse.`);
232
+ return defaultValue.substring(0, MAX_DEFAULT_LENGTH);
233
+ }
234
+ return defaultValue;
235
+ }
236
+ if (warnMissing) {
237
+ console.warn(`Warning: Environment variable ${varName} not set. ` +
238
+ `Using placeholder value.`);
239
+ }
240
+ // Keep original placeholder if no value found
241
+ return match;
242
+ });
243
+ }
244
+ /**
245
+ * Find project root by looking for .fractary directory or .git
246
+ *
247
+ * Walks up the directory tree from startDir until it finds:
248
+ * - A directory containing `.fractary/`
249
+ * - A directory containing `.git/`
250
+ * - The filesystem root
251
+ *
252
+ * Security: Normalizes paths and prevents traversal outside filesystem boundaries.
253
+ * Maximum of 100 directory levels to prevent infinite loops.
254
+ *
255
+ * @param startDir Directory to start searching from (default: current working directory)
256
+ * @returns Project root directory (normalized absolute path)
257
+ */
258
+ export function findProjectRoot(startDir = process.cwd()) {
259
+ // Input validation and normalization
260
+ if (typeof startDir !== 'string') {
261
+ throw new TypeError('startDir must be a string');
262
+ }
263
+ // Normalize and resolve to absolute path to prevent path traversal
264
+ let currentDir = path.resolve(path.normalize(startDir));
265
+ // Get filesystem root for comparison
266
+ const fsRoot = path.parse(currentDir).root;
267
+ // Safety limit: maximum 100 directory levels to prevent infinite loops
268
+ const MAX_LEVELS = 100;
269
+ let levels = 0;
270
+ while (currentDir !== fsRoot && levels < MAX_LEVELS) {
271
+ try {
272
+ // Check for .fractary directory
273
+ if (fs.existsSync(path.join(currentDir, '.fractary'))) {
274
+ return currentDir;
275
+ }
276
+ // Check for .git directory
277
+ if (fs.existsSync(path.join(currentDir, '.git'))) {
278
+ return currentDir;
279
+ }
280
+ // Move up one directory
281
+ const parentDir = path.dirname(currentDir);
282
+ // Safety check: ensure we're actually moving up
283
+ if (parentDir === currentDir) {
284
+ // Reached filesystem root
285
+ break;
286
+ }
287
+ currentDir = parentDir;
288
+ levels++;
289
+ }
290
+ catch (error) {
291
+ // Handle permission errors or invalid paths gracefully
292
+ console.warn(`Warning: Error accessing directory ${currentDir}: ${error}`);
293
+ break;
294
+ }
295
+ }
296
+ if (levels >= MAX_LEVELS) {
297
+ console.warn(`Warning: Exceeded maximum directory depth (${MAX_LEVELS} levels) while searching for project root`);
298
+ }
299
+ // If no marker found, return the normalized starting directory
300
+ return path.resolve(path.normalize(startDir));
301
+ }
302
+ /**
303
+ * Check if a valid configuration file exists
304
+ *
305
+ * @param projectRoot Project root directory (auto-detected if not provided)
306
+ * @returns true if config exists at .fractary/config.yaml
307
+ */
308
+ export function configExists(projectRoot) {
309
+ const root = projectRoot || findProjectRoot();
310
+ const configPath = path.join(root, '.fractary', 'config.yaml');
311
+ return fs.existsSync(configPath);
312
+ }
313
+ /**
314
+ * Get the configuration file path
315
+ *
316
+ * @param projectRoot Project root directory (auto-detected if not provided)
317
+ * @returns Full path to configuration file
318
+ */
319
+ export function getConfigPath(projectRoot) {
320
+ const root = projectRoot || findProjectRoot();
321
+ return path.join(root, '.fractary', 'config.yaml');
322
+ }
323
+ /**
324
+ * Check if old settings.json exists (for migration)
325
+ *
326
+ * @param projectRoot Project root directory (auto-detected if not provided)
327
+ * @returns true if old settings.json exists
328
+ */
329
+ export function oldSettingsExists(projectRoot) {
330
+ const root = projectRoot || findProjectRoot();
331
+ const settingsPath = path.join(root, '.fractary', 'settings.json');
332
+ return fs.existsSync(settingsPath);
333
+ }
334
+ /**
335
+ * Get the old settings.json file path
336
+ *
337
+ * @param projectRoot Project root directory (auto-detected if not provided)
338
+ * @returns Full path to old settings.json file
339
+ */
340
+ export function getOldSettingsPath(projectRoot) {
341
+ const root = projectRoot || findProjectRoot();
342
+ return path.join(root, '.fractary', 'settings.json');
343
+ }
@@ -2,9 +2,15 @@
2
2
  * FABER CLI Configuration Types
3
3
  *
4
4
  * Type definitions for configuration objects
5
+ * Version 2.0: Unified configuration with shared authentication
6
+ */
7
+ /**
8
+ * Anthropic API configuration (shared across all tools)
5
9
  */
6
10
  export interface AnthropicConfig {
7
11
  api_key?: string;
12
+ model?: string;
13
+ max_tokens?: number;
8
14
  }
9
15
  /**
10
16
  * GitHub App authentication configuration
@@ -17,6 +23,9 @@ export interface GitHubAppConfig {
17
23
  created_via?: 'manifest-flow' | 'manual';
18
24
  created_at?: string;
19
25
  }
26
+ /**
27
+ * GitHub authentication configuration (shared across all tools)
28
+ */
20
29
  export interface GitHubConfig {
21
30
  token?: string;
22
31
  organization?: string;
@@ -39,7 +48,45 @@ export interface BacklogManagementConfig {
39
48
  label_prefix?: string;
40
49
  };
41
50
  }
51
+ /**
52
+ * FABER-specific configuration (v2.0: only FABER-specific settings)
53
+ * Note: anthropic and github are now at the top level of UnifiedConfig
54
+ */
42
55
  export interface FaberConfig {
56
+ worktree?: WorktreeConfig;
57
+ workflow?: WorkflowConfig;
58
+ backlog_management?: BacklogManagementConfig;
59
+ }
60
+ /**
61
+ * Loaded FABER configuration including shared authentication
62
+ * This is what ConfigManager.load() returns - combines FABER-specific
63
+ * settings with shared anthropic/github configuration
64
+ */
65
+ export type LoadedFaberConfig = FaberConfig & {
66
+ anthropic?: AnthropicConfig;
67
+ github?: GitHubConfig;
68
+ };
69
+ /**
70
+ * Unified configuration structure (v2.0)
71
+ * Single source of truth shared across FABER and fractary-core plugins
72
+ */
73
+ export interface UnifiedConfig {
74
+ version: string;
75
+ anthropic?: AnthropicConfig;
76
+ github?: GitHubConfig;
77
+ faber?: FaberConfig;
78
+ work?: any;
79
+ repo?: any;
80
+ logs?: any;
81
+ file?: any;
82
+ spec?: any;
83
+ docs?: any;
84
+ }
85
+ /**
86
+ * Legacy configuration structure (v1.x - deprecated)
87
+ * Kept for backward compatibility during migration
88
+ */
89
+ export interface LegacyFaberConfig {
43
90
  anthropic?: AnthropicConfig;
44
91
  github?: GitHubConfig;
45
92
  worktree?: WorktreeConfig;
@@ -51,4 +98,15 @@ export interface ClaudeConfig {
51
98
  directory?: string;
52
99
  };
53
100
  }
101
+ /**
102
+ * Options for loading YAML configuration
103
+ */
104
+ export interface ConfigLoadOptions {
105
+ /** Project root directory (auto-detected if not provided) */
106
+ projectRoot?: string;
107
+ /** Warn about missing environment variables (default: true) */
108
+ warnMissingEnvVars?: boolean;
109
+ /** Throw error if config file doesn't exist (default: false) */
110
+ throwIfMissing?: boolean;
111
+ }
54
112
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,eAAe,GAAG,QAAQ,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC/D,eAAe,CAAC,EAAE;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,eAAe,GAAG,QAAQ,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC/D,eAAe,CAAC,EAAE;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAC5C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B"}
@@ -2,5 +2,6 @@
2
2
  * FABER CLI Configuration Types
3
3
  *
4
4
  * Type definitions for configuration objects
5
+ * Version 2.0: Unified configuration with shared authentication
5
6
  */
6
7
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fractary/faber-cli",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "FABER CLI - Command-line interface for FABER development toolkit",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -37,14 +37,16 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@fractary/core": "^0.2.0",
40
- "@fractary/faber": "^2.1.1",
40
+ "@fractary/faber": "^2.1.2",
41
41
  "ajv": "^8.12.0",
42
42
  "chalk": "^5.0.0",
43
43
  "commander": "^12.0.0",
44
+ "js-yaml": "^4.1.0",
44
45
  "jsonwebtoken": "^9.0.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@types/jest": "^30.0.0",
49
+ "@types/js-yaml": "^4.0.9",
48
50
  "@types/jsonwebtoken": "^9.0.0",
49
51
  "@types/node": "^20.19.26",
50
52
  "@typescript-eslint/eslint-plugin": "^6.19.0",