fraim-framework 2.0.95 → 2.0.97

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 (40) hide show
  1. package/bin/fraim.js +1 -1
  2. package/dist/src/cli/commands/add-ide.js +1 -1
  3. package/dist/src/cli/commands/doctor.js +6 -6
  4. package/dist/src/cli/commands/init-project.js +63 -52
  5. package/dist/src/cli/commands/list-overridable.js +33 -55
  6. package/dist/src/cli/commands/list.js +35 -9
  7. package/dist/src/cli/commands/migrate-project-fraim.js +42 -0
  8. package/dist/src/cli/commands/override.js +18 -39
  9. package/dist/src/cli/commands/setup.js +1 -1
  10. package/dist/src/cli/commands/sync.js +34 -27
  11. package/dist/src/cli/doctor/check-runner.js +3 -3
  12. package/dist/src/cli/doctor/checks/global-setup-checks.js +13 -13
  13. package/dist/src/cli/doctor/checks/project-setup-checks.js +12 -12
  14. package/dist/src/cli/doctor/checks/scripts-checks.js +2 -2
  15. package/dist/src/cli/doctor/checks/workflow-checks.js +56 -60
  16. package/dist/src/cli/doctor/reporters/console-reporter.js +1 -1
  17. package/dist/src/cli/fraim.js +3 -1
  18. package/dist/src/cli/mcp/mcp-server-registry.js +1 -1
  19. package/dist/src/cli/services/device-flow-service.js +83 -0
  20. package/dist/src/cli/setup/auto-mcp-setup.js +2 -2
  21. package/dist/src/cli/setup/first-run.js +4 -3
  22. package/dist/src/cli/utils/agent-adapters.js +126 -0
  23. package/dist/src/cli/utils/fraim-gitignore.js +15 -21
  24. package/dist/src/cli/utils/project-bootstrap.js +93 -0
  25. package/dist/src/cli/utils/remote-sync.js +20 -67
  26. package/dist/src/core/ai-mentor.js +31 -49
  27. package/dist/src/core/config-loader.js +57 -62
  28. package/dist/src/core/config-writer.js +75 -0
  29. package/dist/src/core/types.js +1 -1
  30. package/dist/src/core/utils/job-parser.js +176 -0
  31. package/dist/src/core/utils/local-registry-resolver.js +61 -71
  32. package/dist/src/core/utils/project-fraim-migration.js +103 -0
  33. package/dist/src/core/utils/project-fraim-paths.js +38 -0
  34. package/dist/src/core/utils/stub-generator.js +41 -75
  35. package/dist/src/core/utils/workflow-parser.js +5 -3
  36. package/dist/src/local-mcp-server/learning-context-builder.js +229 -0
  37. package/dist/src/local-mcp-server/stdio-server.js +124 -51
  38. package/dist/src/local-mcp-server/usage-collector.js +109 -27
  39. package/index.js +1 -1
  40. package/package.json +3 -4
@@ -10,27 +10,25 @@ const path_1 = __importDefault(require("path"));
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const axios_1 = __importDefault(require("axios"));
12
12
  const git_utils_1 = require("../../core/utils/git-utils");
13
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
13
14
  exports.overrideCommand = new commander_1.Command('override')
14
15
  .description('Create a local override for a FRAIM registry file')
15
- .argument('<path>', 'Registry path to override (e.g., workflows/product-building/spec.md)')
16
+ .argument('<path>', 'Registry path to override (e.g., jobs/product-building/spec.md)')
16
17
  .option('--inherit', 'Create override with {{ import }} directive (inherits from global)')
17
18
  .option('--copy', 'Copy current content from server to local')
18
19
  .option('--local', 'Fetch from local development server (port derived from git branch)')
