fraim-framework 2.0.68 → 2.0.70

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 (37) hide show
  1. package/bin/fraim.js +1 -1
  2. package/dist/src/cli/commands/doctor.js +1 -1
  3. package/dist/src/cli/commands/init-project.js +8 -14
  4. package/dist/src/cli/commands/list.js +1 -1
  5. package/dist/src/cli/commands/setup.js +16 -30
  6. package/dist/src/cli/commands/sync.js +39 -146
  7. package/dist/src/cli/fraim.js +0 -4
  8. package/dist/src/cli/setup/first-run.js +4 -4
  9. package/dist/src/cli/setup/ide-detector.js +15 -5
  10. package/dist/src/{utils → cli/utils}/version-utils.js +5 -5
  11. package/dist/src/{utils → core/utils}/git-utils.js +2 -2
  12. package/dist/src/core/utils/object-utils.js +11 -0
  13. package/dist/src/core/utils/provider-utils.js +14 -0
  14. package/dist/src/local-mcp-server/stdio-server.js +170 -181
  15. package/index.js +1 -1
  16. package/package.json +15 -13
  17. package/dist/src/cli/commands/init.js +0 -148
  18. package/dist/src/cli/commands/mcp.js +0 -65
  19. package/dist/src/cli/commands/wizard.js +0 -35
  20. package/dist/src/fraim/db-service.js +0 -135
  21. package/dist/src/fraim/issue-tracking/ado-provider.js +0 -304
  22. package/dist/src/fraim/issue-tracking/factory.js +0 -63
  23. package/dist/src/fraim/issue-tracking/github-provider.js +0 -200
  24. package/dist/src/fraim/issue-tracking/types.js +0 -7
  25. package/dist/src/fraim/issue-tracking-config.js +0 -83
  26. package/dist/src/fraim/issues.js +0 -69
  27. package/dist/src/fraim/retrospective-learner.js +0 -301
  28. package/dist/src/fraim/setup-wizard.js +0 -99
  29. package/dist/src/fraim/template-processor.js +0 -166
  30. package/dist/src/{utils → cli/utils}/digest-utils.js +0 -0
  31. package/dist/src/{utils → cli/utils}/platform-detection.js +0 -0
  32. package/dist/src/{utils → cli/utils}/remote-sync.js +0 -0
  33. package/dist/src/{utils → cli/utils}/script-sync-utils.js +0 -0
  34. package/dist/src/{fraim → core}/config-loader.js +0 -0
  35. package/dist/src/{fraim → core}/types.js +0 -0
  36. package/dist/src/{utils → core/utils}/stub-generator.js +0 -0
  37. package/dist/src/{utils → core/utils}/workflow-parser.js +7 -7
@@ -18,13 +18,157 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.FraimLocalMCPServer = void 0;
21
+ exports.FraimLocalMCPServer = exports.FraimTemplateEngine = void 0;
22
22
  const fs_1 = require("fs");
23
23
  const path_1 = require("path");
24
24
  const os_1 = require("os");
25
25
  const child_process_1 = require("child_process");
26
26
  const crypto_1 = require("crypto");
27
27
  const axios_1 = __importDefault(require("axios"));
