@fractary/core 0.1.0 → 0.2.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 (50) hide show
  1. package/README.md +183 -30
  2. package/dist/common/config.d.ts +22 -6
  3. package/dist/common/config.d.ts.map +1 -1
  4. package/dist/common/config.js +43 -14
  5. package/dist/common/config.js.map +1 -1
  6. package/dist/common/exec-file-no-throw.d.ts +42 -0
  7. package/dist/common/exec-file-no-throw.d.ts.map +1 -0
  8. package/dist/common/exec-file-no-throw.js +54 -0
  9. package/dist/common/exec-file-no-throw.js.map +1 -0
  10. package/dist/common/types.d.ts +15 -0
  11. package/dist/common/types.d.ts.map +1 -1
  12. package/dist/common/yaml-config.d.ts +198 -0
  13. package/dist/common/yaml-config.d.ts.map +1 -0
  14. package/dist/common/yaml-config.js +300 -0
  15. package/dist/common/yaml-config.js.map +1 -0
  16. package/dist/common/yaml-config.test.d.ts +5 -0
  17. package/dist/common/yaml-config.test.d.ts.map +1 -0
  18. package/dist/common/yaml-config.test.js +551 -0
  19. package/dist/common/yaml-config.test.js.map +1 -0
  20. package/dist/repo/config.d.ts +112 -0
  21. package/dist/repo/config.d.ts.map +1 -0
  22. package/dist/repo/config.js +202 -0
  23. package/dist/repo/config.js.map +1 -0
  24. package/dist/repo/config.test.d.ts +7 -0
  25. package/dist/repo/config.test.d.ts.map +1 -0
  26. package/dist/repo/config.test.js +173 -0
  27. package/dist/repo/config.test.js.map +1 -0
  28. package/dist/repo/manager.d.ts +20 -1
  29. package/dist/repo/manager.d.ts.map +1 -1
  30. package/dist/repo/manager.js +68 -2
  31. package/dist/repo/manager.js.map +1 -1
  32. package/dist/repo/organization.d.ts +46 -0
  33. package/dist/repo/organization.d.ts.map +1 -0
  34. package/dist/repo/organization.js +119 -0
  35. package/dist/repo/organization.js.map +1 -0
  36. package/dist/repo/organization.test.d.ts +7 -0
  37. package/dist/repo/organization.test.d.ts.map +1 -0
  38. package/dist/repo/organization.test.js +241 -0
  39. package/dist/repo/organization.test.js.map +1 -0
  40. package/dist/repo/path-generator.d.ts +53 -0
  41. package/dist/repo/path-generator.d.ts.map +1 -0
  42. package/dist/repo/path-generator.js +126 -0
  43. package/dist/repo/path-generator.js.map +1 -0
  44. package/dist/repo/path-generator.test.d.ts +7 -0
  45. package/dist/repo/path-generator.test.d.ts.map +1 -0
  46. package/dist/repo/path-generator.test.js +278 -0
  47. package/dist/repo/path-generator.test.js.map +1 -0
  48. package/dist/spec/manager.js +1 -1
  49. package/dist/spec/manager.js.map +1 -1
  50. package/package.json +3 -2
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Unified YAML Configuration Loader
3
+ *
4
+ * Loads and parses `.fractary/core/config.yaml` with environment variable substitution.
5
+ * Provides a single source of truth for all plugin configurations.
6
+ */
7
+ /**
8
+ * Work tracking configuration
9
+ */
10
+ export interface WorkConfig {
11
+ active_handler: string;
12
+ handlers: Record<string, any>;
13
+ defaults?: Record<string, any>;
14
+ hooks?: Record<string, any>;
15
+ advanced?: Record<string, any>;
16
+ }
17
+ /**
18
+ * Repository management configuration
19
+ */
20
+ export interface RepoConfig {
21
+ active_handler: string;
22
+ handlers: Record<string, any>;
23
+ defaults?: Record<string, any>;
24
+ faber_integration?: Record<string, any>;
25
+ hooks?: Record<string, any>;
26
+ platform_specific?: Record<string, any>;
27
+ }
28
+ /**
29
+ * Logs management configuration
30
+ */
31
+ export interface LogsConfig {
32
+ schema_version: string;
33
+ storage?: Record<string, any>;
34
+ retention?: Record<string, any>;
35
+ session_logging?: Record<string, any>;
36
+ auto_backup?: Record<string, any>;
37
+ summarization?: Record<string, any>;
38
+ archive?: Record<string, any>;
39
+ search?: Record<string, any>;
40
+ integration?: Record<string, any>;
41
+ docs_integration?: Record<string, any>;
42
+ }
43
+ /**
44
+ * File storage configuration
45
+ */
46
+ export interface FileConfig {
47
+ schema_version: string;
48
+ active_handler: string;
49
+ handlers: Record<string, any>;
50
+ global_settings?: Record<string, any>;
51
+ }
52
+ /**
53
+ * Specification management configuration
54
+ */
55
+ export interface SpecConfig {
56
+ schema_version: string;
57
+ storage?: Record<string, any>;
58
+ naming?: Record<string, any>;
59
+ archive?: Record<string, any>;
60
+ integration?: Record<string, any>;
61
+ templates?: Record<string, any>;
62
+ }
63
+ /**
64
+ * Documentation management configuration
65
+ */
66
+ export interface DocsConfig {
67
+ schema_version: string;
68
+ hooks?: Record<string, any>;
69
+ doc_types?: Record<string, any>;
70
+ output_paths?: Record<string, any>;
71
+ templates?: Record<string, any>;
72
+ frontmatter?: Record<string, any>;
73
+ validation?: Record<string, any>;
74
+ linking?: Record<string, any>;
75
+ }
76
+ /**
77
+ * Unified configuration structure for all Fractary Core plugins
78
+ */
79
+ export interface CoreYamlConfig {
80
+ version: string;
81
+ work?: WorkConfig;
82
+ repo?: RepoConfig;
83
+ logs?: LogsConfig;
84
+ file?: FileConfig;
85
+ spec?: SpecConfig;
86
+ docs?: DocsConfig;
87
+ }
88
+ /**
89
+ * Configuration loading options
90
+ */
91
+ export interface ConfigLoadOptions {
92
+ /** Project root directory (auto-detected if not provided) */
93
+ projectRoot?: string;
94
+ /** Warn about missing environment variables (default: true) */
95
+ warnMissingEnvVars?: boolean;
96
+ /** Throw error if config file doesn't exist (default: false) */
97
+ throwIfMissing?: boolean;
98
+ }
99
+ /**
100
+ * Load and parse `.fractary/core/config.yaml` with environment variable substitution
101
+ *
102
+ * @param options Configuration loading options
103
+ * @returns Parsed configuration object or null if not found
104
+ * @throws Error if config is invalid or throwIfMissing is true and file doesn't exist
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const config = loadYamlConfig();
109
+ * if (config?.work) {
110
+ * console.log('Work config:', config.work);
111
+ * }
112
+ * ```
113
+ */
114
+ export declare function loadYamlConfig(options?: ConfigLoadOptions): CoreYamlConfig | null;
115
+ /**
116
+ * Write unified configuration to `.fractary/core/config.yaml`
117
+ *
118
+ * @param config Configuration object to write
119
+ * @param projectRoot Project root directory (auto-detected if not provided)
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * writeYamlConfig({
124
+ * version: '2.0',
125
+ * work: {
126
+ * active_handler: 'github',
127
+ * handlers: { ... }
128
+ * }
129
+ * });
130
+ * ```
131
+ */
132
+ export declare function writeYamlConfig(config: CoreYamlConfig, projectRoot?: string): void;
133
+ /**
134
+ * Substitute ${ENV_VAR} placeholders with actual environment variables
135
+ *
136
+ * Supports:
137
+ * - ${VAR_NAME} - Replace with env var value
138
+ * - ${VAR_NAME:-default} - Replace with env var value or default if not set
139
+ *
140
+ * Security: Default values are limited to 1000 characters to prevent abuse.
141
+ * Variable names must match pattern: [A-Z_][A-Z0-9_]*
142
+ *
143
+ * @param content Content with environment variable placeholders
144
+ * @param warnMissing Whether to warn about missing environment variables
145
+ * @returns Content with substituted values
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const content = 'token: ${GITHUB_TOKEN}';
150
+ * const result = substituteEnvVars(content);
151
+ * // result: 'token: ghp_xxxxx'
152
+ * ```
153
+ */
154
+ export declare function substituteEnvVars(content: string, warnMissing?: boolean): string;
155
+ /**
156
+ * Find project root by looking for .fractary directory or .git
157
+ *
158
+ * Walks up the directory tree from startDir until it finds:
159
+ * - A directory containing `.fractary/`
160
+ * - A directory containing `.git/`
161
+ * - The filesystem root
162
+ *
163
+ * Security: Normalizes paths and prevents traversal outside filesystem boundaries.
164
+ * Maximum of 100 directory levels to prevent infinite loops.
165
+ *
166
+ * @param startDir Directory to start searching from (default: current working directory)
167
+ * @returns Project root directory (normalized absolute path)
168
+ */
169
+ export declare function findProjectRoot(startDir?: string): string;
170
+ /**
171
+ * Check if a valid configuration file exists
172
+ *
173
+ * @param projectRoot Project root directory (auto-detected if not provided)
174
+ * @returns true if `.fractary/core/config.yaml` exists
175
+ */
176
+ export declare function configExists(projectRoot?: string): boolean;
177
+ /**
178
+ * Get the configuration file path
179
+ *
180
+ * @param projectRoot Project root directory (auto-detected if not provided)
181
+ * @returns Full path to configuration file
182
+ */
183
+ export declare function getConfigPath(projectRoot?: string): string;
184
+ /**
185
+ * Get the .fractary/core directory path
186
+ *
187
+ * @param projectRoot Project root directory (auto-detected if not provided)
188
+ * @returns Full path to .fractary/core directory
189
+ */
190
+ export declare function getCoreDir(projectRoot?: string): string;
191
+ /**
192
+ * Validate that environment variables referenced in config exist
193
+ *
194
+ * @param config Configuration object to validate
195
+ * @returns Array of missing environment variable names
196
+ */
197
+ export declare function validateEnvVars(config: CoreYamlConfig): string[];
198
+ //# sourceMappingURL=yaml-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml-config.d.ts","sourceRoot":"","sources":["../../src/common/yaml-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ /**
3
+ * Unified YAML Configuration Loader
4
+ *
5
+ * Loads and parses `.fractary/core/config.yaml` with environment variable substitution.
6
+ * Provides a single source of truth for all plugin configurations.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.loadYamlConfig = loadYamlConfig;
43
+ exports.writeYamlConfig = writeYamlConfig;
44
+ exports.substituteEnvVars = substituteEnvVars;
45
+ exports.findProjectRoot = findProjectRoot;
46
+ exports.configExists = configExists;
47
+ exports.getConfigPath = getConfigPath;
48
+ exports.getCoreDir = getCoreDir;
49
+ exports.validateEnvVars = validateEnvVars;
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const yaml = __importStar(require("js-yaml"));
53
+ /**
54
+ * Load and parse `.fractary/core/config.yaml` with environment variable substitution
55
+ *
56
+ * @param options Configuration loading options
57
+ * @returns Parsed configuration object or null if not found
58
+ * @throws Error if config is invalid or throwIfMissing is true and file doesn't exist
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const config = loadYamlConfig();
63
+ * if (config?.work) {
64
+ * console.log('Work config:', config.work);
65
+ * }
66
+ * ```
67
+ */
68
+ function loadYamlConfig(options = {}) {
69
+ const { projectRoot, warnMissingEnvVars = true, throwIfMissing = false, } = options;
70
+ const root = projectRoot || findProjectRoot();
71
+ const configPath = path.join(root, '.fractary', 'core', 'config.yaml');
72
+ if (!fs.existsSync(configPath)) {
73
+ if (throwIfMissing) {
74
+ throw new Error(`Configuration file not found: ${configPath}\n` +
75
+ `Run 'fractary-core:init' to create it.`);
76
+ }
77
+ return null;
78
+ }
79
+ try {
80
+ const content = fs.readFileSync(configPath, 'utf-8');
81
+ const substituted = substituteEnvVars(content, warnMissingEnvVars);
82
+ const parsed = yaml.load(substituted);
83
+ // Validate basic structure
84
+ if (!parsed || typeof parsed !== 'object') {
85
+ throw new Error('Invalid configuration: must be a YAML object');
86
+ }
87
+ if (!parsed.version) {
88
+ console.warn(`Warning: Configuration missing version field in ${configPath}`);
89
+ }
90
+ return parsed;
91
+ }
92
+ catch (error) {
93
+ if (error instanceof Error) {
94
+ throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+ /**
100
+ * Write unified configuration to `.fractary/core/config.yaml`
101
+ *
102
+ * @param config Configuration object to write
103
+ * @param projectRoot Project root directory (auto-detected if not provided)
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * writeYamlConfig({
108
+ * version: '2.0',
109
+ * work: {
110
+ * active_handler: 'github',
111
+ * handlers: { ... }
112
+ * }
113
+ * });
114
+ * ```
115
+ */
116
+ function writeYamlConfig(config, projectRoot) {
117
+ const root = projectRoot || findProjectRoot();
118
+ const fractaryDir = path.join(root, '.fractary', 'core');
119
+ const configPath = path.join(fractaryDir, 'config.yaml');
120
+ // Ensure directory exists
121
+ if (!fs.existsSync(fractaryDir)) {
122
+ fs.mkdirSync(fractaryDir, { recursive: true });
123
+ }
124
+ // Convert to YAML with proper formatting
125
+ const yamlContent = yaml.dump(config, {
126
+ indent: 2,
127
+ lineWidth: 100,
128
+ noRefs: true,
129
+ sortKeys: false,
130
+ });
131
+ fs.writeFileSync(configPath, yamlContent, 'utf-8');
132
+ }
133
+ /**
134
+ * Substitute ${ENV_VAR} placeholders with actual environment variables
135
+ *
136
+ * Supports:
137
+ * - ${VAR_NAME} - Replace with env var value
138
+ * - ${VAR_NAME:-default} - Replace with env var value or default if not set
139
+ *
140
+ * Security: Default values are limited to 1000 characters to prevent abuse.
141
+ * Variable names must match pattern: [A-Z_][A-Z0-9_]*
142
+ *
143
+ * @param content Content with environment variable placeholders
144
+ * @param warnMissing Whether to warn about missing environment variables
145
+ * @returns Content with substituted values
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const content = 'token: ${GITHUB_TOKEN}';
150
+ * const result = substituteEnvVars(content);
151
+ * // result: 'token: ghp_xxxxx'
152
+ * ```
153
+ */
154
+ function substituteEnvVars(content, warnMissing = true) {
155
+ // Input validation
156
+ if (typeof content !== 'string') {
157
+ throw new TypeError('Content must be a string');
158
+ }
159
+ // Maximum length for default values to prevent abuse
160
+ const MAX_DEFAULT_LENGTH = 1000;
161
+ return content.replace(/\$\{([A-Z_][A-Z0-9_]*)(:-([^}]+))?\}/g, (match, varName, _, defaultValue) => {
162
+ // Validate variable name format
163
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(varName)) {
164
+ console.warn(`Warning: Invalid environment variable name: ${varName}`);
165
+ return match;
166
+ }
167
+ const value = process.env[varName];
168
+ if (value !== undefined) {
169
+ return value;
170
+ }
171
+ if (defaultValue !== undefined) {
172
+ // Validate default value length
173
+ if (defaultValue.length > MAX_DEFAULT_LENGTH) {
174
+ console.warn(`Warning: Default value for ${varName} exceeds maximum length (${MAX_DEFAULT_LENGTH} chars). ` +
175
+ `Truncating to prevent abuse.`);
176
+ return defaultValue.substring(0, MAX_DEFAULT_LENGTH);
177
+ }
178
+ return defaultValue;
179
+ }
180
+ if (warnMissing) {
181
+ console.warn(`Warning: Environment variable ${varName} not set. ` +
182
+ `Using placeholder value.`);
183
+ }
184
+ // Keep original placeholder if no value found
185
+ return match;
186
+ });
187
+ }
188
+ /**
189
+ * Find project root by looking for .fractary directory or .git
190
+ *
191
+ * Walks up the directory tree from startDir until it finds:
192
+ * - A directory containing `.fractary/`
193
+ * - A directory containing `.git/`
194
+ * - The filesystem root
195
+ *
196
+ * Security: Normalizes paths and prevents traversal outside filesystem boundaries.
197
+ * Maximum of 100 directory levels to prevent infinite loops.
198
+ *
199
+ * @param startDir Directory to start searching from (default: current working directory)
200
+ * @returns Project root directory (normalized absolute path)
201
+ */
202
+ function findProjectRoot(startDir = process.cwd()) {
203
+ // Input validation and normalization
204
+ if (typeof startDir !== 'string') {
205
+ throw new TypeError('startDir must be a string');
206
+ }
207
+ // Normalize and resolve to absolute path to prevent path traversal
208
+ let currentDir = path.resolve(path.normalize(startDir));
209
+ // Get filesystem root for comparison
210
+ const fsRoot = path.parse(currentDir).root;
211
+ // Safety limit: maximum 100 directory levels to prevent infinite loops
212
+ const MAX_LEVELS = 100;
213
+ let levels = 0;
214
+ while (currentDir !== fsRoot && levels < MAX_LEVELS) {
215
+ try {
216
+ // Check for .fractary directory
217
+ if (fs.existsSync(path.join(currentDir, '.fractary'))) {
218
+ return currentDir;
219
+ }
220
+ // Check for .git directory
221
+ if (fs.existsSync(path.join(currentDir, '.git'))) {
222
+ return currentDir;
223
+ }
224
+ // Move up one directory
225
+ const parentDir = path.dirname(currentDir);
226
+ // Safety check: ensure we're actually moving up
227
+ if (parentDir === currentDir) {
228
+ // Reached filesystem root
229
+ break;
230
+ }
231
+ currentDir = parentDir;
232
+ levels++;
233
+ }
234
+ catch (error) {
235
+ // Handle permission errors or invalid paths gracefully
236
+ console.warn(`Warning: Error accessing directory ${currentDir}: ${error}`);
237
+ break;
238
+ }
239
+ }
240
+ if (levels >= MAX_LEVELS) {
241
+ console.warn(`Warning: Exceeded maximum directory depth (${MAX_LEVELS} levels) while searching for project root`);
242
+ }
243
+ // If no marker found, return the normalized starting directory
244
+ return path.resolve(path.normalize(startDir));
245
+ }
246
+ /**
247
+ * Check if a valid configuration file exists
248
+ *
249
+ * @param projectRoot Project root directory (auto-detected if not provided)
250
+ * @returns true if `.fractary/core/config.yaml` exists
251
+ */
252
+ function configExists(projectRoot) {
253
+ const root = projectRoot || findProjectRoot();
254
+ const configPath = path.join(root, '.fractary', 'core', 'config.yaml');
255
+ return fs.existsSync(configPath);
256
+ }
257
+ /**
258
+ * Get the configuration file path
259
+ *
260
+ * @param projectRoot Project root directory (auto-detected if not provided)
261
+ * @returns Full path to configuration file
262
+ */
263
+ function getConfigPath(projectRoot) {
264
+ const root = projectRoot || findProjectRoot();
265
+ return path.join(root, '.fractary', 'core', 'config.yaml');
266
+ }
267
+ /**
268
+ * Get the .fractary/core directory path
269
+ *
270
+ * @param projectRoot Project root directory (auto-detected if not provided)
271
+ * @returns Full path to .fractary/core directory
272
+ */
273
+ function getCoreDir(projectRoot) {
274
+ const root = projectRoot || findProjectRoot();
275
+ return path.join(root, '.fractary', 'core');
276
+ }
277
+ /**
278
+ * Validate that environment variables referenced in config exist
279
+ *
280
+ * @param config Configuration object to validate
281
+ * @returns Array of missing environment variable names
282
+ */
283
+ function validateEnvVars(config) {
284
+ const content = yaml.dump(config);
285
+ const missing = [];
286
+ // Find all ${VAR_NAME} references
287
+ const matches = content.matchAll(/\$\{([A-Z_][A-Z0-9_]*)(:-[^}]+)?\}/g);
288
+ for (const match of matches) {
289
+ const varName = match[1];
290
+ const hasDefault = match[2] !== undefined;
291
+ // Only check if no default value provided
292
+ if (!hasDefault && process.env[varName] === undefined) {
293
+ if (!missing.includes(varName)) {
294
+ missing.push(varName);
295
+ }
296
+ }
297
+ }
298
+ return missing;
299
+ }
300
+ //# sourceMappingURL=yaml-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml-config.js","sourceRoot":"","sources":["../../src/common/yaml-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for unified YAML configuration loader
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=yaml-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml-config.test.d.ts","sourceRoot":"","sources":["../../src/common/yaml-config.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}