fraim 2.0.100

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 (70) hide show
  1. package/README.md +445 -0
  2. package/bin/fraim.js +23 -0
  3. package/dist/src/cli/api/get-provider-client.js +41 -0
  4. package/dist/src/cli/api/provider-client.js +107 -0
  5. package/dist/src/cli/commands/add-ide.js +430 -0
  6. package/dist/src/cli/commands/add-provider.js +233 -0
  7. package/dist/src/cli/commands/doctor.js +149 -0
  8. package/dist/src/cli/commands/init-project.js +301 -0
  9. package/dist/src/cli/commands/list-overridable.js +184 -0
  10. package/dist/src/cli/commands/list.js +57 -0
  11. package/dist/src/cli/commands/login.js +84 -0
  12. package/dist/src/cli/commands/mcp.js +15 -0
  13. package/dist/src/cli/commands/migrate-project-fraim.js +42 -0
  14. package/dist/src/cli/commands/override.js +177 -0
  15. package/dist/src/cli/commands/setup.js +651 -0
  16. package/dist/src/cli/commands/sync.js +162 -0
  17. package/dist/src/cli/commands/test-mcp.js +171 -0
  18. package/dist/src/cli/doctor/check-runner.js +199 -0
  19. package/dist/src/cli/doctor/checks/global-setup-checks.js +220 -0
  20. package/dist/src/cli/doctor/checks/ide-config-checks.js +250 -0
  21. package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +381 -0
  22. package/dist/src/cli/doctor/checks/project-setup-checks.js +282 -0
  23. package/dist/src/cli/doctor/checks/scripts-checks.js +157 -0
  24. package/dist/src/cli/doctor/checks/workflow-checks.js +251 -0
  25. package/dist/src/cli/doctor/reporters/console-reporter.js +96 -0
  26. package/dist/src/cli/doctor/reporters/json-reporter.js +11 -0
  27. package/dist/src/cli/doctor/types.js +6 -0
  28. package/dist/src/cli/fraim.js +100 -0
  29. package/dist/src/cli/internal/device-flow-service.js +83 -0
  30. package/dist/src/cli/mcp/ide-formats.js +243 -0
  31. package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
  32. package/dist/src/cli/mcp/mcp-server-registry.js +160 -0
  33. package/dist/src/cli/mcp/types.js +3 -0
  34. package/dist/src/cli/providers/local-provider-registry.js +166 -0
  35. package/dist/src/cli/providers/provider-registry.js +230 -0
  36. package/dist/src/cli/setup/auto-mcp-setup.js +331 -0
  37. package/dist/src/cli/setup/codex-local-config.js +37 -0
  38. package/dist/src/cli/setup/first-run.js +242 -0
  39. package/dist/src/cli/setup/ide-detector.js +179 -0
  40. package/dist/src/cli/setup/mcp-config-generator.js +192 -0
  41. package/dist/src/cli/setup/provider-prompts.js +339 -0
  42. package/dist/src/cli/utils/agent-adapters.js +126 -0
  43. package/dist/src/cli/utils/digest-utils.js +47 -0
  44. package/dist/src/cli/utils/fraim-gitignore.js +40 -0
  45. package/dist/src/cli/utils/platform-detection.js +258 -0
  46. package/dist/src/cli/utils/project-bootstrap.js +93 -0
  47. package/dist/src/cli/utils/remote-sync.js +315 -0
  48. package/dist/src/cli/utils/script-sync-utils.js +221 -0
  49. package/dist/src/cli/utils/version-utils.js +32 -0
  50. package/dist/src/core/ai-mentor.js +230 -0
  51. package/dist/src/core/config-loader.js +114 -0
  52. package/dist/src/core/config-writer.js +75 -0
  53. package/dist/src/core/types.js +23 -0
  54. package/dist/src/core/utils/git-utils.js +95 -0
  55. package/dist/src/core/utils/include-resolver.js +92 -0
  56. package/dist/src/core/utils/inheritance-parser.js +288 -0
  57. package/dist/src/core/utils/job-parser.js +176 -0
  58. package/dist/src/core/utils/local-registry-resolver.js +616 -0
  59. package/dist/src/core/utils/object-utils.js +11 -0
  60. package/dist/src/core/utils/project-fraim-migration.js +103 -0
  61. package/dist/src/core/utils/project-fraim-paths.js +38 -0
  62. package/dist/src/core/utils/provider-utils.js +18 -0
  63. package/dist/src/core/utils/server-startup.js +34 -0
  64. package/dist/src/core/utils/stub-generator.js +147 -0
  65. package/dist/src/core/utils/workflow-parser.js +174 -0
  66. package/dist/src/local-mcp-server/learning-context-builder.js +229 -0
  67. package/dist/src/local-mcp-server/stdio-server.js +1698 -0
  68. package/dist/src/local-mcp-server/usage-collector.js +264 -0
  69. package/index.js +85 -0
  70. package/package.json +139 -0