28
+ const provider_utils_1 = require("../core/utils/provider-utils");
29
+ const object_utils_1 = require("../core/utils/object-utils");
30
+ /**
31
+ * Handle template substitution logic separately for better testability
32
+ */
33
+ class FraimTemplateEngine {
34
+ constructor(opts) {
35
+ this.deliveryTemplatesCache = null;
36
+ this.providerTemplatesCache = {};
37
+ this.config = opts.config;
38
+ this.repoInfo = opts.repoInfo;
39
+ this.workingStyle = opts.workingStyle;
40
+ this.projectRoot = opts.projectRoot;
41
+ this.logFn = opts.logFn || (() => { });
42
+ }
43
+ substituteTemplates(content) {
44
+ let result = content;
45
+ // First, substitute config variables with fallback support
46
+ if (this.config) {
47
+ result = result.replace(/\{\{config\.([^}|]+)(?:\s*\|\s*"([^"]+)")?\}\}/g, (match, path, fallback) => {
48
+ try {
49
+ const value = (0, object_utils_1.getNestedValue)(this.config, path.trim());
50
+ if (value !== undefined) {
51
+ return typeof value === 'object'
52
+ ? JSON.stringify(value)
53
+ : String(value);
54
+ }
55
+ if (fallback !== undefined) {
56
+ return fallback;
57
+ }
58
+ return match;
59
+ }
60
+ catch (error) {
61
+ return fallback !== undefined ? fallback : match;
62
+ }
63
+ });
64
+ }
65
+ // Second, substitute {{delivery.*}} templates
66
+ const deliveryValues = this.loadDeliveryTemplates();
67
+ if (deliveryValues) {
68
+ result = result.replace(/\{\{delivery\.([^}]+)\}\}/g, (match, key) => {
69
+ const value = deliveryValues[`delivery.${key.trim()}`];
70
+ return value !== undefined ? value : match;
71
+ });
72
+ }
73
+ // Third, substitute platform-specific action templates
74
+ result = this.substitutePlatformActions(result);
75
+ return result;
76
+ }
77
+ loadDeliveryTemplates() {
78
+ if (this.deliveryTemplatesCache)
79
+ return this.deliveryTemplatesCache;
80
+ const filename = this.workingStyle === 'Conversation' ? 'delivery-conversation.json' : 'delivery-pr.json';
81
+ try {
82
+ let content = null;
83
+ if (this.projectRoot) {
84
+ const deliveryPath = (0, path_1.join)(this.projectRoot, 'registry', 'providers', filename);
85
+ if ((0, fs_1.existsSync)(deliveryPath)) {
86
+ content = (0, fs_1.readFileSync)(deliveryPath, 'utf-8');
87
+ }
88
+ }
89
+ if (!content) {
90
+ const nodeModulesPath = (0, path_1.join)(process.cwd(), 'node_modules', '@fraim', 'framework', 'registry', 'providers', filename);
91
+ if ((0, fs_1.existsSync)(nodeModulesPath)) {
92
+ content = (0, fs_1.readFileSync)(nodeModulesPath, 'utf-8');
93
+ }
94
+ }
95
+ if (content) {
96
+ this.deliveryTemplatesCache = JSON.parse(content);
97
+ return this.deliveryTemplatesCache;
98
+ }
99
+ return null;
100
+ }
101
+ catch (error) {
102
+ this.logFn(`⚠️ Failed to load delivery templates: ${error.message}`);
103
+ return null;
104
+ }
105
+ }
106
+ substitutePlatformActions(content) {
107
+ const provider = (0, provider_utils_1.detectProvider)(this.repoInfo?.url);
108
+ const templates = this.loadProviderTemplates(provider);
109
+ if (!templates)
110
+ return content;
111
+ let result = content;
112
+ for (const [action, template] of Object.entries(templates)) {
113
+ const regex = new RegExp(`\\{\\{${action}\\}\\}`, 'g');
114
+ const renderedTemplate = this.renderActionTemplate(template);
115
+ result = result.replace(regex, renderedTemplate);
116
+ }
117
+ return result;
118
+ }
119
+ loadProviderTemplates(provider) {
120
+ if (this.providerTemplatesCache[provider])
121
+ return this.providerTemplatesCache[provider];
122
+ try {
123
+ let content = null;
124
+ if (this.projectRoot) {
125
+ const providerPath = (0, path_1.join)(this.projectRoot, 'registry', 'providers', `${provider}.json`);
126
+ if ((0, fs_1.existsSync)(providerPath)) {
127
+ content = (0, fs_1.readFileSync)(providerPath, 'utf-8');
128
+ }
129
+ }
130
+ if (!content) {
131
+ const nodeModulesPath = (0, path_1.join)(process.cwd(), 'node_modules', '@fraim', 'framework', 'registry', 'providers', `${provider}.json`);
132
+ if ((0, fs_1.existsSync)(nodeModulesPath)) {
133
+ content = (0, fs_1.readFileSync)(nodeModulesPath, 'utf-8');
134
+ }
135
+ }
136
+ if (content) {
137
+ const templates = JSON.parse(content);
138
+ this.providerTemplatesCache[provider] = templates;
139
+ return templates;
140
+ }
141
+ return null;
142
+ }
143
+ catch (error) {
144
+ this.logFn(`⚠️ Failed to load provider templates: ${error.message}`);
145
+ return null;
146
+ }
147
+ }
148
+ renderActionTemplate(template) {
149
+ if (!this.repoInfo && !this.config?.repository) {
150
+ return template;
151
+ }
152
+ return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
153
+ const trimmedPath = path.trim();
154
+ if (trimmedPath.startsWith('repository.')) {
155
+ const repoPath = trimmedPath.substring('repository.'.length);
156
+ if (this.repoInfo) {
157
+ const value = (0, object_utils_1.getNestedValue)(this.repoInfo, repoPath);
158
+ if (value !== undefined)
159
+ return String(value);
160
+ }
161
+ if (this.config?.repository) {
162
+ const value = (0, object_utils_1.getNestedValue)(this.config.repository, repoPath);
163
+ if (value !== undefined)
164
+ return String(value);
165
+ }
166
+ }
167
+ return match;
168
+ });
169
+ }
170
+ }
171
+ exports.FraimTemplateEngine = FraimTemplateEngine;
28
172
  class FraimLocalMCPServer {
29
173
  constructor() {
30
174
  this.config = null;
@@ -33,6 +177,7 @@ class FraimLocalMCPServer {
33
177
  this.pendingRootsRequest = false;
34
178
  this.machineInfo = null;
35
179
  this.repoInfo = null;
180
+ this.engine = null;
36
181
  this.remoteUrl = process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
37
182
  this.apiKey = process.env.FRAIM_API_KEY || '';
38
183
  this.localVersion = this.detectLocalVersion();
@@ -40,7 +185,7 @@ class FraimLocalMCPServer {
40
185
  this.logError('❌ FRAIM_API_KEY environment variable is required');
41
186
  process.exit(1);
42
187
  }
43
- this.log('🚀 FRAIM Local MCP Server starting...');
188
+ this.log('🚀 FRAIM Local MCP Server starting... [DEBUG-PROXY-V3]');
44
189
  this.log(`📡 Remote server: ${this.remoteUrl}`);
45
190
  this.log(`🔑 API key: ${this.apiKey.substring(0, 10)}...`);
46
191
  this.log(`Local MCP version: ${this.localVersion}`);
@@ -307,183 +452,17 @@ class FraimLocalMCPServer {
307
452
  }
308
453
  return 'PR';
309
454
  }
310
- /**
311
- * Substitute template variables in content
312
- * Supports:
313
- * 1. {{config.path.to.value}} - Config substitution
314
- * 2. {{config.path.to.value | "fallback instruction"}} - With fallback
315
- * 3. {{delivery.*}} - Working-style-specific delivery templates (from registry/providers/delivery-*.json)
316
- * 4. Platform-specific action templates (GitHub vs ADO, from registry/providers/*.json)
317
- */
318
455
  substituteTemplates(content) {
319
- let result = content;
320
- // First, substitute config variables with fallback support (only if config exists)
321
- if (this.config) {
322
- result = result.replace(/\{\{config\.([^}|]+)(?:\s*\|\s*"([^"]+)")?\}\}/g, (match, path, fallback) => {
323
- try {
324
- const value = this.getNestedValue(this.config, path.trim());
325
- if (value !== undefined) {
326
- // Config value exists - substitute it
327
- return typeof value === 'object'
328
- ? JSON.stringify(value)
329
- : String(value);
330
- }
331
- if (fallback !== undefined) {
332
- // Config value missing - use fallback instruction
333
- return fallback;
334
- }
335
- // No fallback provided - keep placeholder
336
- return match;
337
- }
338
- catch (error) {
339
- // On error, use fallback if provided, otherwise keep placeholder
340
- return fallback !== undefined ? fallback : match;
341
- }
342
- });
343
- }
344
- // Second, substitute {{delivery.*}} templates based on workingStyle
345
- // Loaded from registry/providers/delivery-{mode}.json (same pattern as platform templates)
346
- const deliveryValues = this.loadDeliveryTemplates();
347
- if (deliveryValues) {
348
- result = result.replace(/\{\{delivery\.([^}]+)\}\}/g, (match, key) => {
349
- const value = deliveryValues[`delivery.${key.trim()}`];
350
- return value !== undefined ? value : match;
456
+ if (!this.engine) {
457
+ this.engine = new FraimTemplateEngine({
458
+ config: this.config,
459
+ repoInfo: this.detectRepoInfo(),
460
+ workingStyle: this.getWorkingStyle(),
461
+ projectRoot: this.findProjectRoot(),
462
+ logFn: (msg) => this.log(msg)
351
463
  });
352
464
  }
353
- // Third, substitute platform-specific action templates
354
- // This works independently of config - only needs repo info
355
- result = this.substitutePlatformActions(result);
356
- return result;
357
- }
358
- /**
359
- * Load delivery templates from registry based on workingStyle.
360
- * Follows the same pattern as loadProviderTemplates (registry/providers/*.json).
361
- */
362
- loadDeliveryTemplates() {
363
- const workingStyle = this.getWorkingStyle();
364
- const filename = workingStyle === 'Conversation' ? 'delivery-conversation.json' : 'delivery-pr.json';
365
- try {
366
- const projectRoot = this.findProjectRoot();
367
- if (projectRoot) {
368
- const deliveryPath = (0, path_1.join)(projectRoot, 'registry', 'providers', filename);
369
- if ((0, fs_1.existsSync)(deliveryPath)) {
370
- return JSON.parse((0, fs_1.readFileSync)(deliveryPath, 'utf-8'));
371
- }
372
- }
373
- // Fallback: Try node_modules/@fraim/framework
374
- const nodeModulesPath = (0, path_1.join)(process.cwd(), 'node_modules', '@fraim', 'framework', 'registry', 'providers', filename);
375
- if ((0, fs_1.existsSync)(nodeModulesPath)) {
376
- return JSON.parse((0, fs_1.readFileSync)(nodeModulesPath, 'utf-8'));
377
- }
378
- this.log(`⚠️ Could not find delivery templates: ${filename}`);
379
- return null;
380
- }
381
- catch (error) {
382
- this.log(`⚠️ Failed to load delivery templates: ${error.message}`);
383
- return null;
384
- }
385
- }
386
- /**
387
- * Substitute platform-specific action templates
388
- * Replaces {{action}} with provider-specific MCP tool calls
389
- */
390
- substitutePlatformActions(content) {
391
- // Detect provider from repo info
392
- const provider = this.detectProvider();
393
- // Load provider templates
394
- const templates = this.loadProviderTemplates(provider);
395
- if (!templates) {
396
- return content; // No templates available, return unchanged
397
- }
398
- let result = content;
399
- // Replace {{action}} with provider-specific implementations
400
- for (const [action, template] of Object.entries(templates)) {
401
- const regex = new RegExp(`\\{\\{${action}\\}\\}`, 'g');
402
- // Substitute repository variables in the template
403
- const renderedTemplate = this.renderActionTemplate(template, provider);
404
- result = result.replace(regex, renderedTemplate);
405
- }
406
- return result;
407
- }
408
- /**
409
- * Detect provider from repository info
410
- */
411
- detectProvider() {
412
- if (!this.repoInfo) {
413
- return 'github'; // Default
414
- }
415
- const url = this.repoInfo.url || '';
416
- if (url.includes('dev.azure.com') || url.includes('visualstudio.com')) {
417
- return 'ado';
418
- }
419
- // Check config for explicit provider
420
- if (this.config?.repository?.provider) {
421
- return this.config.repository.provider;
422
- }
423
- return 'github'; // Default
424
- }
425
- /**
426
- * Load provider templates from registry
427
- */
428
- loadProviderTemplates(provider) {
429
- try {
430
- // Try to load from project root first
431
- const projectRoot = this.findProjectRoot();
432
- if (projectRoot) {
433
- const providerPath = (0, path_1.join)(projectRoot, 'registry', 'providers', `${provider}.json`);
434
- if ((0, fs_1.existsSync)(providerPath)) {
435
- return JSON.parse((0, fs_1.readFileSync)(providerPath, 'utf-8'));
436
- }
437
- }
438
- // Fallback: Try to load from node_modules/@fraim/framework
439
- const nodeModulesPath = (0, path_1.join)(process.cwd(), 'node_modules', '@fraim', 'framework', 'registry', 'providers', `${provider}.json`);
440
- if ((0, fs_1.existsSync)(nodeModulesPath)) {
441
- return JSON.parse((0, fs_1.readFileSync)(nodeModulesPath, 'utf-8'));
442
- }
443
- this.log(`⚠️ Could not find provider templates for: ${provider}`);
444
- return null;
445
- }
446
- catch (error) {
447
- this.log(`⚠️ Failed to load provider templates: ${error.message}`);
448
- return null;
449
- }
450
- }
451
- /**
452
- * Render action template with repository variables
453
- */
454
- renderActionTemplate(template, provider) {
455
- if (!this.repoInfo && !this.config?.repository) {
456
- return template; // No repo info available
457
- }
458
- return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
459
- const trimmedPath = path.trim();
460
- // Handle repository.* variables
461
- if (trimmedPath.startsWith('repository.')) {
462
- const repoPath = trimmedPath.substring('repository.'.length);
463
- // Try repoInfo first (from git detection)
464
- if (this.repoInfo) {
465
- const value = this.getNestedValue(this.repoInfo, repoPath);
466
- if (value !== undefined)
467
- return String(value);
468
- }
469
- // Fallback to config
470
- if (this.config?.repository) {
471
- const value = this.getNestedValue(this.config.repository, repoPath);
472
- if (value !== undefined)
473
- return String(value);
474
- }
475
- }
476
- // Keep original placeholder if not found
477
- return match;
478
- });
479
- }
480
- /**
481
- * Get nested value from object using dot notation
482
- */
483
- getNestedValue(obj, path) {
484
- return path.split('.').reduce((current, key) => {
485
- return current && current[key] !== undefined ? current[key] : undefined;
486
- }, obj);
465
+ return this.engine.substituteTemplates(content);
487
466
  }
488
467
  /**
489
468
  * Process template substitution in MCP response
@@ -595,14 +574,15 @@ class FraimLocalMCPServer {
595
574
  }
596
575
  return {
597
576
  jsonrpc: '2.0',
598
- id: request.id,
577
+ id: request.id || null,
599
578
  error: {
600
- code: -32603,
601
- message: `Remote server error: ${error.message}`,
579
+ code: status === 401 ? -32001 : -32603,
580
+ message: `Remote server error (${status || 'unknown status'}): ${error.message}`,
602
581
  data: {
603
582
  fraimRequestId: requestId,
604
583
  remoteStatus: status ?? null,
605
- localMcpVersion: this.localVersion
584
+ localMcpVersion: this.localVersion,
585
+ remoteError: remoteData
606
586
  }
607
587
  }
608
588
  };
@@ -741,6 +721,14 @@ class FraimLocalMCPServer {
741
721
  }
742
722
  else {
743
723
  this.logError(`Unknown message type: ${JSON.stringify(message)}`);
724
+ process.stdout.write(JSON.stringify({
725
+ jsonrpc: '2.0',
726
+ error: {
727
+ code: -32600,
728
+ message: 'Invalid Request: Unknown message type'
729
+ },
730
+ id: message.id || null
731
+ }) + '\n');
744
732
  }
745
733
  }
746
734
  catch (error) {
@@ -751,7 +739,8 @@ class FraimLocalMCPServer {
751
739
  error: {
752
740
  code: -32700,
753
741
  message: `Parse error: ${error.message}`
754
- }
742
+ },
743
+ id: null
755
744
  };
756
745
  process.stdout.write(JSON.stringify(errorResponse) + '\n');
757
746
  }
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * FRAIM Framework - Smart Entry Point
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.68",
3
+ "version": "2.0.70",
4
4
  "description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "scripts": {
11
11
  "dev": "tsx --watch src/fraim-mcp-server.ts > server.log 2>&1",
12
12
  "dev:prod": "npm run build && node dist/src/fraim-mcp-server.js > server.log 2>&1",
13
- "build": "tsc && node scripts/copy-ai-manager-rules.js && npm run build:stubs && npm run validate:registry",
13
+ "build": "tsc && npm run build:stubs && node scripts/copy-ai-manager-rules.js && npm run validate:registry && tsx scripts/validate-purity.ts",
14
14
  "build:stubs": "tsx scripts/build-stub-registry.ts",
15
15
  "test": "node scripts/test-with-server.js",
16
16
  "start:fraim": "tsx src/fraim-mcp-server.ts",
@@ -20,11 +20,11 @@
20
20
  "manage-keys": "tsx scripts/fraim/manage-keys.ts",
21
21
  "view-signups": "tsx scripts/view-signups.ts",
22
22
  "fraim:init": "npm run build && node index.js init",
23
- "fraim:sync": "node index.js sync",
23
+ "fraim:sync": "node index.js sync --local",
24
24
  "postinstall": "fraim sync --skip-updates || echo 'FRAIM setup skipped.'",
25
25
  "prepublishOnly": "npm run build",
26
26
  "release": "npm version patch && npm publish",
27
- "test-smoke-ci": "tsx --test tests/test-genericization.ts tests/test-cli.ts tests/test-stub-registry.ts tests/test-sync-stubs.ts",
27
+ "test-smoke-ci": "tsx --test tests/test-genericization.ts tests/test-cli.ts tests/test-stub-registry.ts",
28
28
  "test-all-ci": "tsx --test tests/test-*.ts",
29
29
  "validate:registry": "tsx scripts/verify-registry-paths.ts && npm run validate:workflows && npm run validate:platform-agnostic",
30
30
  "validate:workflows": "tsx scripts/validate-workflows.ts",
@@ -66,15 +66,24 @@
66
66
  "@types/express": "^5.0.6",
67
67
  "@types/node": "^20.0.0",
68
68
  "@types/prompts": "^2.4.9",
69
+ "cors": "^2.8.5",
70
+ "express": "^5.2.1",
69
71
  "fast-glob": "^3.3.3",
72
+ "html-to-docx": "^1.8.0",
73
+ "markdown-it": "^14.1.0",
74
+ "markdown-it-highlightjs": "^4.2.0",
75
+ "mongodb": "^7.0.0",
76
+ "pptxgenjs": "^4.0.1",
77
+ "puppeteer": "^24.36.1",
78
+ "qrcode": "^1.5.4",
79
+ "sharp": "^0.34.5",
70
80
  "tsx": "^4.0.0",
71
81
  "typescript": "^5.0.0"
72
82
  },
73
83
  "files": [
74
84
  "dist/src/local-mcp-server/",
75
85
  "dist/src/cli/",
76
- "dist/src/fraim/",
77
- "dist/src/utils/",
86
+ "dist/src/core/",
78
87
  "bin/fraim.js",
79
88
  "bin/fraim-mcp.js",
80
89
  "index.js",
@@ -90,15 +99,8 @@
90
99
  "axios": "^1.7.0",
91
100
  "chalk": "4.1.2",
92
101
  "commander": "^14.0.2",
93
- "cors": "^2.8.5",
94
102
  "dotenv": "^16.4.7",
95
- "express": "^5.2.1",
96
- "markdown-it": "^14.1.0",
97
- "markdown-it-highlightjs": "^4.2.0",
98
- "mongodb": "^7.0.0",
99
103
  "prompts": "^2.4.2",
100
- "puppeteer": "^24.36.1",
101
- "sharp": "^0.34.5",
102
104
  "tree-kill": "^1.2.2"
103
105
  }
104
106
  }
@@ -1,148 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initCommand = exports.runInit = void 0;
7
- const commander_1 = require("commander");
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const first_run_1 = require("../setup/first-run");
12
- const sync_1 = require("./sync");
13
- const platform_detection_1 = require("../../utils/platform-detection");
14
- const version_utils_1 = require("../../utils/version-utils");
15
- const script_sync_utils_1 = require("../../utils/script-sync-utils");
16
- const runInit = async (options = {}) => {
17
- const projectRoot = process.cwd();
18
- const fraimDir = path_1.default.join(projectRoot, '.fraim');
19
- const configPath = path_1.default.join(fraimDir, 'config.json');
20
- console.log(chalk_1.default.blue('🚀 Initializing FRAIM...'));
21
- if (!fs_1.default.existsSync(fraimDir)) {
22
- fs_1.default.mkdirSync(fraimDir, { recursive: true });
23
- console.log(chalk_1.default.green('✅ Created .fraim directory'));
24
- }
25
- else {
26
- console.log(chalk_1.default.yellow('ℹ️ .fraim directory already exists'));
27
- }
28
- if (!fs_1.default.existsSync(configPath)) {
29
- let config;
30
- // Try to detect platform from git remote
31
- const detection = (0, platform_detection_1.detectPlatformFromGit)();
32
- if (options.skipPlatform || detection.provider === 'unknown' || !detection.repository) {
33
- // Conversational mode - no platform integration
34
- if (!options.skipPlatform && (detection.provider === 'unknown' || !detection.repository)) {
35
- console.log(chalk_1.default.yellow('\nℹ️ No git remote found or unsupported platform.'));
36
- console.log(chalk_1.default.blue(' Initializing in conversational mode (no platform integration).'));
37
- console.log(chalk_1.default.gray(' You can still use FRAIM workflows and AI features.'));
38
- console.log(chalk_1.default.gray(' Platform features (issues, PRs) will be unavailable.\n'));
39
- }
40
- else {
41
- console.log(chalk_1.default.blue('\n Initializing in conversational mode (platform integration skipped).\n'));
42
- }
43
- // Get project name from directory or git
44
- let projectName = path_1.default.basename(projectRoot);
45
- try {
46
- const { execSync } = require('child_process');
47
- const gitName = execSync('git config --get remote.origin.url', {
48
- stdio: 'pipe',
49
- timeout: 2000
50
- }).toString().trim();
51
- const match = gitName.match(/\/([^\/]+?)(\.git)?$/);
52
- if (match) {
53
- projectName = match[1];
54
- }
55
- }
56
- catch (e) {
57
- // Use directory name as fallback
58
- }
59
- config = {
60
- version: (0, version_utils_1.getFraimVersion)(),
61
- project: {
62
- name: projectName
63
- },
64
- customizations: {
65
- workflowsPath: '.fraim/workflows'
66
- }
67
- };
68
- console.log(chalk_1.default.gray(` Project: ${projectName}`));
69
- console.log(chalk_1.default.gray(` Mode: Conversational (no platform integration)`));
70
- }
71
- else {
72
- // Integrated mode - use platform
73
- if (!detection.repository) {
74
- console.log(chalk_1.default.red('❌ Error: Repository information not available'));
75
- process.exit(1);
76
- }
77
- // Validate the detected repository config
78
- const validation = (0, platform_detection_1.validateRepositoryConfig)(detection.repository);
79
- if (!validation.valid) {
80
- console.log(chalk_1.default.red('❌ Error: Invalid repository configuration:'));
81
- validation.errors.forEach(err => console.log(chalk_1.default.red(` - ${err}`)));
82
- process.exit(1);
83
- }
84
- console.log(chalk_1.default.blue('\n Initializing in integrated mode (platform integration enabled).'));
85
- console.log(chalk_1.default.gray(` Platform: ${detection.provider.toUpperCase()}`));
86
- if (detection.provider === 'github') {
87
- console.log(chalk_1.default.gray(` Repository: ${detection.repository.owner}/${detection.repository.name}`));
88
- }
89
- else if (detection.provider === 'ado') {
90
- console.log(chalk_1.default.gray(` Organization: ${detection.repository.organization}`));
91
- console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
92
- console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
93
- }
94
- console.log();
95
- config = {
96
- version: (0, version_utils_1.getFraimVersion)(),
97
- project: {
98
- name: detection.repository.name
99
- },
100
- repository: detection.repository,
101
- customizations: {
102
- workflowsPath: '.fraim/workflows'
103
- }
104
- };
105
- }
106
- fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
107
- console.log(chalk_1.default.green('✅ Created .fraim/config.json'));
108
- }
109
- // Create subdirectories
110
- ['workflows'].forEach(dir => {
111
- const dirPath = path_1.default.join(fraimDir, dir);
112
- if (!fs_1.default.existsSync(dirPath)) {
113
- fs_1.default.mkdirSync(dirPath, { recursive: true });
114
- console.log(chalk_1.default.green(`✅ Created .fraim/${dir}`));
115
- }
116
- });
117
- console.log(chalk_1.default.blue('\n🎉 FRAIM initialized successfully!'));
118
- // Sync workflows from registry
119
- await (0, sync_1.runSync)({});
120
- // Sync scripts to user directory
121
- console.log(chalk_1.default.blue('🔄 Syncing FRAIM scripts to user directory...'));
122
- // Find registry path (same logic as sync command)
123
- let registryPath = path_1.default.join(__dirname, '../../../../registry');
124
- if (!fs_1.default.existsSync(registryPath)) {
125
- registryPath = path_1.default.join(__dirname, '../../../registry');
126
- }
127
- if (!fs_1.default.existsSync(registryPath)) {
128
- registryPath = path_1.default.join(projectRoot, 'registry');
129
- }
130
- if (fs_1.default.existsSync(registryPath)) {
131
- const syncResult = (0, script_sync_utils_1.syncScriptsToUserDirectory)(registryPath);
132
- console.log(chalk_1.default.green(`✅ Synced ${syncResult.synced} self-contained scripts to user directory.`));
133
- if (syncResult.ephemeral > 0) {
134
- console.log(chalk_1.default.gray(` ${syncResult.ephemeral} dependent scripts will use ephemeral execution.`));
135
- }
136
- }
137
- else {
138
- console.log(chalk_1.default.yellow('⚠️ Registry not found, skipping script sync.'));
139
- }
140
- // Trigger First Run Experience
141
- await (0, first_run_1.runFirstRunExperience)();
142
- process.exit(0);
143
- };
144
- exports.runInit = runInit;
145
- exports.initCommand = new commander_1.Command('init')
146
- .description('Initialize FRAIM in the current project')
147
- .option('--skip-platform', 'Skip platform integration (conversational mode only)')
148
- .action((options) => (0, exports.runInit)(options));