fraim-framework 2.0.62 ā 2.0.64
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.
- package/bin/fraim-mcp.js +25 -9
- package/dist/src/cli/commands/init-project.js +98 -26
- package/dist/src/cli/commands/init.js +81 -23
- package/dist/src/cli/commands/setup.js +275 -47
- package/dist/src/local-mcp-server/stdio-server.js +412 -27
- package/dist/src/utils/enforcement-utils.js +239 -0
- package/dist/src/utils/git-utils.js +0 -27
- package/dist/src/utils/platform-detection.js +1 -1
- package/dist/src/utils/script-sync-utils.js +6 -1
- package/dist/src/utils/validate-workflows.js +101 -0
- package/package.json +5 -4
- package/registry/stubs/workflows/azure/cost-optimization.md +11 -0
- package/bin/fraim.js +0 -23
- package/dist/src/cli/commands/mcp.js +0 -65
- package/dist/src/fraim/issue-tracking/ado-provider.js +0 -304
- package/dist/src/fraim/issue-tracking/factory.js +0 -63
- package/dist/src/fraim/issue-tracking/github-provider.js +0 -200
- package/dist/src/fraim/issue-tracking/types.js +0 -7
- package/dist/src/fraim/issue-tracking-config.js +0 -83
package/bin/fraim-mcp.js
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* FRAIM MCP
|
|
3
|
+
* FRAIM Local MCP Proxy
|
|
4
4
|
*
|
|
5
|
-
* This is
|
|
6
|
-
*
|
|
7
|
-
* and performs template substitution.
|
|
5
|
+
* This is a thin wrapper that launches the local STDIO MCP server.
|
|
6
|
+
* The actual implementation is in dist/src/local-mcp-server/stdio-server.js
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
const {
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { spawn } = require('child_process');
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
server.
|
|
12
|
+
// Path to the actual server implementation
|
|
13
|
+
const serverPath = path.join(__dirname, '..', 'dist', 'src', 'local-mcp-server', 'stdio-server.js');
|
|
14
|
+
|
|
15
|
+
// Spawn the server with all arguments and environment variables
|
|
16
|
+
const server = spawn('node', [serverPath, ...process.argv.slice(2)], {
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
env: process.env
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Forward exit code
|
|
22
|
+
server.on('exit', (code) => {
|
|
23
|
+
process.exit(code || 0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Handle errors
|
|
27
|
+
server.on('error', (err) => {
|
|
28
|
+
console.error('Failed to start FRAIM MCP server:', err);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
|
@@ -10,22 +10,38 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const os_1 = __importDefault(require("os"));
|
|
11
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
12
12
|
const sync_1 = require("./sync");
|
|
13
|
-
const
|
|
14
|
-
const git_utils_1 = require("../../utils/git-utils");
|
|
13
|
+
const platform_detection_1 = require("../../utils/platform-detection");
|
|
15
14
|
const version_utils_1 = require("../../utils/version-utils");
|
|
16
15
|
const checkGlobalSetup = () => {
|
|
17
16
|
const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
|
|
18
|
-
|
|
17
|
+
if (!fs_1.default.existsSync(globalConfigPath)) {
|
|
18
|
+
return { exists: false };
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
|
|
22
|
+
return {
|
|
23
|
+
exists: true,
|
|
24
|
+
mode: config.mode || 'integrated', // Default to integrated for backward compatibility
|
|
25
|
+
tokens: config.tokens || {}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return { exists: true, mode: 'integrated', tokens: {} };
|
|
30
|
+
}
|
|
19
31
|
};
|
|
20
32
|
const runInitProject = async () => {
|
|
21
33
|
console.log(chalk_1.default.blue('š Initializing FRAIM project...'));
|
|
22
34
|
// Check if global setup exists
|
|
23
|
-
|
|
35
|
+
const globalSetup = checkGlobalSetup();
|
|
36
|
+
if (!globalSetup.exists) {
|
|
24
37
|
console.log(chalk_1.default.red('ā Global FRAIM setup not found.'));
|
|
25
38
|
console.log(chalk_1.default.yellow('Please run global setup first:'));
|
|
26
|
-
console.log(chalk_1.default.cyan(' fraim setup
|
|
39
|
+
console.log(chalk_1.default.cyan(' fraim setup'));
|
|
27
40
|
process.exit(1);
|
|
28
41
|
}
|
|
42
|
+
const mode = globalSetup.mode;
|
|
43
|
+
const tokens = globalSetup.tokens || {};
|
|
44
|
+
console.log(chalk_1.default.gray(` Mode: ${mode === 'conversational' ? 'Conversational' : 'Integrated'} (from global config)`));
|
|
29
45
|
const projectRoot = process.cwd();
|
|
30
46
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
31
47
|
const configPath = path_1.default.join(fraimDir, 'config.json');
|
|
@@ -37,28 +53,84 @@ const runInitProject = async () => {
|
|
|
37
53
|
console.log(chalk_1.default.yellow('ā¹ļø .fraim directory already exists'));
|
|
38
54
|
}
|
|
39
55
|
if (!fs_1.default.existsSync(configPath)) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
let config;
|
|
57
|
+
if (mode === 'conversational') {
|
|
58
|
+
// Conversational mode - no platform integration
|
|
59
|
+
const projectName = path_1.default.basename(projectRoot);
|
|
60
|
+
config = {
|
|
61
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
62
|
+
project: {
|
|
63
|
+
name: projectName
|
|
64
|
+
},
|
|
65
|
+
mode: 'conversational',
|
|
66
|
+
customizations: {
|
|
67
|
+
workflowsPath: '.fraim/workflows'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
console.log(chalk_1.default.blue(' Conversational mode: No platform integration'));
|
|
71
|
+
console.log(chalk_1.default.gray(` Project: ${projectName}`));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Integrated mode - try to detect platform
|
|
75
|
+
const detection = (0, platform_detection_1.detectPlatformFromGit)();
|
|
76
|
+
if (detection.provider !== 'unknown' && detection.repository) {
|
|
77
|
+
// Check if we have the appropriate token
|
|
78
|
+
const hasToken = detection.provider === 'github' ? tokens.github : tokens.ado;
|
|
79
|
+
if (!hasToken) {
|
|
80
|
+
console.log(chalk_1.default.yellow(`\nā ļø Warning: ${detection.provider.toUpperCase()} repository detected but no token configured`));
|
|
81
|
+
console.log(chalk_1.default.gray(` Platform features will be limited.`));
|
|
82
|
+
console.log(chalk_1.default.cyan(` Run: fraim setup --${detection.provider === 'github' ? 'github' : 'ado'}\n`));
|
|
83
|
+
}
|
|
84
|
+
// Use detected repository info
|
|
85
|
+
config = {
|
|
86
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
87
|
+
project: {
|
|
88
|
+
name: detection.repository.name
|
|
89
|
+
},
|
|
90
|
+
repository: detection.repository,
|
|
91
|
+
mode: 'integrated',
|
|
92
|
+
customizations: {
|
|
93
|
+
workflowsPath: '.fraim/workflows'
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
console.log(chalk_1.default.blue(` Detected ${detection.provider.toUpperCase()} repository`));
|
|
97
|
+
if (detection.provider === 'github') {
|
|
98
|
+
console.log(chalk_1.default.gray(` Repository: ${detection.repository.owner}/${detection.repository.name}`));
|
|
99
|
+
console.log(chalk_1.default.gray(` Token: ${hasToken ? 'ā Configured' : 'ā Missing'}`));
|
|
100
|
+
}
|
|
101
|
+
else if (detection.provider === 'ado') {
|
|
102
|
+
console.log(chalk_1.default.gray(` Organization: ${detection.repository.organization}`));
|
|
103
|
+
console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
|
|
104
|
+
console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
|
|
105
|
+
console.log(chalk_1.default.gray(` Token: ${hasToken ? 'ā Configured' : 'ā Missing'}`));
|
|
106
|
+
}
|
|
60
107
|
}
|
|
61
|
-
|
|
108
|
+
else {
|
|
109
|
+
// No git remote detected - warn user
|
|
110
|
+
console.log(chalk_1.default.yellow(' ā ļø No git remote detected'));
|
|
111
|
+
console.log(chalk_1.default.yellow(' Integrated mode requires a git remote for platform features.'));
|
|
112
|
+
console.log(chalk_1.default.gray(' Creating config with placeholder values...'));
|
|
113
|
+
const repoName = path_1.default.basename(projectRoot);
|
|
114
|
+
config = {
|
|
115
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
116
|
+
project: {
|
|
117
|
+
name: repoName
|
|
118
|
+
},
|
|
119
|
+
repository: {
|
|
120
|
+
provider: 'github',
|
|
121
|
+
owner: 'your-username',
|
|
122
|
+
name: repoName,
|
|
123
|
+
url: `https://github.com/your-username/${repoName}.git`,
|
|
124
|
+
defaultBranch: 'main'
|
|
125
|
+
},
|
|
126
|
+
mode: 'integrated',
|
|
127
|
+
customizations: {
|
|
128
|
+
workflowsPath: '.fraim/workflows'
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
console.log(chalk_1.default.gray(' Please update .fraim/config.json with your repository details.'));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
62
134
|
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
63
135
|
console.log(chalk_1.default.green('ā
Created .fraim/config.json'));
|
|
64
136
|
}
|
|
@@ -10,10 +10,10 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const first_run_1 = require("../setup/first-run");
|
|
12
12
|
const sync_1 = require("./sync");
|
|
13
|
-
const
|
|
13
|
+
const platform_detection_1 = require("../../utils/platform-detection");
|
|
14
14
|
const version_utils_1 = require("../../utils/version-utils");
|
|
15
15
|
const script_sync_utils_1 = require("../../utils/script-sync-utils");
|
|
16
|
-
const runInit = async () => {
|
|
16
|
+
const runInit = async (options = {}) => {
|
|
17
17
|
const projectRoot = process.cwd();
|
|
18
18
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
19
19
|
const configPath = path_1.default.join(fraimDir, 'config.json');
|
|
@@ -26,28 +26,85 @@ const runInit = async () => {
|
|
|
26
26
|
console.log(chalk_1.default.yellow('ā¹ļø .fraim directory already exists'));
|
|
27
27
|
}
|
|
28
28
|
if (!fs_1.default.existsSync(configPath)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
mode: 'conversational',
|
|
65
|
+
customizations: {
|
|
66
|
+
workflowsPath: '.fraim/workflows'
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
console.log(chalk_1.default.gray(` Project: ${projectName}`));
|
|
70
|
+
console.log(chalk_1.default.gray(` Mode: Conversational (no platform integration)`));
|
|
35
71
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
repository: {
|
|
42
|
-
provider: 'github',
|
|
43
|
-
owner: remoteInfo.owner || 'your-username',
|
|
44
|
-
name: remoteInfo.repo,
|
|
45
|
-
defaultBranch: remoteInfo.defaultBranch || 'main'
|
|
46
|
-
},
|
|
47
|
-
customizations: {
|
|
48
|
-
workflowsPath: '.fraim/workflows'
|
|
72
|
+
else {
|
|
73
|
+
// Integrated mode - use platform
|
|
74
|
+
if (!detection.repository) {
|
|
75
|
+
console.log(chalk_1.default.red('ā Error: Repository information not available'));
|
|
76
|
+
process.exit(1);
|
|
49
77
|
}
|
|
50
|
-
|
|
78
|
+
// Validate the detected repository config
|
|
79
|
+
const validation = (0, platform_detection_1.validateRepositoryConfig)(detection.repository);
|
|
80
|
+
if (!validation.valid) {
|
|
81
|
+
console.log(chalk_1.default.red('ā Error: Invalid repository configuration:'));
|
|
82
|
+
validation.errors.forEach(err => console.log(chalk_1.default.red(` - ${err}`)));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
console.log(chalk_1.default.blue('\n Initializing in integrated mode (platform integration enabled).'));
|
|
86
|
+
console.log(chalk_1.default.gray(` Platform: ${detection.provider.toUpperCase()}`));
|
|
87
|
+
if (detection.provider === 'github') {
|
|
88
|
+
console.log(chalk_1.default.gray(` Repository: ${detection.repository.owner}/${detection.repository.name}`));
|
|
89
|
+
}
|
|
90
|
+
else if (detection.provider === 'ado') {
|
|
91
|
+
console.log(chalk_1.default.gray(` Organization: ${detection.repository.organization}`));
|
|
92
|
+
console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
|
|
93
|
+
console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
|
|
94
|
+
}
|
|
95
|
+
console.log();
|
|
96
|
+
config = {
|
|
97
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
98
|
+
project: {
|
|
99
|
+
name: detection.repository.name
|
|
100
|
+
},
|
|
101
|
+
repository: detection.repository,
|
|
102
|
+
mode: 'integrated',
|
|
103
|
+
customizations: {
|
|
104
|
+
workflowsPath: '.fraim/workflows'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
51
108
|
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
52
109
|
console.log(chalk_1.default.green('ā
Created .fraim/config.json'));
|
|
53
110
|
}
|
|
@@ -89,4 +146,5 @@ const runInit = async () => {
|
|
|
89
146
|
exports.runInit = runInit;
|
|
90
147
|
exports.initCommand = new commander_1.Command('init')
|
|
91
148
|
.description('Initialize FRAIM in the current project')
|
|
92
|
-
.
|
|
149
|
+
.option('--skip-platform', 'Skip platform integration (conversational mode only)')
|
|
150
|
+
.action((options) => (0, exports.runInit)(options));
|