@fractary/faber 2.4.0 → 2.4.2

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,232 @@
1
+ /**
2
+ * @fractary/faber - Workflow Registry
3
+ *
4
+ * Manages workflow discovery and loading from the workflows manifest.
5
+ * Provides a unified interface for finding and loading workflow definitions.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as yaml from 'js-yaml';
10
+ import { z } from 'zod';
11
+ import { FABER_DEFAULTS } from '../defaults.js';
12
+ import { findProjectRoot } from '../config.js';
13
+ // ============================================================================
14
+ // Schema Validation
15
+ // ============================================================================
16
+ const WorkflowEntrySchema = z.object({
17
+ id: z
18
+ .string()
19
+ .min(1)
20
+ .max(64)
21
+ .regex(/^[a-z][a-z0-9-]*$/, 'Workflow ID must be lowercase alphanumeric with hyphens'),
22
+ file: z
23
+ .string()
24
+ .min(1)
25
+ .max(255)
26
+ .regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*\.(yaml|yml|json)$/, 'File must be a .yaml, .yml, or .json file'),
27
+ description: z.string().max(500).optional(),
28
+ });
29
+ const WorkflowRegistrySchema = z.object({
30
+ workflows: z.array(WorkflowEntrySchema).min(1),
31
+ });
32
+ // ============================================================================
33
+ // Registry Errors
34
+ // ============================================================================
35
+ export class WorkflowRegistryError extends Error {
36
+ constructor(message) {
37
+ super(message);
38
+ this.name = 'WorkflowRegistryError';
39
+ }
40
+ }
41
+ export class RegistryWorkflowNotFoundError extends WorkflowRegistryError {
42
+ workflowId;
43
+ availableWorkflows;
44
+ constructor(workflowId, availableWorkflows) {
45
+ super(`Workflow '${workflowId}' not found. Available workflows: ${availableWorkflows.join(', ') || 'none'}`);
46
+ this.workflowId = workflowId;
47
+ this.availableWorkflows = availableWorkflows;
48
+ this.name = 'RegistryWorkflowNotFoundError';
49
+ }
50
+ }
51
+ /**
52
+ * Load the workflow registry from the manifest file or auto-discover workflows.
53
+ *
54
+ * @param options Loading options
55
+ * @returns Workflow registry
56
+ * @throws WorkflowRegistryError if registry cannot be loaded
57
+ */
58
+ export function loadWorkflowRegistry(options = {}) {
59
+ const projectRoot = options.projectRoot || findProjectRoot();
60
+ const workflowsPath = options.config?.workflows?.path || FABER_DEFAULTS.paths.workflows;
61
+ const workflowsDir = path.isAbsolute(workflowsPath)
62
+ ? workflowsPath
63
+ : path.join(projectRoot, workflowsPath);
64
+ const manifestPath = path.join(workflowsDir, FABER_DEFAULTS.manifestFilename);
65
+ // Try to load manifest file
66
+ if (fs.existsSync(manifestPath)) {
67
+ return loadManifestFile(manifestPath);
68
+ }
69
+ // Auto-discover if enabled
70
+ if (options.autoDiscover !== false) {
71
+ return autoDiscoverWorkflows(workflowsDir);
72
+ }
73
+ // Return empty registry
74
+ return { workflows: [] };
75
+ }
76
+ /**
77
+ * Load registry from manifest file (workflows.yaml)
78
+ */
79
+ function loadManifestFile(manifestPath) {
80
+ try {
81
+ const content = fs.readFileSync(manifestPath, 'utf-8');
82
+ const data = yaml.load(content);
83
+ const result = WorkflowRegistrySchema.safeParse(data);
84
+ if (!result.success) {
85
+ const errors = result.error.errors
86
+ .map((e) => `${e.path.join('.')}: ${e.message}`)
87
+ .join(', ');
88
+ throw new WorkflowRegistryError(`Invalid workflow manifest at ${manifestPath}: ${errors}`);
89
+ }
90
+ return result.data;
91
+ }
92
+ catch (error) {
93
+ if (error instanceof WorkflowRegistryError)
94
+ throw error;
95
+ throw new WorkflowRegistryError(`Failed to load workflow manifest: ${error instanceof Error ? error.message : String(error)}`);
96
+ }
97
+ }
98
+ /**
99
+ * Auto-discover workflows by scanning the workflows directory
100
+ */
101
+ function autoDiscoverWorkflows(workflowsDir) {
102
+ if (!fs.existsSync(workflowsDir)) {
103
+ return { workflows: [] };
104
+ }
105
+ const files = fs.readdirSync(workflowsDir);
106
+ const workflows = [];
107
+ for (const file of files) {
108
+ // Skip manifest file and non-workflow files
109
+ if (file === FABER_DEFAULTS.manifestFilename)
110
+ continue;
111
+ if (!/\.(yaml|yml|json)$/.test(file))
112
+ continue;
113
+ // Skip files starting with underscore (templates, partials)
114
+ if (file.startsWith('_'))
115
+ continue;
116
+ // Extract ID from filename
117
+ const id = file.replace(/\.(yaml|yml|json)$/, '');
118
+ // Validate ID format
119
+ if (!/^[a-z][a-z0-9-]*$/.test(id)) {
120
+ console.warn(`Skipping workflow file ${file}: invalid ID format`);
121
+ continue;
122
+ }
123
+ workflows.push({
124
+ id,
125
+ file,
126
+ });
127
+ }
128
+ // Sort by ID for consistent ordering
129
+ workflows.sort((a, b) => a.id.localeCompare(b.id));
130
+ return { workflows };
131
+ }
132
+ /**
133
+ * Get a specific workflow entry by ID
134
+ *
135
+ * @param options Options including workflowId
136
+ * @returns Workflow entry
137
+ * @throws WorkflowNotFoundError if workflow doesn't exist
138
+ */
139
+ export function getWorkflow(options) {
140
+ const registry = loadWorkflowRegistry(options);
141
+ const workflow = registry.workflows.find((w) => w.id === options.workflowId);
142
+ if (!workflow) {
143
+ throw new RegistryWorkflowNotFoundError(options.workflowId, registry.workflows.map((w) => w.id));
144
+ }
145
+ return workflow;
146
+ }
147
+ /**
148
+ * Get the file path for a workflow
149
+ *
150
+ * @param options Options including workflowId
151
+ * @returns Absolute path to the workflow file
152
+ * @throws WorkflowNotFoundError if workflow doesn't exist
153
+ */
154
+ export function getWorkflowPath(options) {
155
+ const projectRoot = options.projectRoot || findProjectRoot();
156
+ const workflowsPath = options.config?.workflows?.path || FABER_DEFAULTS.paths.workflows;
157
+ const workflowsDir = path.isAbsolute(workflowsPath)
158
+ ? workflowsPath
159
+ : path.join(projectRoot, workflowsPath);
160
+ const workflow = getWorkflow(options);
161
+ return path.join(workflowsDir, workflow.file);
162
+ }
163
+ /**
164
+ * List all available workflows
165
+ *
166
+ * @param options Loading options
167
+ * @returns Array of workflow entries
168
+ */
169
+ export function listWorkflows(options = {}) {
170
+ const registry = loadWorkflowRegistry(options);
171
+ return registry.workflows;
172
+ }
173
+ /**
174
+ * Check if a workflow exists
175
+ *
176
+ * @param workflowId Workflow ID to check
177
+ * @param options Loading options
178
+ * @returns true if workflow exists
179
+ */
180
+ export function workflowExists(workflowId, options = {}) {
181
+ const registry = loadWorkflowRegistry(options);
182
+ return registry.workflows.some((w) => w.id === workflowId);
183
+ }
184
+ /**
185
+ * Get the default workflow ID from config or fallback
186
+ *
187
+ * @param options Loading options
188
+ * @returns Default workflow ID
189
+ */
190
+ export function getDefaultWorkflowId(options = {}) {
191
+ return options.config?.workflows?.default || FABER_DEFAULTS.workflow.defaultWorkflow;
192
+ }
193
+ /**
194
+ * Create a new workflow manifest file
195
+ *
196
+ * @param options Creation options
197
+ * @returns Path to created manifest
198
+ */
199
+ export function createWorkflowManifest(options = {}) {
200
+ const projectRoot = options.projectRoot || findProjectRoot();
201
+ const workflowsPath = options.workflowsPath || FABER_DEFAULTS.paths.workflows;
202
+ const workflowsDir = path.isAbsolute(workflowsPath)
203
+ ? workflowsPath
204
+ : path.join(projectRoot, workflowsPath);
205
+ // Ensure directory exists
206
+ fs.mkdirSync(workflowsDir, { recursive: true });
207
+ const manifestPath = path.join(workflowsDir, FABER_DEFAULTS.manifestFilename);
208
+ // Use provided workflows or create default entry
209
+ const workflows = options.workflows || [
210
+ {
211
+ id: 'default',
212
+ file: 'default.yaml',
213
+ description: 'Default FABER workflow for software development',
214
+ },
215
+ ];
216
+ const registry = { workflows };
217
+ const content = yaml.dump(registry, {
218
+ indent: 2,
219
+ lineWidth: 100,
220
+ noRefs: true,
221
+ sortKeys: false,
222
+ });
223
+ // Add header comment
224
+ const fileContent = `# Workflow Registry - Lists available FABER workflows
225
+ # Each workflow is defined in a separate file in this directory
226
+ # Schema: https://fractary.dev/schemas/workflow-registry.schema.json
227
+
228
+ ${content}`;
229
+ fs.writeFileSync(manifestPath, fileContent, 'utf-8');
230
+ return manifestPath;
231
+ }
232
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/workflows/registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fractary/faber",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "FABER SDK - Development toolkit for AI-assisted workflows",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",