fraim-framework 2.0.76 ā 2.0.77
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.
|
@@ -24,42 +24,38 @@ const checkGlobalSetup = () => {
|
|
|
24
24
|
const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
|
|
25
25
|
return {
|
|
26
26
|
exists: true,
|
|
27
|
-
mode: config.mode || 'integrated',
|
|
27
|
+
mode: config.mode || 'integrated',
|
|
28
28
|
tokens: config.tokens || {}
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
catch
|
|
31
|
+
catch {
|
|
32
32
|
return { exists: true, mode: 'integrated', tokens: {} };
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
const runInitProject = async () => {
|
|
36
|
-
console.log(chalk_1.default.blue('
|
|
37
|
-
// Check if global setup exists
|
|
36
|
+
console.log(chalk_1.default.blue('Initializing FRAIM project...'));
|
|
38
37
|
const globalSetup = checkGlobalSetup();
|
|
39
38
|
if (!globalSetup.exists) {
|
|
40
|
-
console.log(chalk_1.default.red('
|
|
39
|
+
console.log(chalk_1.default.red('Global FRAIM setup not found.'));
|
|
41
40
|
console.log(chalk_1.default.yellow('Please run global setup first:'));
|
|
42
41
|
console.log(chalk_1.default.cyan(' fraim setup'));
|
|
43
42
|
process.exit(1);
|
|
44
43
|
}
|
|
45
|
-
const mode = globalSetup.mode;
|
|
46
|
-
const tokens = globalSetup.tokens || {};
|
|
47
|
-
console.log(chalk_1.default.gray(` Mode: ${mode === 'conversational' ? 'Conversational' : 'Integrated'} (from global config)`));
|
|
48
44
|
const projectRoot = process.cwd();
|
|
49
45
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
50
46
|
const configPath = path_1.default.join(fraimDir, 'config.json');
|
|
51
47
|
if (!fs_1.default.existsSync(fraimDir)) {
|
|
52
48
|
fs_1.default.mkdirSync(fraimDir, { recursive: true });
|
|
53
|
-
console.log(chalk_1.default.green('
|
|
49
|
+
console.log(chalk_1.default.green('Created .fraim directory'));
|
|
54
50
|
}
|
|
55
51
|
else {
|
|
56
|
-
console.log(chalk_1.default.yellow('
|
|
52
|
+
console.log(chalk_1.default.yellow('.fraim directory already exists'));
|
|
57
53
|
}
|
|
58
54
|
if (!fs_1.default.existsSync(configPath)) {
|
|
55
|
+
const projectName = path_1.default.basename(projectRoot);
|
|
56
|
+
const preferredMode = globalSetup.mode || 'integrated';
|
|
59
57
|
let config;
|
|
60
|
-
if (
|
|
61
|
-
// Conversational mode - no platform integration
|
|
62
|
-
const projectName = path_1.default.basename(projectRoot);
|
|
58
|
+
if (preferredMode === 'conversational') {
|
|
63
59
|
config = {
|
|
64
60
|
version: (0, version_utils_1.getFraimVersion)(),
|
|
65
61
|
project: {
|
|
@@ -67,77 +63,72 @@ const runInitProject = async () => {
|
|
|
67
63
|
},
|
|
68
64
|
customizations: {}
|
|
69
65
|
};
|
|
70
|
-
console.log(chalk_1.default.blue('
|
|
66
|
+
console.log(chalk_1.default.blue(' conversational mode: no platform integration'));
|
|
71
67
|
console.log(chalk_1.default.gray(` Project: ${projectName}`));
|
|
72
68
|
}
|
|
73
69
|
else {
|
|
74
|
-
// Integrated mode - try to detect platform
|
|
75
70
|
const detection = (0, platform_detection_1.detectPlatformFromGit)();
|
|
76
71
|
if (detection.provider !== 'unknown' && detection.repository) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
const issueTracking = detection.provider === 'github'
|
|
73
|
+
? {
|
|
74
|
+
provider: 'github',
|
|
75
|
+
owner: detection.repository.owner,
|
|
76
|
+
name: detection.repository.name
|
|
77
|
+
}
|
|
78
|
+
: {
|
|
79
|
+
provider: 'ado',
|
|
80
|
+
organization: detection.repository.organization,
|
|
81
|
+
project: detection.repository.project,
|
|
82
|
+
name: detection.repository.name
|
|
83
|
+
};
|
|
85
84
|
config = {
|
|
86
85
|
version: (0, version_utils_1.getFraimVersion)(),
|
|
87
86
|
project: {
|
|
88
|
-
name: detection.repository.name
|
|
87
|
+
name: detection.repository.name || projectName
|
|
89
88
|
},
|
|
90
89
|
repository: detection.repository,
|
|
90
|
+
issueTracking,
|
|
91
91
|
customizations: {}
|
|
92
92
|
};
|
|
93
93
|
console.log(chalk_1.default.blue(` Platform: ${detection.provider.toUpperCase()}`));
|
|
94
94
|
if (detection.provider === 'github') {
|
|
95
95
|
console.log(chalk_1.default.gray(` Repository: ${detection.repository.owner}/${detection.repository.name}`));
|
|
96
|
-
console.log(chalk_1.default.gray(` Token: ${hasToken ? 'ā Configured' : 'ā Missing'}`));
|
|
97
96
|
}
|
|
98
97
|
else if (detection.provider === 'ado') {
|
|
99
98
|
console.log(chalk_1.default.gray(` Organization: ${detection.repository.organization}`));
|
|
100
99
|
console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
|
|
101
100
|
console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
|
|
102
|
-
console.log(chalk_1.default.gray(` Token: ${hasToken ? 'ā Configured' : 'ā Missing'}`));
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
else {
|
|
106
|
-
// No git remote detected - warn user and fallback to conversational-style config
|
|
107
|
-
console.log(chalk_1.default.yellow(' ā ļø No git remote detected'));
|
|
108
|
-
console.log(chalk_1.default.yellow(' Integrated mode requires a git remote for platform features.'));
|
|
109
|
-
console.log(chalk_1.default.gray(' Falling back to conversational mode configuration...'));
|
|
110
|
-
const repoName = path_1.default.basename(projectRoot);
|
|
111
104
|
config = {
|
|
112
105
|
version: (0, version_utils_1.getFraimVersion)(),
|
|
113
106
|
project: {
|
|
114
|
-
name:
|
|
107
|
+
name: projectName
|
|
115
108
|
},
|
|
116
109
|
customizations: {}
|
|
117
110
|
};
|
|
118
|
-
console.log(chalk_1.default.
|
|
111
|
+
console.log(chalk_1.default.yellow(' No git remote detected. Falling back to conversational mode.'));
|
|
119
112
|
}
|
|
120
113
|
}
|
|
121
114
|
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
122
|
-
console.log(chalk_1.default.green('
|
|
115
|
+
console.log(chalk_1.default.green('Created .fraim/config.json'));
|
|
123
116
|
}
|
|
124
|
-
|
|
125
|
-
['workflows'].forEach(dir => {
|
|
117
|
+
['workflows'].forEach((dir) => {
|
|
126
118
|
const dirPath = path_1.default.join(fraimDir, dir);
|
|
127
119
|
if (!fs_1.default.existsSync(dirPath)) {
|
|
128
120
|
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
129
|
-
console.log(chalk_1.default.green(
|
|
121
|
+
console.log(chalk_1.default.green(`Created .fraim/${dir}`));
|
|
130
122
|
}
|
|
131
123
|
});
|
|
132
|
-
// Sync workflows from registry
|
|
133
124
|
await (0, sync_1.runSync)({});
|
|
134
125
|
const codexAvailable = (0, ide_detector_1.detectInstalledIDEs)().some((ide) => ide.configType === 'codex');
|
|
135
126
|
if (codexAvailable) {
|
|
136
127
|
const codexLocalResult = (0, codex_local_config_1.ensureCodexLocalConfig)(projectRoot);
|
|
137
128
|
const status = codexLocalResult.created ? 'Created' : codexLocalResult.updated ? 'Updated' : 'Verified';
|
|
138
|
-
console.log(chalk_1.default.green(
|
|
129
|
+
console.log(chalk_1.default.green(`${status} project Codex config at ${codexLocalResult.path}`));
|
|
139
130
|
}
|
|
140
|
-
console.log(chalk_1.default.green('\
|
|
131
|
+
console.log(chalk_1.default.green('\nFRAIM project initialized!'));
|
|
141
132
|
console.log(chalk_1.default.cyan('Try: Ask your AI agent "list fraim workflows"'));
|
|
142
133
|
};
|
|
143
134
|
exports.runInitProject = runInitProject;
|
|
@@ -46,6 +46,9 @@ function loadFraimConfig() {
|
|
|
46
46
|
...(config.customizations || {})
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
+
if (config.issueTracking && typeof config.issueTracking === 'object') {
|
|
50
|
+
mergedConfig.issueTracking = config.issueTracking;
|
|
51
|
+
}
|
|
49
52
|
// Add optional workflow-driven fields only if they exist in the config
|
|
50
53
|
if (config.compliance) {
|
|
51
54
|
mergedConfig.compliance = config.compliance;
|
|
@@ -106,19 +106,49 @@ class FraimTemplateEngine {
|
|
|
106
106
|
return null;
|
|
107
107
|
}
|
|
108
108
|
substitutePlatformActions(content) {
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
const codeProvider = this.getCodeProvider();
|
|
110
|
+
const issueProvider = this.getIssueProvider();
|
|
111
|
+
const codeTemplates = this.loadProviderTemplates(codeProvider);
|
|
112
|
+
const issueTemplates = issueProvider ? this.loadProviderTemplates(issueProvider) : null;
|
|
113
113
|
let result = content;
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
if (issueTemplates) {
|
|
115
|
+
for (const [action, template] of Object.entries(issueTemplates)) {
|
|
116
|
+
if (!FraimTemplateEngine.ISSUE_ACTIONS.has(action))
|
|
117
|
+
continue;
|
|
118
|
+
const escapedAction = action.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
119
|
+
const regex = new RegExp(`\\{\\{${FraimTemplateEngine.PROXY_ACTION_PREFIX}${escapedAction}\\}\\}`, 'g');
|
|
120
|
+
result = result.replace(regex, this.renderActionTemplate(template));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (codeTemplates) {
|
|
124
|
+
for (const [action, template] of Object.entries(codeTemplates)) {
|
|
125
|
+
if (issueProvider && issueProvider !== codeProvider && FraimTemplateEngine.ISSUE_ACTIONS.has(action))
|
|
126
|
+
continue;
|
|
127
|
+
const escapedAction = action.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
128
|
+
const regex = new RegExp(`\\{\\{${FraimTemplateEngine.PROXY_ACTION_PREFIX}${escapedAction}\\}\\}`, 'g');
|
|
129
|
+
result = result.replace(regex, this.renderActionTemplate(template));
|
|
130
|
+
}
|
|
119
131
|
}
|
|
120
132
|
return result;
|
|
121
133
|
}
|
|
134
|
+
getCodeProvider() {
|
|
135
|
+
const repoProvider = this.repoInfo?.provider || this.config?.repository?.provider;
|
|
136
|
+
if (typeof repoProvider === 'string' && repoProvider.trim().length > 0) {
|
|
137
|
+
return repoProvider;
|
|
138
|
+
}
|
|
139
|
+
return (0, provider_utils_1.detectProvider)(this.repoInfo?.url || this.config?.repository?.url);
|
|
140
|
+
}
|
|
141
|
+
getIssueProvider() {
|
|
142
|
+
const fromRepoInfo = this.repoInfo?.issueTracking?.provider;
|
|
143
|
+
if (typeof fromRepoInfo === 'string' && fromRepoInfo.trim().length > 0) {
|
|
144
|
+
return fromRepoInfo;
|
|
145
|
+
}
|
|
146
|
+
const fromConfig = this.config?.issueTracking?.provider;
|
|
147
|
+
if (typeof fromConfig === 'string' && fromConfig.trim().length > 0) {
|
|
148
|
+
return fromConfig;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
122
152
|
loadProviderTemplates(provider) {
|
|
123
153
|
if (this.providerTemplatesCache[provider])
|
|
124
154
|
return this.providerTemplatesCache[provider];
|
|
@@ -130,11 +160,12 @@ class FraimTemplateEngine {
|
|
|
130
160
|
return null;
|
|
131
161
|
}
|
|
132
162
|
renderActionTemplate(template) {
|
|
133
|
-
if (!this.repoInfo && !this.config?.repository) {
|
|
163
|
+
if (!this.repoInfo && !this.config?.repository && !this.config?.issueTracking) {
|
|
134
164
|
return template;
|
|
135
165
|
}
|
|
136
166
|
return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
137
167
|
const trimmedPath = path.trim();
|
|
168
|
+
// Handle proxy.repository.* variables
|
|
138
169
|
if (trimmedPath.startsWith('proxy.repository.')) {
|
|
139
170
|
const repoPath = trimmedPath.substring('proxy.repository.'.length);
|
|
140
171
|
if (this.repoInfo) {
|
|
@@ -148,12 +179,36 @@ class FraimTemplateEngine {
|
|
|
148
179
|
return String(value);
|
|
149
180
|
}
|
|
150
181
|
}
|
|
182
|
+
// Handle proxy.issueTracking.* variables (for split provider mode)
|
|
183
|
+
if (trimmedPath.startsWith('proxy.issueTracking.')) {
|
|
184
|
+
const issueTrackingPath = trimmedPath.substring('proxy.issueTracking.'.length);
|
|
185
|
+
if (this.repoInfo?.issueTracking) {
|
|
186
|
+
const value = (0, object_utils_1.getNestedValue)(this.repoInfo.issueTracking, issueTrackingPath);
|
|
187
|
+
if (value !== undefined)
|
|
188
|
+
return String(value);
|
|
189
|
+
}
|
|
190
|
+
if (this.config?.issueTracking) {
|
|
191
|
+
const value = (0, object_utils_1.getNestedValue)(this.config.issueTracking, issueTrackingPath);
|
|
192
|
+
if (value !== undefined)
|
|
193
|
+
return String(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
151
196
|
return match;
|
|
152
197
|
});
|
|
153
198
|
}
|
|
154
199
|
}
|
|
155
200
|
exports.FraimTemplateEngine = FraimTemplateEngine;
|
|
156
201
|
FraimTemplateEngine.PROXY_ACTION_PREFIX = 'proxy.action.';
|
|
202
|
+
FraimTemplateEngine.ISSUE_ACTIONS = new Set([
|
|
203
|
+
'get_issue',
|
|
204
|
+
'update_issue_status',
|
|
205
|
+
'add_issue_comment',
|
|
206
|
+
'create_issue',
|
|
207
|
+
'assign_issue',
|
|
208
|
+
'search_issues',
|
|
209
|
+
'close_issue',
|
|
210
|
+
'list_issues'
|
|
211
|
+
]);
|
|
157
212
|
class FraimLocalMCPServer {
|
|
158
213
|
constructor() {
|
|
159
214
|
this.config = null;
|
|
@@ -365,16 +420,17 @@ class FraimLocalMCPServer {
|
|
|
365
420
|
}).trim();
|
|
366
421
|
}
|
|
367
422
|
catch (error) {
|
|
368
|
-
// If git command fails, construct URL from config if available
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
423
|
+
// If git command fails, construct URL from config if available.
|
|
424
|
+
const repositoryConfig = this.config?.repository;
|
|
425
|
+
if (repositoryConfig?.provider === 'github' && repositoryConfig.owner && repositoryConfig.name) {
|
|
426
|
+
repoUrl = `https://github.com/${repositoryConfig.owner}/${repositoryConfig.name}.git`;
|
|
427
|
+
this.log(`š Constructed repo URL from config: ${repoUrl}`);
|
|
428
|
+
}
|
|
429
|
+
else if (repositoryConfig?.provider === 'ado' &&
|
|
430
|
+
repositoryConfig.organization &&
|
|
431
|
+
repositoryConfig.project &&
|
|
432
|
+
repositoryConfig.name) {
|
|
433
|
+
repoUrl = `https://dev.azure.com/${repositoryConfig.organization}/${repositoryConfig.project}/_git/${repositoryConfig.name}`;
|
|
378
434
|
this.log(`š Constructed repo URL from config: ${repoUrl}`);
|
|
379
435
|
}
|
|
380
436
|
}
|
|
@@ -383,10 +439,10 @@ class FraimLocalMCPServer {
|
|
|
383
439
|
return null;
|
|
384
440
|
}
|
|
385
441
|
// Parse owner and name from URL
|
|
386
|
-
let owner = '';
|
|
387
442
|
let name = '';
|
|
388
443
|
let organization = '';
|
|
389
444
|
let project = '';
|
|
445
|
+
let owner = '';
|
|
390
446
|
// Handle GitHub URLs: https://github.com/owner/repo.git or git@github.com:owner/repo.git
|
|
391
447
|
const githubHttpsMatch = repoUrl.match(/github\.com[\/:]([^\/]+)\/([^\/\.]+)/);
|
|
392
448
|
const adoMatch = repoUrl.match(/dev\.azure\.com\/([^\/]+)\/([^\/]+)\/_git\/([^\/]+)/);
|
|
@@ -398,7 +454,6 @@ class FraimLocalMCPServer {
|
|
|
398
454
|
// Azure DevOps: organization and project are separate fields
|
|
399
455
|
organization = adoMatch[1];
|
|
400
456
|
project = adoMatch[2];
|
|
401
|
-
owner = organization; // For compatibility
|
|
402
457
|
name = adoMatch[3];
|
|
403
458
|
}
|
|
404
459
|
else if (this.config?.repository) {
|
|
@@ -423,15 +478,21 @@ class FraimLocalMCPServer {
|
|
|
423
478
|
branch = this.config.repository.defaultBranch;
|
|
424
479
|
}
|
|
425
480
|
}
|
|
426
|
-
|
|
481
|
+
const repoInfo = {
|
|
427
482
|
url: repoUrl,
|
|
428
|
-
owner: owner || 'unknown',
|
|
429
483
|
name: name || 'unknown',
|
|
430
484
|
...(organization && { organization }),
|
|
431
485
|
...(project && { project }),
|
|
432
486
|
...(branch && { branch })
|
|
433
487
|
};
|
|
434
|
-
|
|
488
|
+
if (owner) {
|
|
489
|
+
repoInfo.owner = owner;
|
|
490
|
+
}
|
|
491
|
+
this.repoInfo = repoInfo;
|
|
492
|
+
const repoLabel = this.repoInfo.owner
|
|
493
|
+
? `${this.repoInfo.owner}/${this.repoInfo.name}`
|
|
494
|
+
: this.repoInfo.name;
|
|
495
|
+
this.log(`ā
Detected repo info: ${repoLabel}`);
|
|
435
496
|
return this.repoInfo;
|
|
436
497
|
}
|
|
437
498
|
catch (error) {
|
|
@@ -630,25 +691,57 @@ class FraimLocalMCPServer {
|
|
|
630
691
|
if (!hasDirectProviderPlaceholders && !hasDeliveryPlaceholders)
|
|
631
692
|
return;
|
|
632
693
|
const engine = this.ensureEngine();
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
694
|
+
const codeProvider = engine.getCodeProvider();
|
|
695
|
+
const issueProvider = engine.getIssueProvider();
|
|
696
|
+
const providers = Array.from(new Set([codeProvider, issueProvider].filter((provider) => typeof provider === 'string' && provider.length > 0)));
|
|
697
|
+
for (const provider of providers) {
|
|
698
|
+
if (engine.hasProviderTemplates(provider))
|
|
699
|
+
continue;
|
|
700
|
+
if (engine.loadProviderTemplates(provider))
|
|
701
|
+
continue;
|
|
702
|
+
const filename = `${provider}.json`;
|
|
703
|
+
const cached = this.readCachedTemplateFile(filename);
|
|
704
|
+
if (cached) {
|
|
705
|
+
engine.setProviderTemplates(provider, cached);
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
const remoteContent = await this.fetchRegistryFileFromServer(`providers/${filename}`, requestSessionId);
|
|
709
|
+
if (remoteContent) {
|
|
710
|
+
const parsed = this.parseTemplateJson(remoteContent, `providers/${filename}`);
|
|
711
|
+
if (parsed) {
|
|
712
|
+
engine.setProviderTemplates(provider, parsed);
|
|
713
|
+
this.writeCachedTemplateFile(filename, parsed);
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
const bundled = this.readBundledTemplateFile(filename);
|
|
718
|
+
if (bundled) {
|
|
719
|
+
engine.setProviderTemplates(provider, bundled);
|
|
720
|
+
}
|
|
643
721
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
722
|
+
}
|
|
723
|
+
readBundledTemplateFile(filename) {
|
|
724
|
+
const candidates = [
|
|
725
|
+
(0, path_1.join)(__dirname, '..', '..', '..', 'registry', 'providers', filename),
|
|
726
|
+
(0, path_1.join)(__dirname, '..', '..', 'registry', 'providers', filename),
|
|
727
|
+
(0, path_1.join)(process.cwd(), 'registry', 'providers', filename)
|
|
728
|
+
];
|
|
729
|
+
for (const candidate of candidates) {
|
|
730
|
+
try {
|
|
731
|
+
if (!(0, fs_1.existsSync)(candidate))
|
|
732
|
+
continue;
|
|
733
|
+
const content = (0, fs_1.readFileSync)(candidate, 'utf8');
|
|
734
|
+
const parsed = JSON.parse(content);
|
|
735
|
+
if (parsed && typeof parsed === 'object') {
|
|
736
|
+
this.log(`ā
Loaded bundled provider template: ${candidate}`);
|
|
737
|
+
return parsed;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
catch (error) {
|
|
741
|
+
this.log(`ā ļø Failed to load bundled template ${candidate}: ${error.message}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return null;
|
|
652
745
|
}
|
|
653
746
|
async hydrateTemplateCachesForResponse(response, requestSessionId) {
|
|
654
747
|
if (!response.result)
|
|
@@ -661,10 +754,12 @@ class FraimLocalMCPServer {
|
|
|
661
754
|
let processedResponse = this.processResponse(response);
|
|
662
755
|
// Delivery substitution can introduce provider action placeholders (e.g. {{proxy.action.update_issue_status}})
|
|
663
756
|
// that were not visible pre-substitution. Run one targeted re-hydration pass if needed.
|
|
664
|
-
const provider = (0, provider_utils_1.detectProvider)(this.detectRepoInfo()?.url);
|
|
665
757
|
const engine = this.ensureEngine();
|
|
666
|
-
|
|
667
|
-
|
|
758
|
+
const codeProvider = engine.getCodeProvider();
|
|
759
|
+
const issueProvider = engine.getIssueProvider();
|
|
760
|
+
const providers = Array.from(new Set([codeProvider, issueProvider].filter((provider) => typeof provider === 'string' && provider.length > 0)));
|
|
761
|
+
const hasMissingProviderTemplate = providers.some((provider) => !engine.hasProviderTemplates(provider));
|
|
762
|
+
if (this.responseHasPotentialProviderPlaceholders(processedResponse) && hasMissingProviderTemplate) {
|
|
668
763
|
await this.ensureProviderTemplatesAvailable(processedResponse, requestSessionId);
|
|
669
764
|
processedResponse = this.processResponse(processedResponse);
|
|
670
765
|
}
|
|
@@ -988,7 +1083,8 @@ class FraimLocalMCPServer {
|
|
|
988
1083
|
...args.repo, // Agent values as fallback
|
|
989
1084
|
...detectedRepo // Detected values override (always win)
|
|
990
1085
|
};
|
|
991
|
-
|
|
1086
|
+
const repoLabel = args.repo.owner ? `${args.repo.owner}/${args.repo.name}` : args.repo.name;
|
|
1087
|
+
this.log(`[req:${requestId}] Auto-detected and injected repo info: ${repoLabel}`);
|
|
992
1088
|
}
|
|
993
1089
|
else {
|
|
994
1090
|
// If detection fails, use agent-provided values (if any)
|
|
@@ -1004,12 +1100,25 @@ class FraimLocalMCPServer {
|
|
|
1004
1100
|
}
|
|
1005
1101
|
};
|
|
1006
1102
|
}
|
|
1007
|
-
|
|
1103
|
+
const repoLabel = args.repo.owner ? `${args.repo.owner}/${args.repo.name}` : args.repo.name;
|
|
1104
|
+
this.log(`[req:${requestId}] Using agent-provided repo info: ${repoLabel}`);
|
|
1008
1105
|
}
|
|
1106
|
+
const configuredIssueTracking = this.config?.issueTracking;
|
|
1107
|
+
if (configuredIssueTracking && typeof configuredIssueTracking === 'object') {
|
|
1108
|
+
args.issueTracking = {
|
|
1109
|
+
...(args.issueTracking || {}),
|
|
1110
|
+
...configuredIssueTracking
|
|
1111
|
+
};
|
|
1112
|
+
this.log(`[req:${requestId}] Applied issueTracking context: ${args.issueTracking.provider || 'unknown'}`);
|
|
1113
|
+
}
|
|
1114
|
+
const runtimeRepoContext = {
|
|
1115
|
+
...args.repo,
|
|
1116
|
+
...(args.issueTracking ? { issueTracking: args.issueTracking } : {})
|
|
1117
|
+
};
|
|
1009
1118
|
// Keep proxy runtime repository context in sync with connect payload.
|
|
1010
|
-
this.repoInfo =
|
|
1119
|
+
this.repoInfo = runtimeRepoContext;
|
|
1011
1120
|
if (this.engine) {
|
|
1012
|
-
this.engine.setRepoInfo(
|
|
1121
|
+
this.engine.setRepoInfo(runtimeRepoContext);
|
|
1013
1122
|
}
|
|
1014
1123
|
// Update the request with injected info
|
|
1015
1124
|
request.params.arguments = args;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-framework",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.77",
|
|
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": {
|