19
20
  .action(async (registryPath, options) => {
20
21
  const projectRoot = process.cwd();
21
- const fraimDir = path_1.default.join(projectRoot, '.fraim');
22
- const configPath = path_1.default.join(fraimDir, 'config.json');
23
- // Validate .fraim directory exists
24
- if (!fs_1.default.existsSync(fraimDir)) {
25
- console.log(chalk_1.default.red('❌ .fraim/ directory not found. Run "fraim setup" or "fraim init-project" first.'));
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
26
  process.exit(1);
27
27
  }
28
- // Validate path format
29
28
  if (registryPath.includes('..') || path_1.default.isAbsolute(registryPath)) {
30
29
  console.log(chalk_1.default.red('❌ Invalid path. Path must be relative and cannot contain ".."'));
31
30
  process.exit(1);
32
31
  }
33
- // Ensure exactly one option is specified
34
32
  if (options.inherit && options.copy) {
35
33
  console.log(chalk_1.default.red('❌ Cannot use both --inherit and --copy. Choose one.'));
36
34
  process.exit(1);
@@ -39,13 +37,11 @@ exports.overrideCommand = new commander_1.Command('override')
39
37
  console.log(chalk_1.default.red('❌ Must specify either --inherit or --copy.'));
40
38
  process.exit(1);
41
39
  }
42
- // Create personalized override directory structure
43
- const overridePath = path_1.default.join(fraimDir, 'personalized-employee', registryPath);
40
+ const overridePath = (0, project_fraim_paths_1.getWorkspaceFraimPath)(projectRoot, 'personalized-employee', registryPath);
44
41
  const overrideDir = path_1.default.dirname(overridePath);
45
42
  if (!fs_1.default.existsSync(overrideDir)) {
46
43
  fs_1.default.mkdirSync(overrideDir, { recursive: true });
47
44
  }
48
- // Check if override already exists
49
45
  if (fs_1.default.existsSync(overridePath)) {
50
46
  console.log(chalk_1.default.yellow(`⚠️ Override already exists: ${registryPath}`));
51
47
  console.log(chalk_1.default.gray(` Location: ${overridePath}`));
@@ -53,22 +49,18 @@ exports.overrideCommand = new commander_1.Command('override')
53
49
  }
54
50
  let content;
55
51
  if (options.inherit) {
56
- // Create file with import directive
57
52
  content = `{{ import: ${registryPath} }}\n\n<!-- Add your custom content below -->\n`;
58
- console.log(chalk_1.default.blue(`📝 Creating inherited override for: ${registryPath}`));
53
+ console.log(chalk_1.default.blue(`Creating inherited override for: ${registryPath}`));
59
54
  }
60
55
  else {
61
- // Fetch current content from server using MCP protocol
62
56
  const isLocal = options.local || false;
63
57
  const serverType = isLocal ? 'local server' : 'remote server';
64
- console.log(chalk_1.default.blue(`📥 Fetching current content from ${serverType}: ${registryPath}`));
58
+ console.log(chalk_1.default.blue(`Fetching current content from ${serverType}: ${registryPath}`));
65
59
  try {
66
- // Read config to get remote URL
67
60
  let config = {};
68
61
  if (fs_1.default.existsSync(configPath)) {
69
62
  config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
70
63
  }
71
- // Determine server URL
72
64
  let serverUrl;
73
65
  if (isLocal) {
74
66
  const localPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) : (0, git_utils_1.getPort)();
@@ -78,13 +70,10 @@ exports.overrideCommand = new commander_1.Command('override')
78
70
  serverUrl = config.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
79
71
  }
80
72
  const apiKey = isLocal ? 'local-dev' : (config.apiKey || process.env.FRAIM_API_KEY);
81
- const headers = {
82
- 'Content-Type': 'application/json'
83
- };
73
+ const headers = { 'Content-Type': 'application/json' };
84
74
  if (apiKey) {
85
75
  headers['x-api-key'] = apiKey;
86
76
  }
87
- // First, establish session with fraim_connect
88
77
  const connectRequest = {
89
78
  jsonrpc: '2.0',
90
79
  id: 0,
@@ -113,19 +102,15 @@ exports.overrideCommand = new commander_1.Command('override')
113
102
  if (typeof sessionId !== 'string' || sessionId.trim().length === 0) {
114
103
  throw new Error('fraim_connect did not return a sessionId');
115
104
  }
116
- // Now determine MCP tool and arguments based on path
117
105
  let toolName;
118
106
  let toolArgs;
119
107
  if (registryPath.startsWith('workflows/')) {
120
- // Extract workflow name from path
121
- // e.g., "workflows/product-building/spec.md" -> "spec"
122
108
  const parts = registryPath.split('/');
123
109
  const workflowName = parts[parts.length - 1].replace('.md', '');
124
110
  toolName = 'get_fraim_workflow';
125
111
  toolArgs = { workflow: workflowName };
126
112
  }
127
113
  else if (registryPath.startsWith('jobs/')) {
128
- // e.g., "jobs/product-building/feature-specification.md" -> "feature-specification"
129
114
  const parts = registryPath.split('/');
130
115
  const jobName = parts[parts.length - 1].replace('.md', '');
131
116
  toolName = 'get_fraim_job';
@@ -136,7 +121,6 @@ exports.overrideCommand = new commander_1.Command('override')
136
121
  toolArgs = { path: registryPath };
137
122
  }
138
123
  console.log(chalk_1.default.gray(` Using MCP tool: ${toolName}`));
139
- // Make MCP JSON-RPC request
140
124
  const mcpRequest = {
141
125
  jsonrpc: '2.0',
142
126
  id: 1,
@@ -144,19 +128,16 @@ exports.overrideCommand = new commander_1.Command('override')
144
128
  params: {
145
129
  name: toolName,
146
130
  sessionId: sessionId.trim(),
147
- arguments: toolArgs
131
+ arguments: {
132
+ ...toolArgs,
133
+ sessionId: sessionId.trim()
134
+ }
148
135
  }
149
136
  };
150
- // Mirror sessionId in arguments for compatibility with both server and proxy paths.
151
- mcpRequest.params.arguments = {
152
- ...mcpRequest.params.arguments,
153
- sessionId: sessionId.trim()
154
- };
155
137
  const response = await axios_1.default.post(`${serverUrl}/mcp`, mcpRequest, {
156
138
  headers,
157
139
  timeout: 30000
158
140
  });
159
- // Extract content from MCP response
160
141
  if (response.data.error) {
161
142
  throw new Error(response.data.error.message || 'MCP request failed');
162
143
  }
@@ -164,7 +145,6 @@ exports.overrideCommand = new commander_1.Command('override')
164
145
  if (!result || !result.content || !Array.isArray(result.content)) {
165
146
  throw new Error('Unexpected MCP response format');
166
147
  }
167
- // Extract text content from MCP response
168
148
  const textContent = result.content.find((c) => c.type === 'text');
169
149
  if (!textContent || !textContent.text) {
170
150
  throw new Error('No text content in MCP response');
@@ -175,21 +155,20 @@ exports.overrideCommand = new commander_1.Command('override')
175
155
  catch (error) {
176
156
  console.log(chalk_1.default.red(`❌ Failed to fetch content from ${serverType}: ${error.message}`));
177
157
  if (isLocal) {
178
- console.log(chalk_1.default.gray(` Tip: Make sure the FRAIM server is running locally (npm run start:fraim)`));
158
+ console.log(chalk_1.default.gray(' Tip: Make sure the FRAIM server is running locally (npm run start:fraim)'));
179
159
  }
180
160
  else {
181
- console.log(chalk_1.default.gray(` Tip: Check your API key and network connection`));
182
- console.log(chalk_1.default.gray(` Or use --local to fetch from a locally running server`));
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'));
183
163
  }
184
164
  process.exit(1);
185
165
  }
186
166
  }
187
- // Write override file
188
167
  try {
189
168
  fs_1.default.writeFileSync(overridePath, content, 'utf-8');
190
169
  console.log(chalk_1.default.green(`✅ Created override: ${registryPath}`));
191
170
  console.log(chalk_1.default.gray(` Location: ${overridePath}`));
192
- console.log(chalk_1.default.gray(` Edit this file to customize for your project`));
171
+ console.log(chalk_1.default.gray(` Overrides are stored in ${personalizedRootDisplayPath}`));
193
172
  }
194
173
  catch (error) {
195
174
  console.log(chalk_1.default.red(`❌ Failed to write override file: ${error.message}`));
@@ -594,7 +594,7 @@ const runSetup = async (options) => {
594
594
  console.log(chalk_1.default.cyan('\n📝 For future projects:'));
595
595
  console.log(chalk_1.default.cyan(' 1. cd into any project directory'));
596
596
  console.log(chalk_1.default.cyan(' 2. Run: fraim init-project'));
597
- console.log(chalk_1.default.cyan(' 3. Ask your AI agent "list fraim workflows"'));
597
+ console.log(chalk_1.default.cyan(' 3. Tell your AI agent: "Onboard this project"'));
598
598
  if (mode === 'integrated') {
599
599
  const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
600
600
  const unconfiguredProviders = allProviderIds.filter(id => !tokens[id]);
@@ -45,6 +45,8 @@ const config_loader_1 = require("../../core/config-loader");
45
45
  const version_utils_1 = require("../utils/version-utils");
46
46
  const script_sync_utils_1 = require("../utils/script-sync-utils");
47
47
  const git_utils_1 = require("../../core/utils/git-utils");
48
+ const agent_adapters_1 = require("../utils/agent-adapters");
49
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
48
50
  /**
49
51
  * Load API key from user-level config (~/.fraim/config.json)
50
52
  */
@@ -57,8 +59,8 @@ function loadUserApiKey() {
57
59
  const userConfig = JSON.parse(fs_1.default.readFileSync(userConfigPath, 'utf8'));
58
60
  return userConfig.apiKey;
59
61
  }
60
- catch (error) {
61
- console.warn(chalk_1.default.yellow('⚠️ Failed to read user-level config for API key'));
62
+ catch {
63
+ console.warn(chalk_1.default.yellow('Failed to read user-level config for API key'));
62
64
  return undefined;
63
65
  }
64
66
  }
@@ -73,29 +75,27 @@ function updateVersionInConfig(fraimDir) {
73
75
  if (currentConfig.version !== newVersion) {
74
76
  currentConfig.version = newVersion;
75
77
  fs_1.default.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2));
76
- console.log(chalk_1.default.green(`✅ Updated FRAIM version to ${newVersion} in config.`));
78
+ console.log(chalk_1.default.green(`Updated FRAIM version to ${newVersion} in config.`));
77
79
  }
78
80
  }
79
- catch (e) {
80
- console.warn(chalk_1.default.yellow('⚠️ Could not update version in config.json.'));
81
+ catch {
82
+ console.warn(chalk_1.default.yellow('Could not update version in config.json.'));
81
83
  }
82
84
  }
83
85
  const runSync = async (options) => {
84
86
  const projectRoot = process.cwd();
85
87
  const config = (0, config_loader_1.loadFraimConfig)();
86
- const fraimDir = path_1.default.join(projectRoot, '.fraim');
87
- // Check if running via npx
88
+ const fraimDir = (0, project_fraim_paths_1.getWorkspaceFraimDir)(projectRoot);
88
89
  const isNpx = process.env.npm_config_prefix === undefined || process.env.npm_lifecycle_event === 'npx';
89
90
  const isGlobal = !isNpx && (process.env.npm_config_global === 'true' || process.env.npm_config_prefix);
90
91
  if (isGlobal && !options.skipUpdates) {
91
- console.log(chalk_1.default.yellow('⚠️ You are running a global installation of FRAIM.'));
92
- console.log(chalk_1.default.gray(' Updates are not automatic in this mode.'));
93
- console.log(chalk_1.default.cyan(' 💡 Recommended: Use "npx fraim-framework@latest sync" instead.\n'));
92
+ console.log(chalk_1.default.yellow('You are running a global installation of FRAIM.'));
93
+ console.log(chalk_1.default.gray('Updates are not automatic in this mode.'));
94
+ console.log(chalk_1.default.cyan('Recommended: Use "npx fraim-framework@latest sync" instead.\n'));
94
95
  }
95
96
  const { syncFromRemote } = await Promise.resolve().then(() => __importStar(require('../utils/remote-sync')));
96
- // Path 1: --local flag → hit localhost MCP server
97
97
  if (options.local) {
98
- console.log(chalk_1.default.blue('🔄 Syncing FRAIM workflows from local server...'));
98
+ console.log(chalk_1.default.blue('Syncing FRAIM jobs from local server...'));
99
99
  const localPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) : (0, git_utils_1.getPort)();
100
100
  const result = await syncFromRemote({
101
101
  remoteUrl: `http://localhost:${localPort}`,
@@ -104,51 +104,58 @@ const runSync = async (options) => {
104
104
  skipUpdates: true
105
105
  });
106
106
  if (result.success) {
107
- console.log(chalk_1.default.green(`✅ Successfully synced ${result.workflowsSynced} workflows, ${result.employeeJobsSynced} ai-employee jobs, ${result.managerJobsSynced} ai-manager jobs, ${result.skillsSynced} skills, ${result.rulesSynced} rules, ${result.scriptsSynced} scripts, and ${result.docsSynced} docs from local server`));
107
+ console.log(chalk_1.default.green(`Successfully synced ${result.employeeJobsSynced} ai-employee jobs, ${result.managerJobsSynced} ai-manager jobs, ${result.skillsSynced} skills, ${result.rulesSynced} rules, ${result.scriptsSynced} scripts, and ${result.docsSynced} docs from local server`));
108
108
  updateVersionInConfig(fraimDir);
109
+ const adapterUpdates = (0, agent_adapters_1.ensureAgentAdapterFiles)(projectRoot);
110
+ if (adapterUpdates.length > 0) {
111
+ console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${adapterUpdates.join(', ')}`));
112
+ }
109
113
  return;
110
114
  }
111
- console.error(chalk_1.default.red(`❌ Local sync failed: ${result.error}`));
112
- console.error(chalk_1.default.yellow('💡 Make sure the FRAIM MCP server is running locally (npm run dev).'));
115
+ console.error(chalk_1.default.red(`Local sync failed: ${result.error}`));
116
+ console.error(chalk_1.default.yellow('Make sure the FRAIM MCP server is running locally (npm run dev).'));
113
117
  process.exit(1);
114
118
  }
115
- // Path 2: Remote sync with API key
116
119
  let apiKey = loadUserApiKey() || config.apiKey || process.env.FRAIM_API_KEY;
117
120
  if (!apiKey) {
118
121
  if (process.env.TEST_MODE === 'true') {
119
- console.log(chalk_1.default.yellow('⚠️ TEST_MODE: No API key configured. Using test placeholder key.'));
122
+ console.log(chalk_1.default.yellow('TEST_MODE: No API key configured. Using test placeholder key.'));
120
123
  apiKey = 'test-mode-key';
121
124
  }
122
125
  else {
123
- console.error(chalk_1.default.red('No API key configured. Cannot sync.'));
124
- console.error(chalk_1.default.yellow('💡 Set FRAIM_API_KEY in your environment, or add apiKey to ~/.fraim/config.json or .fraim/config.json'));
125
- console.error(chalk_1.default.yellow('💡 Or use --local to sync from a locally running FRAIM server.'));
126
+ console.error(chalk_1.default.red('No API key configured. Cannot sync.'));
127
+ console.error(chalk_1.default.yellow(`Set FRAIM_API_KEY in your environment, or add apiKey to ~/.fraim/config.json or ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`));
128
+ console.error(chalk_1.default.yellow('Or use --local to sync from a locally running FRAIM server.'));
126
129
  process.exit(1);
127
130
  }
128
131
  }
129
- console.log(chalk_1.default.blue('🔄 Syncing FRAIM workflows from remote server...'));
132
+ console.log(chalk_1.default.blue('Syncing FRAIM jobs from remote server...'));
130
133
  const result = await syncFromRemote({
131
134
  remoteUrl: config.remoteUrl,
132
- apiKey: apiKey,
135
+ apiKey,
133
136
  projectRoot,
134
137
  skipUpdates: options.skipUpdates || false
135
138
  });
136
139
  if (!result.success) {
137
- console.error(chalk_1.default.red(`❌ Remote sync failed: ${result.error}`));
138
- console.error(chalk_1.default.yellow('💡 Check your API key and network connection.'));
140
+ console.error(chalk_1.default.red(`Remote sync failed: ${result.error}`));
141
+ console.error(chalk_1.default.yellow('Check your API key and network connection.'));
139
142
  if (process.env.TEST_MODE === 'true') {
140
- console.log(chalk_1.default.yellow('⚠️ TEST_MODE: Continuing without remote sync (server may be unavailable).'));
143
+ console.log(chalk_1.default.yellow('TEST_MODE: Continuing without remote sync (server may be unavailable).'));
141
144
  updateVersionInConfig(fraimDir);
142
145
  return;
143
146
  }
144
147
  process.exit(1);
145
148
  }
146
- console.log(chalk_1.default.green(`✅ Successfully synced ${result.workflowsSynced} workflows, ${result.employeeJobsSynced} ai-employee jobs, ${result.managerJobsSynced} ai-manager jobs, ${result.skillsSynced} skills, ${result.rulesSynced} rules, ${result.scriptsSynced} scripts, and ${result.docsSynced} docs from remote`));
149
+ console.log(chalk_1.default.green(`Successfully synced ${result.employeeJobsSynced} ai-employee jobs, ${result.managerJobsSynced} ai-manager jobs, ${result.skillsSynced} skills, ${result.rulesSynced} rules, ${result.scriptsSynced} scripts, and ${result.docsSynced} docs from remote`));
147
150
  updateVersionInConfig(fraimDir);
151
+ const adapterUpdates = (0, agent_adapters_1.ensureAgentAdapterFiles)(projectRoot);
152
+ if (adapterUpdates.length > 0) {
153
+ console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${adapterUpdates.join(', ')}`));
154
+ }
148
155
  };
149
156
  exports.runSync = runSync;
150
157
  exports.syncCommand = new commander_1.Command('sync')
151
- .description('Sync workflow stubs from the framework registry')
158
+ .description('Sync job stubs from the framework registry')
152
159
  .option('-f, --force', 'Force sync even if digest matches')
153
160
  .option('--skip-updates', 'Skip checking for CLI updates (legacy)')
154
161
  .option('--local', 'Sync from local development server (port derived from git branch)')
@@ -76,7 +76,7 @@ async function runCheckWithTimeout(check, timeout = CHECK_TIMEOUT) {
76
76
  */
77
77
  function filterChecks(checks, options) {
78
78
  // If no specific flags, run all checks
79
- if (!options.testMcp && !options.testConfig && !options.testWorkflows) {
79
+ if (!options.testMcp && !options.testConfig && !options.testJobs) {
80
80
  return checks;
81
81
  }
82
82
  return checks.filter(check => {
@@ -84,7 +84,7 @@ function filterChecks(checks, options) {
84
84
  return true;
85
85
  if (options.testConfig && (check.category === 'globalSetup' || check.category === 'projectSetup' || check.category === 'ideConfiguration'))
86
86
  return true;
87
- if (options.testWorkflows && check.category === 'workflows')
87
+ if (options.testJobs && check.category === 'jobs')
88
88
  return true;
89
89
  return false;
90
90
  });
@@ -127,7 +127,7 @@ async function runChecks(checks, options, version) {
127
127
  const categories = {
128
128
  globalSetup: { checks: [] },
129
129
  projectSetup: { checks: [] },
130
- workflows: { checks: [] },
130
+ jobs: { checks: [] },
131
131
  ideConfiguration: { checks: [] },
132
132
  mcpConnectivity: { checks: [] },
133
133
  scripts: { checks: [] }
@@ -12,7 +12,7 @@ exports.getGlobalSetupChecks = getGlobalSetupChecks;
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const os_1 = __importDefault(require("os"));
15
- const GLOBAL_CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
15
+ const getGlobalConfigPath = () => path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
16
16
  /**
17
17
  * Check if global config exists
18
18
  */
@@ -22,11 +22,11 @@ function checkGlobalConfigExists() {
22
22
  category: 'globalSetup',
23
23
  critical: true,
24
24
  run: async () => {
25
- if (fs_1.default.existsSync(GLOBAL_CONFIG_PATH)) {
25
+ if (fs_1.default.existsSync(getGlobalConfigPath())) {
26
26
  return {
27
27
  status: 'passed',
28
28
  message: 'Global config exists',
29
- details: { path: GLOBAL_CONFIG_PATH }
29
+ details: { path: getGlobalConfigPath() }
30
30
  };
31
31
  }
32
32
  return {
@@ -48,13 +48,13 @@ function checkModeValid() {
48
48
  critical: false,
49
49
  run: async () => {
50
50
  try {
51
- if (!fs_1.default.existsSync(GLOBAL_CONFIG_PATH)) {
51
+ if (!fs_1.default.existsSync(getGlobalConfigPath())) {
52
52
  return {
53
53
  status: 'error',
54
54
  message: 'Cannot check mode - global config missing'
55
55
  };
56
56
  }
57
- const config = JSON.parse(fs_1.default.readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
57
+ const config = JSON.parse(fs_1.default.readFileSync(getGlobalConfigPath(), 'utf8'));
58
58
  const mode = config.mode || 'conversational';
59
59
  const validModes = ['conversational', 'integrated', 'split'];
60
60
  if (validModes.includes(mode)) {
@@ -91,13 +91,13 @@ function checkApiKeyConfigured() {
91
91
  critical: false,
92
92
  run: async () => {
93
93
  try {
94
- if (!fs_1.default.existsSync(GLOBAL_CONFIG_PATH)) {
94
+ if (!fs_1.default.existsSync(getGlobalConfigPath())) {
95
95
  return {
96
96
  status: 'error',
97
97
  message: 'Cannot check API key - global config missing'
98
98
  };
99
99
  }
100
- const config = JSON.parse(fs_1.default.readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
100
+ const config = JSON.parse(fs_1.default.readFileSync(getGlobalConfigPath(), 'utf8'));
101
101
  if (config.apiKey) {
102
102
  const maskedKey = config.apiKey.substring(0, 10) + '...';
103
103
  return {
@@ -110,7 +110,7 @@ function checkApiKeyConfigured() {
110
110
  status: 'warning',
111
111
  message: 'API key not configured',
112
112
  suggestion: 'Add apiKey to global config for remote features',
113
- details: { configPath: GLOBAL_CONFIG_PATH }
113
+ details: { configPath: getGlobalConfigPath() }
114
114
  };
115
115
  }
116
116
  catch (error) {
@@ -133,13 +133,13 @@ function checkGitHubTokenConfigured() {
133
133
  critical: false,
134
134
  run: async () => {
135
135
  try {
136
- if (!fs_1.default.existsSync(GLOBAL_CONFIG_PATH)) {
136
+ if (!fs_1.default.existsSync(getGlobalConfigPath())) {
137
137
  return {
138
138
  status: 'error',
139
139
  message: 'Cannot check GitHub token - global config missing'
140
140
  };
141
141
  }
142
- const config = JSON.parse(fs_1.default.readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
142
+ const config = JSON.parse(fs_1.default.readFileSync(getGlobalConfigPath(), 'utf8'));
143
143
  // Check if repository provider is GitHub
144
144
  if (config.repository?.provider === 'github') {
145
145
  // Token would be in IDE MCP configs, not global config
@@ -175,13 +175,13 @@ function checkRemoteUrlConfigured() {
175
175
  critical: false,
176
176
  run: async () => {
177
177
  try {
178
- if (!fs_1.default.existsSync(GLOBAL_CONFIG_PATH)) {
178
+ if (!fs_1.default.existsSync(getGlobalConfigPath())) {
179
179
  return {
180
180
  status: 'error',
181
181
  message: 'Cannot check remote URL - global config missing'
182
182
  };
183
183
  }
184
- const config = JSON.parse(fs_1.default.readFileSync(GLOBAL_CONFIG_PATH, 'utf8'));
184
+ const config = JSON.parse(fs_1.default.readFileSync(getGlobalConfigPath(), 'utf8'));
185
185
  if (config.remoteUrl) {
186
186
  return {
187
187
  status: 'passed',
@@ -193,7 +193,7 @@ function checkRemoteUrlConfigured() {
193
193
  return {
194
194
  status: 'passed',
195
195
  message: 'Remote URL not configured (optional)',
196
- details: { configPath: GLOBAL_CONFIG_PATH, optional: true }
196
+ details: { configPath: getGlobalConfigPath(), optional: true }
197
197
  };
198
198
  }
199
199
  catch (error) {
@@ -10,8 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.getProjectSetupChecks = getProjectSetupChecks;
12
12
  const fs_1 = __importDefault(require("fs"));
13
- const path_1 = __importDefault(require("path"));
14
13
  const child_process_1 = require("child_process");
14
+ const project_fraim_paths_1 = require("../../../core/utils/project-fraim-paths");
15
15
  /**
16
16
  * Check if project is initialized
17
17
  */
@@ -21,8 +21,8 @@ function checkProjectInitialized() {
21
21
  category: 'projectSetup',
22
22
  critical: true,
23
23
  run: async () => {
24
- const fraimDir = path_1.default.join(process.cwd(), '.fraim');
25
- if (fs_1.default.existsSync(fraimDir)) {
24
+ const fraimDir = (0, project_fraim_paths_1.getWorkspaceFraimDir)(process.cwd());
25
+ if ((0, project_fraim_paths_1.workspaceFraimExists)(process.cwd())) {
26
26
  return {
27
27
  status: 'passed',
28
28
  message: 'Project initialized',
@@ -47,7 +47,7 @@ function checkProjectConfigValid() {
47
47
  category: 'projectSetup',
48
48
  critical: true,
49
49
  run: async () => {
50
- const configPath = path_1.default.join(process.cwd(), '.fraim', 'config.json');
50
+ const configPath = (0, project_fraim_paths_1.getWorkspaceConfigPath)(process.cwd());
51
51
  if (!fs_1.default.existsSync(configPath)) {
52
52
  return {
53
53
  status: 'error',
@@ -62,7 +62,7 @@ function checkProjectConfigValid() {
62
62
  return {
63
63
  status: 'warning',
64
64
  message: 'Project config incomplete',
65
- suggestion: 'Add project name to .fraim/config.json'
65
+ suggestion: `Add project name to ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`
66
66
  };
67
67
  }
68
68
  return {
@@ -75,7 +75,7 @@ function checkProjectConfigValid() {
75
75
  return {
76
76
  status: 'error',
77
77
  message: 'Project config corrupted',
78
- suggestion: 'Fix JSON syntax in .fraim/config.json',
78
+ suggestion: `Fix JSON syntax in ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`,
79
79
  details: { error: error.message }
80
80
  };
81
81
  }
@@ -129,7 +129,7 @@ function checkProviderMatches() {
129
129
  category: 'projectSetup',
130
130
  critical: false,
131
131
  run: async () => {
132
- const configPath = path_1.default.join(process.cwd(), '.fraim', 'config.json');
132
+ const configPath = (0, project_fraim_paths_1.getWorkspaceConfigPath)(process.cwd());
133
133
  if (!fs_1.default.existsSync(configPath)) {
134
134
  return {
135
135
  status: 'error',
@@ -163,7 +163,7 @@ function checkProviderMatches() {
163
163
  return {
164
164
  status: 'warning',
165
165
  message: `Provider mismatch: config says ${provider} but remote is ${remote}`,
166
- suggestion: 'Update repository.provider in .fraim/config.json'
166
+ suggestion: `Update repository.provider in ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`
167
167
  };
168
168
  }
169
169
  catch {
@@ -193,7 +193,7 @@ function checkConfigMatchesMode() {
193
193
  category: 'projectSetup',
194
194
  critical: false,
195
195
  run: async () => {
196
- const configPath = path_1.default.join(process.cwd(), '.fraim', 'config.json');
196
+ const configPath = (0, project_fraim_paths_1.getWorkspaceConfigPath)(process.cwd());
197
197
  if (!fs_1.default.existsSync(configPath)) {
198
198
  return {
199
199
  status: 'error',
@@ -217,7 +217,7 @@ function checkConfigMatchesMode() {
217
217
  return {
218
218
  status: 'error',
219
219
  message: 'Integrated mode requires repository configuration',
220
- suggestion: 'Add repository config to .fraim/config.json'
220
+ suggestion: `Add repository config to ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`
221
221
  };
222
222
  }
223
223
  return {
@@ -232,14 +232,14 @@ function checkConfigMatchesMode() {
232
232
  return {
233
233
  status: 'error',
234
234
  message: 'Split mode requires repository configuration',
235
- suggestion: 'Add repository config to .fraim/config.json'
235
+ suggestion: `Add repository config to ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`
236
236
  };
237
237
  }
238
238
  if (!config.issueTracking?.provider) {
239
239
  return {
240
240
  status: 'error',
241
241
  message: 'Split mode requires issue tracking configuration',
242
- suggestion: 'Add issueTracking config to .fraim/config.json'
242
+ suggestion: `Add issueTracking config to ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`
243
243
  };
244
244
  }
245
245
  return {
@@ -32,7 +32,7 @@ function checkScriptsDirectoryExists() {
32
32
  return {
33
33
  status: 'warning',
34
34
  message: 'Scripts directory missing',
35
- suggestion: 'Run fraim sync to create scripts directory',
35
+ suggestion: 'Run fraim sync to hydrate local scripts for the repo.',
36
36
  command: 'fraim sync'
37
37
  };
38
38
  }
@@ -65,7 +65,7 @@ function checkScriptsSynced() {
65
65
  return {
66
66
  status: 'warning',
67
67
  message: 'No scripts found',
68
- suggestion: 'Run fraim sync to fetch scripts',
68
+ suggestion: 'Run fraim sync to hydrate local scripts.',
69
69
  command: 'fraim sync'
70
70
  };
71
71
  }