@@ -0,0 +1,57 @@
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.listCommand = 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 project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
12
+ function collectRelativeMarkdownPaths(rootDir, currentRelativeDir = '') {
13
+ if (!fs_1.default.existsSync(rootDir)) {
14
+ return [];
15
+ }
16
+ const results = [];
17
+ const entries = fs_1.default.readdirSync(rootDir, { withFileTypes: true });
18
+ for (const entry of entries) {
19
+ const relativePath = currentRelativeDir
20
+ ? path_1.default.join(currentRelativeDir, entry.name)
21
+ : entry.name;
22
+ const fullPath = path_1.default.join(rootDir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ results.push(...collectRelativeMarkdownPaths(fullPath, relativePath));
25
+ continue;
26
+ }
27
+ if (entry.isFile() && entry.name.endsWith('.md')) {
28
+ results.push(relativePath.replace(/\\/g, '/'));
29
+ }
30
+ }
31
+ return results;
32
+ }
33
+ exports.listCommand = new commander_1.Command('list')
34
+ .description('List available FRAIM jobs in the project')
35
+ .action(() => {
36
+ const projectRoot = process.cwd();
37
+ const employeeJobsDir = (0, project_fraim_paths_1.getWorkspaceFraimPath)(projectRoot, 'ai-employee', 'jobs');
38
+ const managerJobsDir = (0, project_fraim_paths_1.getWorkspaceFraimPath)(projectRoot, 'ai-manager', 'jobs');
39
+ if (!(0, project_fraim_paths_1.workspaceFraimExists)(projectRoot) || (!fs_1.default.existsSync(employeeJobsDir) && !fs_1.default.existsSync(managerJobsDir))) {
40
+ console.log(chalk_1.default.yellow(`No local job stubs found under ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('ai-employee/jobs')} or ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('ai-manager/jobs')}. Run "fraim setup" or "fraim init-project" first.`));
41
+ return;
42
+ }
43
+ const stubs = [
44
+ ...collectRelativeMarkdownPaths(employeeJobsDir).map(stub => `ai-employee/${stub}`),
45
+ ...collectRelativeMarkdownPaths(managerJobsDir).map(stub => `ai-manager/${stub}`)
46
+ ];
47
+ if (stubs.length === 0) {
48
+ console.log(chalk_1.default.gray('No jobs found.'));
49
+ return;
50
+ }
51
+ console.log(chalk_1.default.blue(`${stubs.length} Local Jobs Found:`));
52
+ stubs.sort().forEach(stub => {
53
+ const jobName = stub.replace('.md', '');
54
+ console.log(chalk_1.default.white(` - ${jobName}`));
55
+ });
56
+ console.log(chalk_1.default.gray('\nTip: Use these stubs for AI agent discoverability.'));
57
+ });
@@ -0,0 +1,84 @@
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.loginCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const device_flow_service_1 = require("../internal/device-flow-service");
10
+ const provider_client_1 = require("../api/provider-client");
11
+ const script_sync_utils_1 = require("../utils/script-sync-utils");
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const version_utils_1 = require("../utils/version-utils");
15
+ const auto_mcp_setup_1 = require("../setup/auto-mcp-setup");
16
+ exports.loginCommand = new commander_1.Command('login')
17
+ .description('Login to platforms (GitHub, etc.)');
18
+ exports.loginCommand
19
+ .command('github')
20
+ .description('Login to GitHub using Device Flow')
21
+ .action(async () => {
22
+ // Load the token to global config
23
+ const globalConfigDir = (0, script_sync_utils_1.getUserFraimDir)();
24
+ const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
25
+ let config = {};
26
+ if (fs_1.default.existsSync(globalConfigPath)) {
27
+ config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
28
+ }
29
+ if (!config.apiKey) {
30
+ console.error(chalk_1.default.red('\n❌ No FRAIM API key found. Please run "fraim setup" first.'));
31
+ process.exit(1);
32
+ }
33
+ const client = new provider_client_1.ProviderClient(config.apiKey);
34
+ let githubConfig;
35
+ try {
36
+ const providers = await client.getAllProviders();
37
+ githubConfig = providers.find(p => p.id === 'github')?.deviceFlowConfig;
38
+ if (!githubConfig) {
39
+ console.error(chalk_1.default.red('\n❌ Device flow configuration for GitHub not found on server.'));
40
+ process.exit(1);
41
+ }
42
+ }
43
+ catch (error) {
44
+ console.error(chalk_1.default.red(`\n❌ Failed to fetch provider configuration: ${error.message}`));
45
+ process.exit(1);
46
+ }
47
+ const deviceFlow = new device_flow_service_1.DeviceFlowService(githubConfig);
48
+ try {
49
+ const token = await deviceFlow.login();
50
+ config.tokens = {
51
+ ...(config.tokens || {}),
52
+ github: token
53
+ };
54
+ config.version = config.version || (0, version_utils_1.getFraimVersion)();
55
+ config.updatedAt = new Date().toISOString();
56
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
57
+ console.log(chalk_1.default.green('\n✅ GitHub token saved to global configuration.'));
58
+ // Trigger MCP reconfiguration
59
+ if (config.apiKey) {
60
+ console.log(chalk_1.default.blue('🔌 Updating MCP server configurations...'));
61
+ const mcpTokens = {};
62
+ Object.entries(config.tokens).forEach(([id, t]) => {
63
+ mcpTokens[id] = t;
64
+ });
65
+ // Re-use existing provider configs if any
66
+ const providerConfigsMap = {};
67
+ if (config.providerConfigs) {
68
+ Object.entries(config.providerConfigs).forEach(([key, val]) => {
69
+ const providerId = key.replace('Config', '');
70
+ providerConfigsMap[providerId] = val;
71
+ });
72
+ }
73
+ await (0, auto_mcp_setup_1.autoConfigureMCP)(config.apiKey, mcpTokens, undefined, providerConfigsMap);
74
+ console.log(chalk_1.default.green('✅ MCP servers updated with new GitHub token.'));
75
+ }
76
+ else {
77
+ console.log(chalk_1.default.yellow('⚠️ No FRAIM API key found. Run "fraim setup" to complete configuration.'));
78
+ }
79
+ }
80
+ catch (error) {
81
+ console.error(chalk_1.default.red(`\n❌ Login failed: ${error.message}`));
82
+ process.exit(1);
83
+ }
84
+ });
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mcpCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const stdio_server_1 = require("../../local-mcp-server/stdio-server");
6
+ /**
7
+ * mcpCommand - Entry point for starting the local FRAIM MCP server.
8
+ * This integrates the local-mcp-server into the main CLI.
9
+ */
10
+ exports.mcpCommand = new commander_1.Command('mcp')
11
+ .description('Start the local FRAIM MCP server (stdio)')
12
+ .action(() => {
13
+ const server = new stdio_server_1.FraimLocalMCPServer();
14
+ server.start();
15
+ });
@@ -0,0 +1,42 @@
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.migrateProjectFraimCommand = exports.runMigrateProjectFraim = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const project_fraim_migration_1 = require("../../core/utils/project-fraim-migration");
10
+ const runMigrateProjectFraim = async () => {
11
+ const result = (0, project_fraim_migration_1.migrateLegacyProjectFraimDir)(process.cwd());
12
+ if (!result.legacyDirFound) {
13
+ console.log(chalk_1.default.gray('No project .fraim directory found. Nothing to migrate.'));
14
+ return;
15
+ }
16
+ if (result.migratedPaths.length === 0 && result.prunedLegacyPaths.length === 0 && result.conflictPaths.length === 0) {
17
+ console.log(chalk_1.default.gray('No migratable legacy FRAIM content found in project .fraim/.'));
18
+ return;
19
+ }
20
+ if (result.migratedPaths.length > 0) {
21
+ console.log(chalk_1.default.green(`Migrated into fraim/: ${result.migratedPaths.join(', ')}`));
22
+ }
23
+ if (result.prunedLegacyPaths.length > 0) {
24
+ console.log(chalk_1.default.green(`Removed legacy synced content from project .fraim/: ${result.prunedLegacyPaths.join(', ')}`));
25
+ }
26
+ if (result.conflictPaths.length > 0) {
27
+ console.log(chalk_1.default.yellow(`Conflicts left in project .fraim/: ${result.conflictPaths.join(', ')}`));
28
+ console.log(chalk_1.default.yellow('Review those files and make sure the canonical version in fraim/ is correct before deleting project .fraim/.'));
29
+ return;
30
+ }
31
+ if (result.legacyDirReadyForDeletion) {
32
+ console.log(chalk_1.default.green('Migration complete. Everything that FRAIM still uses is now under fraim/.'));
33
+ console.log(chalk_1.default.yellow('You can now delete the project .fraim/ folder.'));
34
+ return;
35
+ }
36
+ console.log(chalk_1.default.green('Migration complete. Everything that FRAIM still uses is now under fraim/.'));
37
+ console.log(chalk_1.default.yellow('After you verify the migrated files, delete the project .fraim/ folder.'));
38
+ };
39
+ exports.runMigrateProjectFraim = runMigrateProjectFraim;
40
+ exports.migrateProjectFraimCommand = new commander_1.Command('migrate-project-fraim')
41
+ .description('One-time migration from project .fraim/ to fraim/')
42
+ .action(exports.runMigrateProjectFraim);
@@ -0,0 +1,177 @@
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.overrideCommand = 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 axios_1 = __importDefault(require("axios"));
12
+ const git_utils_1 = require("../../core/utils/git-utils");
13
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
14
+ exports.overrideCommand = new commander_1.Command('override')
15
+ .description('Create a local override for a FRAIM registry file')
16
+ .argument('<path>', 'Registry path to override (e.g., jobs/product-building/spec.md)')
17
+ .option('--inherit', 'Create override with {{ import }} directive (inherits from global)')
18
+ .option('--copy', 'Copy current content from server to local')
19
+ .option('--local', 'Fetch from local development server (port derived from git branch)')
20
+ .action(async (registryPath, options) => {
21
+ const projectRoot = process.cwd();
22
+ const configPath = (0, project_fraim_paths_1.getWorkspaceConfigPath)(projectRoot);
23
+ const personalizedRootDisplayPath = (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('personalized-employee');
24
+ if (!(0, project_fraim_paths_1.workspaceFraimExists)(projectRoot)) {
25
+ console.log(chalk_1.default.red(`❌ ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)()} directory not found. Run "fraim setup" or "fraim init-project" first.`));
26
+ process.exit(1);
27
+ }
28
+ if (registryPath.includes('..') || path_1.default.isAbsolute(registryPath)) {
29
+ console.log(chalk_1.default.red('❌ Invalid path. Path must be relative and cannot contain ".."'));
30
+ process.exit(1);
31
+ }
32
+ if (options.inherit && options.copy) {
33
+ console.log(chalk_1.default.red('❌ Cannot use both --inherit and --copy. Choose one.'));
34
+ process.exit(1);
35
+ }
36
+ if (!options.inherit && !options.copy) {
37
+ console.log(chalk_1.default.red('❌ Must specify either --inherit or --copy.'));
38
+ process.exit(1);
39
+ }
40
+ const overridePath = (0, project_fraim_paths_1.getWorkspaceFraimPath)(projectRoot, 'personalized-employee', registryPath);
41
+ const overrideDir = path_1.default.dirname(overridePath);
42
+ if (!fs_1.default.existsSync(overrideDir)) {
43
+ fs_1.default.mkdirSync(overrideDir, { recursive: true });
44
+ }
45
+ if (fs_1.default.existsSync(overridePath)) {
46
+ console.log(chalk_1.default.yellow(`⚠️ Override already exists: ${registryPath}`));
47
+ console.log(chalk_1.default.gray(` Location: ${overridePath}`));
48
+ process.exit(0);
49
+ }
50
+ let content;
51
+ if (options.inherit) {
52
+ content = `{{ import: ${registryPath} }}\n\n<!-- Add your custom content below -->\n`;
53
+ console.log(chalk_1.default.blue(`Creating inherited override for: ${registryPath}`));
54
+ }
55
+ else {
56
+ const isLocal = options.local || false;
57
+ const serverType = isLocal ? 'local server' : 'remote server';
58
+ console.log(chalk_1.default.blue(`Fetching current content from ${serverType}: ${registryPath}`));
59
+ try {
60
+ let config = {};
61
+ if (fs_1.default.existsSync(configPath)) {
62
+ config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
63
+ }
64
+ let serverUrl;
65
+ if (isLocal) {
66
+ const localPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) : (0, git_utils_1.getPort)();
67
+ serverUrl = `http://localhost:${localPort}`;
68
+ }
69
+ else {
70
+ serverUrl = config.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
71
+ }
72
+ const apiKey = isLocal ? 'local-dev' : (config.apiKey || process.env.FRAIM_API_KEY);
73
+ const headers = { 'Content-Type': 'application/json' };
74
+ if (apiKey) {
75
+ headers['x-api-key'] = apiKey;
76
+ }
77
+ const connectRequest = {
78
+ jsonrpc: '2.0',
79
+ id: 0,
80
+ method: 'tools/call',
81
+ params: {
82
+ name: 'fraim_connect',
83
+ arguments: {
84
+ agent: { name: 'fraim-cli', model: 'override-command' },
85
+ machine: {
86
+ hostname: require('os').hostname(),
87
+ platform: process.platform,
88
+ memory: require('os').totalmem(),
89
+ cpus: require('os').cpus().length
90
+ },
91
+ repo: {
92
+ url: config.repository?.url || 'https://github.com/unknown/unknown',
93
+ owner: config.repository?.owner || 'unknown',
94
+ name: config.repository?.name || 'unknown',
95
+ branch: 'main'
96
+ }
97
+ }
98
+ }
99
+ };
100
+ const connectResponse = await axios_1.default.post(`${serverUrl}/mcp`, connectRequest, { headers, timeout: 30000 });
101
+ const sessionId = connectResponse.data?.result?.sessionId;
102
+ if (typeof sessionId !== 'string' || sessionId.trim().length === 0) {
103
+ throw new Error('fraim_connect did not return a sessionId');
104
+ }
105
+ let toolName;
106
+ let toolArgs;
107
+ if (registryPath.startsWith('workflows/')) {
108
+ const parts = registryPath.split('/');
109
+ const workflowName = parts[parts.length - 1].replace('.md', '');
110
+ toolName = 'get_fraim_workflow';
111
+ toolArgs = { workflow: workflowName };
112
+ }
113
+ else if (registryPath.startsWith('jobs/')) {
114
+ const parts = registryPath.split('/');
115
+ const jobName = parts[parts.length - 1].replace('.md', '');
116
+ toolName = 'get_fraim_job';
117
+ toolArgs = { job: jobName };
118
+ }
119
+ else {
120
+ toolName = 'get_fraim_file';
121
+ toolArgs = { path: registryPath };
122
+ }
123
+ console.log(chalk_1.default.gray(` Using MCP tool: ${toolName}`));
124
+ const mcpRequest = {
125
+ jsonrpc: '2.0',
126
+ id: 1,
127
+ method: 'tools/call',
128
+ params: {
129
+ name: toolName,
130
+ sessionId: sessionId.trim(),
131
+ arguments: {
132
+ ...toolArgs,
133
+ sessionId: sessionId.trim()
134
+ }
135
+ }
136
+ };
137
+ const response = await axios_1.default.post(`${serverUrl}/mcp`, mcpRequest, {
138
+ headers,
139
+ timeout: 30000
140
+ });
141
+ if (response.data.error) {
142
+ throw new Error(response.data.error.message || 'MCP request failed');
143
+ }
144
+ const result = response.data.result;
145
+ if (!result || !result.content || !Array.isArray(result.content)) {
146
+ throw new Error('Unexpected MCP response format');
147
+ }
148
+ const textContent = result.content.find((c) => c.type === 'text');
149
+ if (!textContent || !textContent.text) {
150
+ throw new Error('No text content in MCP response');
151
+ }
152
+ content = textContent.text;
153
+ console.log(chalk_1.default.green(`✅ Fetched ${content.length} bytes from ${serverType}`));
154
+ }
155
+ catch (error) {
156
+ console.log(chalk_1.default.red(`❌ Failed to fetch content from ${serverType}: ${error.message}`));
157
+ if (isLocal) {
158
+ console.log(chalk_1.default.gray(' Tip: Make sure the FRAIM server is running locally (npm run start:fraim)'));
159
+ }
160
+ else {
161
+ console.log(chalk_1.default.gray(' Tip: Check your API key and network connection'));
162
+ console.log(chalk_1.default.gray(' Or use --local to fetch from a locally running server'));
163
+ }
164
+ process.exit(1);
165
+ }
166
+ }
167
+ try {
168
+ fs_1.default.writeFileSync(overridePath, content, 'utf-8');
169
+ console.log(chalk_1.default.green(`✅ Created override: ${registryPath}`));
170
+ console.log(chalk_1.default.gray(` Location: ${overridePath}`));
171
+ console.log(chalk_1.default.gray(` Overrides are stored in ${personalizedRootDisplayPath}`));
172
+ }
173
+ catch (error) {
174
+ console.log(chalk_1.default.red(`❌ Failed to write override file: ${error.message}`));
175
+ process.exit(1);
176
+ }
177
+ });