fraim-framework 2.0.116 → 2.0.119

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.
@@ -268,7 +268,8 @@ const listSupportedIDEs = () => {
268
268
  console.log(chalk_1.default.gray(` Config: ${ide.configPath}\n`));
269
269
  });
270
270
  console.log(chalk_1.default.yellow('šŸ’” Use "fraim add-ide --ide <name>" to configure a specific IDE'));
271
- console.log(chalk_1.default.yellow(' Example: fraim add-ide --ide claude'));
271
+ console.log(chalk_1.default.yellow(' Example: fraim add-ide --ide claude-code'));
272
+ console.log(chalk_1.default.yellow(' Anthropic aliases: claude, claude-code, claude-desktop, claude-cowork'));
272
273
  };
273
274
  const promptForIDESelection = async (availableIDEs, tokens) => {
274
275
  console.log(chalk_1.default.green(`āœ… Found ${availableIDEs.length} IDEs that can be configured:\n`));
@@ -356,7 +357,7 @@ const runAddIDE = async (options) => {
356
357
  const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
357
358
  if (detectedIDEs.length === 0) {
358
359
  console.log(chalk_1.default.yellow('āš ļø No supported IDEs detected on your system.'));
359
- console.log(chalk_1.default.gray('Supported IDEs: Claude, Antigravity, Kiro, Cursor, VSCode, Codex, Windsurf'));
360
+ console.log(chalk_1.default.gray('Supported IDEs: Claude, Claude Code, Antigravity, Kiro, Cursor, VSCode, Codex, Windsurf'));
360
361
  console.log(chalk_1.default.blue('\nšŸ’” Install an IDE and run this command again.'));
361
362
  return;
362
363
  }
@@ -420,16 +421,21 @@ const runAddIDE = async (options) => {
420
421
  });
421
422
  }
422
423
  if (results.successful.length > 0) {
424
+ const { describeConfiguredInvocationSurfaces } = await Promise.resolve().then(() => __importStar(require('../setup/ide-global-integration')));
425
+ const successfulIDEs = idesToConfigure.filter(ide => results.successful.includes(ide.name));
426
+ const invocationSummaries = describeConfiguredInvocationSurfaces(successfulIDEs);
423
427
  console.log(chalk_1.default.blue('\nšŸ”„ Next steps:'));
424
428
  console.log(chalk_1.default.cyan(' 1. Restart your configured IDEs'));
425
- console.log(chalk_1.default.cyan(' 2. In any FRAIM-enabled repo, tell your AI agent: "Onboard this project"'));
429
+ invocationSummaries.forEach((summary, index) => {
430
+ console.log(chalk_1.default.cyan(` ${index + 2}. ${summary}`));
431
+ });
426
432
  console.log(chalk_1.default.blue('\nšŸ’” Use "fraim doctor --test-mcp" to verify the configuration.'));
427
433
  }
428
434
  };
429
435
  exports.runAddIDE = runAddIDE;
430
436
  exports.addIDECommand = new commander_1.Command('add-ide')
431
437
  .description('Add FRAIM configuration to additional IDEs')
432
- .option('--ide <name>', 'Configure specific IDE (claude, claude-code, antigravity, kiro, cursor, vscode, codex, windsurf)')
438
+ .option('--ide <name>', 'Configure specific IDE (claude, claude-code, claude-desktop, claude-cowork, antigravity, kiro, cursor, vscode, codex, windsurf)')
433
439
  .option('--all', 'Configure all detected IDEs')
434
440
  .option('--list', 'List all supported IDEs and their detection status')
435
441
  .action(exports.runAddIDE);
@@ -60,6 +60,19 @@ function parseModeOption(mode) {
60
60
  process.exit(1);
61
61
  }
62
62
  const promptForFraimKey = async () => {
63
+ // In non-interactive environments (tests, installers), we cannot prompt.
64
+ // Respect FRAIM_NON_INTERACTIVE and require the key to be provided via
65
+ // CLI flag or environment variable.
66
+ if (process.env.FRAIM_NON_INTERACTIVE) {
67
+ const envKey = process.env.FRAIM_API_KEY || process.env.FRAIM_SETUP_KEY || process.env.FRAIM_INSTALL_KEY;
68
+ if (envKey) {
69
+ console.log(chalk_1.default.yellow('\nā„¹ļø Non-interactive mode: using FRAIM key from environment.'));
70
+ return envKey;
71
+ }
72
+ console.log(chalk_1.default.red('\nāŒ Non-interactive mode: FRAIM key is required but cannot prompt.'));
73
+ console.log(chalk_1.default.yellow(' Provide a key via `--key=<key>` or set FRAIM_API_KEY.'));
74
+ process.exit(1);
75
+ }
63
76
  console.log(chalk_1.default.blue('šŸ”‘ FRAIM Key Setup'));
64
77
  console.log('FRAIM requires a valid API key to access workflows and features.\n');
65
78
  let key = null;
@@ -102,6 +115,20 @@ const promptForFraimKey = async () => {
102
115
  process.exit(1);
103
116
  };
104
117
  const promptForMode = async () => {
118
+ // Non-interactive environments (e.g. curl | sh installers, automated tests)
119
+ // cannot support interactive mode selection. When FRAIM_NON_INTERACTIVE is
120
+ // set, use FRAIM_SETUP_MODE/FRAIM_MODE if provided, otherwise default to
121
+ // conversational mode so no provider credentials are required.
122
+ if (process.env.FRAIM_NON_INTERACTIVE) {
123
+ const envMode = process.env.FRAIM_SETUP_MODE || process.env.FRAIM_MODE;
124
+ if (envMode) {
125
+ const parsed = parseModeOption(envMode);
126
+ console.log(chalk_1.default.yellow(`\nā„¹ļø Non-interactive mode: using mode "${parsed}" from environment.`));
127
+ return parsed;
128
+ }
129
+ console.log(chalk_1.default.yellow('\nā„¹ļø Non-interactive mode: defaulting to Conversational mode'));
130
+ return 'conversational';
131
+ }
105
132
  console.log(chalk_1.default.blue('\nšŸŽÆ Usage Mode Selection'));
106
133
  console.log(chalk_1.default.gray('Choose how you want to use FRAIM:\n'));
107
134
  const response = await (0, prompts_1.default)({
@@ -654,12 +681,12 @@ const runSetup = async (options) => {
654
681
  else {
655
682
  console.log(chalk_1.default.cyan('\n Get started — open your AI tool and:'));
656
683
  }
657
- // Check if Claude is among configured IDEs
658
- const hasClaude = configuredIDEs.some(ide => ide.name.toLowerCase().includes('claude'));
659
- if (hasClaude) {
660
- console.log(chalk_1.default.white(' /fraim ') + chalk_1.default.gray('(Claude Code) Browse all jobs'));
661
- }
662
- console.log(chalk_1.default.white(' "What can FRAIM help me with?" ') + chalk_1.default.gray('Works in any AI tool'));
684
+ const { describeConfiguredInvocationSurfaces } = await Promise.resolve().then(() => __importStar(require('../setup/ide-global-integration')));
685
+ const invocationSummaries = describeConfiguredInvocationSurfaces(configuredIDEs);
686
+ invocationSummaries.forEach((summary) => {
687
+ console.log(chalk_1.default.white(` ${summary}`));
688
+ });
689
+ console.log(chalk_1.default.white(' "What can FRAIM help me with?"'));
663
690
  console.log(chalk_1.default.gray('\n Just tell your AI what you need — FRAIM will find the right job.'));
664
691
  if (mode !== 'conversational') {
665
692
  console.log(chalk_1.default.cyan('\n To set up FRAIM in a specific project:'));
@@ -0,0 +1,45 @@
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.resolveManagedCommand = exports.getPortableNpxCommand = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
10
+ const getPortableNodeRoot = () => path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), 'node');
11
+ const getPortableNpxCandidates = () => {
12
+ const nodeRoot = getPortableNodeRoot();
13
+ if (process.platform === 'win32') {
14
+ const candidates = [path_1.default.join(nodeRoot, 'npx.cmd')];
15
+ if (fs_1.default.existsSync(nodeRoot)) {
16
+ const extractedDirs = fs_1.default.readdirSync(nodeRoot, { withFileTypes: true })
17
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('node-v'))
18
+ .sort((a, b) => b.name.localeCompare(a.name));
19
+ for (const entry of extractedDirs) {
20
+ candidates.push(path_1.default.join(nodeRoot, entry.name, 'npx.cmd'));
21
+ }
22
+ }
23
+ return candidates;
24
+ }
25
+ return [
26
+ path_1.default.join(nodeRoot, 'bin', 'npx'),
27
+ path_1.default.join(nodeRoot, 'npx')
28
+ ];
29
+ };
30
+ const getPortableNpxCommand = () => {
31
+ for (const candidate of getPortableNpxCandidates()) {
32
+ if (fs_1.default.existsSync(candidate)) {
33
+ return candidate;
34
+ }
35
+ }
36
+ return null;
37
+ };
38
+ exports.getPortableNpxCommand = getPortableNpxCommand;
39
+ const resolveManagedCommand = (command) => {
40
+ if (command !== 'npx') {
41
+ return command;
42
+ }
43
+ return (0, exports.getPortableNpxCommand)() || command;
44
+ };
45
+ exports.resolveManagedCommand = resolveManagedCommand;
@@ -6,6 +6,7 @@ exports.IDE_FORMATS = exports.CodexFormat = exports.WindsurfFormat = exports.Cla
6
6
  exports.getIDEFormat = getIDEFormat;
7
7
  const mcp_server_registry_1 = require("./mcp-server-registry");
8
8
  const provider_registry_1 = require("../providers/provider-registry");
9
+ const command_resolution_1 = require("./command-resolution");
9
10
  // Standard format (Cursor, Kiro, Antigravity, etc.)
10
11
  class StandardFormat {
11
12
  constructor() {
@@ -160,7 +161,7 @@ class WindsurfFormat {
160
161
  if (tokenEnvVar && server.headers?.Authorization) {
161
162
  const token = server.headers.Authorization.replace('Bearer ', '');
162
163
  mcpServers[key] = {
163
- command: 'npx',
164
+ command: (0, command_resolution_1.resolveManagedCommand)('npx'),
164
165
  args: ['-y', '@modelcontextprotocol/server-fetch', server.url],
165
166
  env: {
166
167
  [tokenEnvVar]: token
@@ -9,13 +9,14 @@ exports.isProviderServer = isProviderServer;
9
9
  exports.isHTTPServer = isHTTPServer;
10
10
  exports.buildAllBaseServers = buildAllBaseServers;
11
11
  const provider_registry_1 = require("../providers/provider-registry");
12
+ const command_resolution_1 = require("./command-resolution");
12
13
  exports.BASE_MCP_SERVERS = [
13
14
  {
14
15
  id: 'git',
15
16
  name: 'Git',
16
17
  description: 'Git repository operations (commit, branch, merge, etc.)',
17
18
  buildServer: () => ({
18
- command: 'npx',
19
+ command: (0, command_resolution_1.resolveManagedCommand)('npx'),
19
20
  args: ['-y', '@cyanheads/git-mcp-server']
20
21
  })
21
22
  },
@@ -24,7 +25,7 @@ exports.BASE_MCP_SERVERS = [
24
25
  name: 'Playwright',
25
26
  description: 'Browser automation and testing',
26
27
  buildServer: () => ({
27
- command: 'npx',
28
+ command: (0, command_resolution_1.resolveManagedCommand)('npx'),
28
29
  args: ['-y', '@playwright/mcp']
29
30
  })
30
31
  },
@@ -33,7 +34,7 @@ exports.BASE_MCP_SERVERS = [
33
34
  name: 'FRAIM',
34
35
  description: 'FRAIM job orchestration and mentoring',
35
36
  buildServer: (fraimKey) => ({
36
- command: 'npx',
37
+ command: (0, command_resolution_1.resolveManagedCommand)('npx'),
37
38
  args: ['-y', 'fraim-framework@latest', 'mcp'],
38
39
  env: {
39
40
  // Include API key for IDE configs (Codex, VSCode, etc.)
@@ -119,7 +120,7 @@ function buildStdioServer(mcpConfig, token, config) {
119
120
  }
120
121
  }
121
122
  return {
122
- command: mcpConfig.command,
123
+ command: (0, command_resolution_1.resolveManagedCommand)(mcpConfig.command),
123
124
  args: (mcpConfig.args || []).map(resolveTemplate),
124
125
  env
125
126
  };
@@ -55,6 +55,10 @@ const normalizePlatformTokens = (tokenInput) => {
55
55
  };
56
56
  const promptForIDESelection = async (detectedIDEs, tokenInput) => {
57
57
  const tokens = normalizePlatformTokens(tokenInput);
58
+ if (process.env.FRAIM_NON_INTERACTIVE) {
59
+ console.log(chalk_1.default.yellow(`\nā„¹ļø Non-interactive mode: configuring all detected IDEs (${detectedIDEs.length})`));
60
+ return detectedIDEs;
61
+ }
58
62
  console.log(chalk_1.default.green(`āœ… Found ${detectedIDEs.length} IDEs that can be configured with FRAIM:\n`));
59
63
  detectedIDEs.forEach((ide, index) => {
60
64
  const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
@@ -253,6 +257,10 @@ const autoConfigureMCP = async (fraimKey, tokenInput, selectedIDEs, providerConf
253
257
  console.log(chalk_1.default.gray('Supported IDEs: Claude, Antigravity, Kiro, Cursor, VSCode, Codex, Windsurf'));
254
258
  console.log(chalk_1.default.blue('\nšŸ’” You can install an IDE and run setup again later.'));
255
259
  console.log(chalk_1.default.gray(' Or continue with manual MCP configuration.'));
260
+ if (process.env.FRAIM_NON_INTERACTIVE) {
261
+ console.log(chalk_1.default.yellow('ā„¹ļø Non-interactive mode: skipping IDE configuration because no supported IDEs were detected.'));
262
+ return;
263
+ }
256
264
  const continueAnyway = await (0, prompts_1.default)({
257
265
  type: 'confirm',
258
266
  name: 'continue',
@@ -60,28 +60,33 @@ exports.IDE_CONFIGS = [
60
60
  configPath: '~/.claude.json',
61
61
  configFormat: 'json',
62
62
  configType: 'claude-code',
63
+ invocationProfile: 'claude-slash',
63
64
  detectMethod: detectClaude,
65
+ aliases: ['claude-code', 'claude code', 'claude code cli', 'code tab'],
64
66
  alternativePaths: [
65
67
  '~/.claude/settings.json'
66
68
  ],
67
- description: 'Anthropic Claude Code CLI tool'
69
+ description: 'Anthropic Claude Code local surfaces (CLI and Desktop Code tab)'
68
70
  },
69
71
  {
70
72
  name: 'Claude Desktop / Cowork',
71
73
  configPath: '~/AppData/Roaming/Claude/claude_desktop_config.json',
72
74
  configFormat: 'json',
73
75
  configType: 'claude',
76
+ invocationProfile: 'launch-phrase',
74
77
  detectMethod: detectClaude,
78
+ aliases: ['claude', 'claude-desktop', 'claude desktop', 'claude-cowork', 'cowork', 'claude cowork'],
75
79
  alternativePaths: [
76
80
  '~/Library/Application Support/Claude/claude_desktop_config.json'
77
81
  ],
78
- description: 'Anthropic Claude Desktop and Cowork application'
82
+ description: 'Anthropic Claude Desktop chat and Cowork surfaces'
79
83
  },
80
84
  {
81
85
  name: 'Antigravity',
82
86
  configPath: '~/.gemini/antigravity/mcp_config.json',
83
87
  configFormat: 'json',
84
88
  configType: 'standard',
89
+ invocationProfile: 'instructions-only',
85
90
  detectMethod: () => fs_1.default.existsSync(expandPath('~/.gemini/antigravity')),
86
91
  description: 'Google Gemini Antigravity IDE'
87
92
  },
@@ -90,6 +95,7 @@ exports.IDE_CONFIGS = [
90
95
  configPath: '~/.kiro/settings/mcp.json',
91
96
  configFormat: 'json',
92
97
  configType: 'kiro',
98
+ invocationProfile: 'kiro-hashtag',
93
99
  detectMethod: () => fs_1.default.existsSync(expandPath('~/.kiro')),
94
100
  description: 'Kiro AI-powered IDE'
95
101
  },
@@ -98,6 +104,7 @@ exports.IDE_CONFIGS = [
98
104
  configPath: '~/.cursor/mcp.json',
99
105
  configFormat: 'json',
100
106
  configType: 'kiro',
107
+ invocationProfile: 'cursor-mention',
101
108
  detectMethod: detectCursor,
102
109
  alternativePaths: [
103
110
  '~/Library/Application Support/Cursor/mcp.json',
@@ -115,6 +122,7 @@ exports.IDE_CONFIGS = [
115
122
  : '~/.config/Code/User/mcp.json',
116
123
  configFormat: 'json',
117
124
  configType: 'vscode',
125
+ invocationProfile: 'vscode-prompt',
118
126
  detectMethod: detectVSCode,
119
127
  alternativePaths: [
120
128
  '~/Library/Application Support/Code/User/mcp.json',
@@ -128,6 +136,7 @@ exports.IDE_CONFIGS = [
128
136
  configPath: '~/.codex/config.toml',
129
137
  configFormat: 'toml',
130
138
  configType: 'codex',
139
+ invocationProfile: 'codex-skill',
131
140
  detectMethod: () => fs_1.default.existsSync(expandPath('~/.codex')),
132
141
  description: 'Codex AI development environment'
133
142
  },
@@ -136,6 +145,7 @@ exports.IDE_CONFIGS = [
136
145
  configPath: '~/.codeium/windsurf/mcp_config.json',
137
146
  configFormat: 'json',
138
147
  configType: 'windsurf',
148
+ invocationProfile: 'windsurf-command',
139
149
  detectMethod: detectWindsurf,
140
150
  alternativePaths: [
141
151
  '~/Library/Application Support/Windsurf/mcp_config.json',
@@ -173,7 +183,9 @@ const getAllSupportedIDEs = () => {
173
183
  };
174
184
  exports.getAllSupportedIDEs = getAllSupportedIDEs;
175
185
  const findIDEByName = (name) => {
176
- return exports.IDE_CONFIGS.find(ide => ide.name.toLowerCase().includes(name.toLowerCase()) ||
177
- name.toLowerCase().includes(ide.name.toLowerCase()));
186
+ const normalized = name.toLowerCase();
187
+ return exports.IDE_CONFIGS.find(ide => ide.name.toLowerCase().includes(normalized) ||
188
+ normalized.includes(ide.name.toLowerCase()) ||
189
+ ide.aliases?.some(alias => alias.includes(normalized) || normalized.includes(alias)));
178
190
  };
179
191
  exports.findIDEByName = findIDEByName;
@@ -3,122 +3,62 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.describeConfiguredInvocationSurfaces = describeConfiguredInvocationSurfaces;
6
7
  exports.installSlashCommands = installSlashCommands;
7
8
  exports.installGlobalRules = installGlobalRules;
8
9
  /**
9
10
  * IDE Global Integration
10
11
  *
11
- * Installs FRAIM slash commands and global rules into user-level IDE
12
- * configuration directories. These enable FRAIM to be discoverable
13
- * in any project without requiring fraim init-project.
14
- *
15
- * Part of: User-Level FRAIM Artifacts with Local Shadow Semantics
12
+ * Installs user-level FRAIM invocation artifacts for supported IDEs.
13
+ * These surfaces make FRAIM discoverable without requiring `fraim init-project`.
16
14
  */
17
15
  const fs_1 = __importDefault(require("fs"));
18
- const path_1 = __importDefault(require("path"));
19
16
  const os_1 = __importDefault(require("os"));
17
+ const path_1 = __importDefault(require("path"));
20
18
  const chalk_1 = __importDefault(require("chalk"));
21
- const FRAIM_SLASH_COMMAND_CONTENT = `The user wants to run FRAIM. The requested job or topic is: $ARGUMENTS
22
-
23
- Follow this process:
24
-
25
- 1. **If no argument was given** (the line above ends with ": "):
26
- Call \`list_fraim_jobs()\` to discover available jobs. Present the results to the user grouped by business function (the server returns jobs organized by category — use those categories as group headings). For each group, list 3-5 of the most impactful jobs with a one-line description.
27
-
28
- After listing, suggest 2-3 starting points based on what seems most relevant:
29
- - If in a code repo: suggest jobs from engineering/product-building categories
30
- - If no repo context: suggest jobs from marketing, fundraising, or business categories
31
-
32
- Ask the user which job they want to run, then proceed to step 2.
33
-
34
- 2. **Find the match**: from the list returned by \`list_fraim_jobs()\`, find the job whose name matches or closely resembles the argument. If no job matches, search for a matching skill by calling \`get_fraim_file({ path: "skills/<likely-category>/<argument>.md" })\` — try common categories like \`engineering/\`, \`marketing/\`, \`business/\`, \`product-management/\`, \`ai-tools/\`. Confirm the match with the user.
35
-
36
- 3. **Load the full content**:
37
- - For jobs: call \`get_fraim_job({ job: "<matched-job-name>" })\` — never execute from stub content.
38
- - For skills: the content from \`get_fraim_file\` is the full skill. Use it directly.
39
-
40
- 4. **Execute**: for jobs, follow the phased instructions returned by \`get_fraim_job\`, using \`seekMentoring\` at phase transitions where indicated. For skills, apply the skill steps to the user's current context.
41
- `;
42
- const FRAIM_CURSOR_RULES_CONTENT = `# FRAIM Rules
43
-
44
- This workspace uses FRAIM (Framework for Rigor-based AI Management).
45
-
46
- ## Discovery
47
- - Before acting on any user request, check if a FRAIM job matches the intent.
48
- - Call \`list_fraim_jobs()\` to discover available jobs.
49
- - Call \`get_fraim_job({ job: "<job-name>" })\` to get full phased instructions.
50
-
51
- ## Execution
52
- - Jobs are FRAIM's primary execution units — structured, multi-phase workflows.
53
- - Skills are reusable capabilities that jobs compose.
54
- - Rules are always-on constraints and conventions.
55
- - Follow phased instructions and use \`seekMentoring\` at phase transitions.
56
-
57
- ## Principles
58
- - Never execute from job stubs — always load full instructions via MCP.
59
- - Follow the constitution: integrity, correctness, contribution, completeness.
60
- `;
19
+ const ide_invocation_surfaces_1 = require("./ide-invocation-surfaces");
20
+ function describeConfiguredInvocationSurfaces(installedIDEs) {
21
+ return installedIDEs.map((ide) => (0, ide_invocation_surfaces_1.describeInvocationSurface)(ide.name, ide.invocationProfile));
22
+ }
61
23
  /**
62
24
  * Install the FRAIM slash command for Claude Code at the user level.
63
- * Writes to ~/.claude/commands/fraim.md.
64
- * Does NOT overwrite if the file already exists.
65
- *
66
- * @param homeDir - Override for home directory (for testing)
25
+ * Writes to ~/.claude/commands/fraim.md and does not overwrite existing files.
67
26
  */
68
27
  async function installSlashCommands(homeDir) {
69
28
  const home = homeDir || os_1.default.homedir();
70
29
  const claudeDir = path_1.default.join(home, '.claude');
71
- // Only install if Claude Code is installed (indicated by .claude/ existing)
72
30
  if (!fs_1.default.existsSync(claudeDir)) {
73
31
  return;
74
32
  }
75
- const commandsDir = path_1.default.join(claudeDir, 'commands');
76
- const slashCommandPath = path_1.default.join(commandsDir, 'fraim.md');
77
- // Do not overwrite existing file
78
- if (fs_1.default.existsSync(slashCommandPath)) {
79
- console.log(chalk_1.default.gray(' Claude slash command already exists — skipping'));
80
- return;
81
- }
82
- fs_1.default.mkdirSync(commandsDir, { recursive: true });
83
- fs_1.default.writeFileSync(slashCommandPath, FRAIM_SLASH_COMMAND_CONTENT, 'utf8');
84
- console.log(chalk_1.default.green(' āœ… Installed Claude slash command (~/.claude/commands/fraim.md)'));
33
+ installFileIfMissing(path_1.default.join(claudeDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildClaudeSlashCommandContent)(), 'Claude slash command (~/.claude/commands/fraim.md)');
85
34
  }
86
35
  /**
87
- * Install FRAIM global rules/instructions for supported IDEs.
36
+ * Install FRAIM invocation artifacts for non-Claude IDEs.
88
37
  * Supports: Cursor, Codex, Windsurf, Kiro
89
- * Does NOT overwrite if files already exist.
90
- *
91
- * @param homeDir - Override for home directory (for testing)
38
+ * Does not overwrite existing files.
92
39
  */
93
40
  async function installGlobalRules(homeDir) {
94
41
  const home = homeDir || os_1.default.homedir();
95
- // Cursor: ~/.cursor/rules/fraim-rules.md
96
42
  const cursorDir = path_1.default.join(home, '.cursor');
97
43
  if (fs_1.default.existsSync(cursorDir)) {
98
- installRuleFile(path_1.default.join(cursorDir, 'rules', 'fraim-rules.md'), FRAIM_CURSOR_RULES_CONTENT, 'Cursor global rules (~/.cursor/rules/fraim-rules.md)');
44
+ installFileIfMissing(path_1.default.join(cursorDir, 'rules', 'fraim.mdc'), (0, ide_invocation_surfaces_1.buildCursorMentionRuleContent)(), 'Cursor FRAIM rule (~/.cursor/rules/fraim.mdc)');
99
45
  }
100
- // Codex: ~/.codex/instructions.md
101
46
  const codexDir = path_1.default.join(home, '.codex');
102
47
  if (fs_1.default.existsSync(codexDir)) {
103
- installRuleFile(path_1.default.join(codexDir, 'instructions.md'), FRAIM_CURSOR_RULES_CONTENT, 'Codex global instructions (~/.codex/instructions.md)');
48
+ installFileIfMissing(path_1.default.join(codexDir, 'skills', 'fraim', 'SKILL.md'), (0, ide_invocation_surfaces_1.buildCodexSkillContent)(), 'Codex FRAIM skill (~/.codex/skills/fraim/SKILL.md)');
104
49
  }
105
- // Windsurf: ~/.codeium/windsurf/rules/fraim-rules.md
106
50
  const windsurfDir = path_1.default.join(home, '.codeium', 'windsurf');
107
51
  if (fs_1.default.existsSync(windsurfDir)) {
108
- installRuleFile(path_1.default.join(windsurfDir, 'rules', 'fraim-rules.md'), FRAIM_CURSOR_RULES_CONTENT, 'Windsurf global rules (~/.codeium/windsurf/rules/fraim-rules.md)');
52
+ installFileIfMissing(path_1.default.join(windsurfDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildWindsurfCommandContent)(), 'Windsurf FRAIM command (~/.codeium/windsurf/commands/fraim.md)');
109
53
  }
110
- // Kiro: ~/.kiro/rules/fraim-rules.md
111
54
  const kiroDir = path_1.default.join(home, '.kiro');
112
55
  if (fs_1.default.existsSync(kiroDir)) {
113
- installRuleFile(path_1.default.join(kiroDir, 'rules', 'fraim-rules.md'), FRAIM_CURSOR_RULES_CONTENT, 'Kiro global rules (~/.kiro/rules/fraim-rules.md)');
56
+ installFileIfMissing(path_1.default.join(kiroDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildKiroCommandContent)(), 'Kiro FRAIM command (~/.kiro/commands/fraim.md)');
114
57
  }
115
58
  }
116
- /**
117
- * Install a rule file if it doesn't already exist.
118
- */
119
- function installRuleFile(filePath, content, displayName) {
59
+ function installFileIfMissing(filePath, content, displayName) {
120
60
  if (fs_1.default.existsSync(filePath)) {
121
- console.log(chalk_1.default.gray(` ${displayName.split('(')[0].trim()} already exist — skipping`));
61
+ console.log(chalk_1.default.gray(` ${displayName.split('(')[0].trim()} already exists — skipping`));
122
62
  return;
123
63
  }
124
64
  fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FRAIM_INVOCATION_BODY = exports.CURSOR_MDC_FRONTMATTER = exports.FRAIM_LAUNCH_PHRASE = void 0;
4
+ exports.buildClaudeSlashCommandContent = buildClaudeSlashCommandContent;
5
+ exports.buildCursorMentionRuleContent = buildCursorMentionRuleContent;
6
+ exports.buildCodexSkillContent = buildCodexSkillContent;
7
+ exports.buildWindsurfCommandContent = buildWindsurfCommandContent;
8
+ exports.buildKiroCommandContent = buildKiroCommandContent;
9
+ exports.describeInvocationSurface = describeInvocationSurface;
10
+ exports.FRAIM_LAUNCH_PHRASE = 'Use FRAIM for <job or task>';
11
+ exports.CURSOR_MDC_FRONTMATTER = `---
12
+ description: FRAIM discovery and execution contract
13
+ alwaysApply: true
14
+ ---`;
15
+ exports.FRAIM_INVOCATION_BODY = `Follow this process:
16
+
17
+ 1. **If the user did not specify a FRAIM job or topic**:
18
+ Call \`list_fraim_jobs()\` to discover available jobs. Present the results grouped by the categories returned by the server. For each group, list 3-5 of the most relevant jobs with a one-line description.
19
+
20
+ 2. **Find the match**:
21
+ Match the user's request to a FRAIM job from \`list_fraim_jobs()\`. If no job matches, try a likely FRAIM skill with \`get_fraim_file({ path: "skills/<likely-category>/<argument>.md" })\` and confirm the match with the user.
22
+
23
+ 3. **Load the full content**:
24
+ - For jobs, call \`get_fraim_job({ job: "<matched-job-name>" })\`.
25
+ - For skills, use the content returned by \`get_fraim_file(...)\`.
26
+
27
+ 4. **Execute**:
28
+ - For jobs, follow the phased instructions and use \`seekMentoring\` when the job requires phase transitions.
29
+ - For skills, apply the skill steps directly to the user's current context.
30
+ `;
31
+ function buildClaudeSlashCommandContent() {
32
+ return exports.FRAIM_INVOCATION_BODY;
33
+ }
34
+ function buildCursorMentionRuleContent() {
35
+ return `${exports.CURSOR_MDC_FRONTMATTER}
36
+
37
+ # FRAIM
38
+
39
+ ${exports.FRAIM_INVOCATION_BODY}
40
+ `;
41
+ }
42
+ function buildCodexSkillContent() {
43
+ return `# FRAIM
44
+
45
+ ${exports.FRAIM_INVOCATION_BODY}`;
46
+ }
47
+ function buildWindsurfCommandContent() {
48
+ return `# FRAIM
49
+
50
+ ${exports.FRAIM_INVOCATION_BODY}`;
51
+ }
52
+ function buildKiroCommandContent() {
53
+ return `# FRAIM
54
+
55
+ ${exports.FRAIM_INVOCATION_BODY}`;
56
+ }
57
+ function describeInvocationSurface(ideName, invocationProfile) {
58
+ switch (invocationProfile) {
59
+ case 'claude-slash':
60
+ return ideName === 'Claude Code'
61
+ ? 'Claude Code (CLI/Desktop): /fraim'
62
+ : `${ideName}: /fraim`;
63
+ case 'launch-phrase':
64
+ return `${ideName}: "${exports.FRAIM_LAUNCH_PHRASE}"`;
65
+ case 'cursor-mention':
66
+ return `${ideName}: @fraim`;
67
+ case 'vscode-prompt':
68
+ return `${ideName}: /fraim via workspace prompt`;
69
+ case 'codex-skill':
70
+ return `${ideName}: /fraim, $fraim`;
71
+ case 'windsurf-command':
72
+ return `${ideName}: /fraim`;
73
+ case 'kiro-hashtag':
74
+ return `${ideName}: #fraim`;
75
+ case 'instructions-only':
76
+ default:
77
+ return `${ideName}: natural language`;
78
+ }
79
+ }
@@ -6,15 +6,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ensureAgentAdapterFiles = ensureAgentAdapterFiles;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ const ide_invocation_surfaces_1 = require("../setup/ide-invocation-surfaces");
9
10
  const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
10
11
  const START_MARKER = '<!-- FRAIM_AGENT_ADAPTER_START -->';
11
12
  const END_MARKER = '<!-- FRAIM_AGENT_ADAPTER_END -->';
12
13
  const CURSOR_RULE_PATH = path_1.default.join('.cursor', 'rules', 'fraim.mdc');
13
14
  const CLAUDE_FRAIM_COMMAND_PATH = path_1.default.join('.claude', 'commands', 'fraim.md');
14
- const CURSOR_FRONTMATTER = `---
15
- description: FRAIM discovery and execution contract
16
- alwaysApply: true
17
- ---`;
15
+ const VSCODE_FRAIM_PROMPT_PATH = path_1.default.join('.github', 'prompts', 'fraim.prompt.md');
16
+ const CODEX_FRAIM_SKILL_PATH = path_1.default.join('.codex', 'skills', 'fraim', 'SKILL.md');
17
+ const WINDSURF_FRAIM_COMMAND_PATH = path_1.default.join('.windsurf', 'commands', 'fraim.md');
18
+ const KIRO_FRAIM_COMMAND_PATH = path_1.default.join('.kiro', 'commands', 'fraim.md');
18
19
  function buildManagedSection(body) {
19
20
  return `${START_MARKER}
20
21
  ${body.trim()}
@@ -40,7 +41,7 @@ function mergeCursorRule(existingContent, managedSection) {
40
41
  const normalized = existingContent.replace(/\r\n/g, '\n').trimStart();
41
42
  const bodyWithoutLeadingFrontmatter = normalized.replace(/^(?:---\n[\s\S]*?\n---(?:\n+)?)*/, '');
42
43
  const mergedBody = mergeManagedSection(bodyWithoutLeadingFrontmatter, managedSection).trim();
43
- return `${CURSOR_FRONTMATTER}\n\n${mergedBody}\n`;
44
+ return `${ide_invocation_surfaces_1.CURSOR_MDC_FRONTMATTER}\n\n${mergedBody}\n`;
44
45
  }
45
46
  function getAdapterFiles() {
46
47
  const fraimRoot = (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)().replace(/\/$/, '');
@@ -70,15 +71,9 @@ This repository uses FRAIM.
70
71
  > **Job stubs are for discovery only.** When a user @mentions or references any file under \`${employeeJobsPath}/\` or \`${managerJobsPath}/\`, do NOT attempt to execute the job from the stub content. The stub only shows intent and phase names. Always call \`get_fraim_job({ job: "<job-name>" })\` first to get the full phased instructions before doing any work.
71
72
  `);
72
73
  const cursorManagedBody = buildManagedSection(`
73
- Use FRAIM as the repo's execution framework.
74
+ # FRAIM
74
75
 
75
- - Discover available jobs, skills, and rules under \`${fraimRoot}/\`.
76
- - Jobs are the primary execution units; treat them like first-class workflows.
77
- - Skills are reusable capability modules jobs compose.
78
- - Rules are always-on constraints.
79
- - Repo-specific overrides and learnings under \`${personalizedRootPath}/\` take precedence.
80
- - Choose a relevant job from the stubs, then call \`get_fraim_job(...)\` for the full phased instructions.
81
- - **Job stubs are for discovery only.** Never execute a job from stub content — always call \`get_fraim_job({ job: "<job-name>" })\` first.
76
+ ${ide_invocation_surfaces_1.FRAIM_INVOCATION_BODY}
82
77
  `);
83
78
  const copilotBody = buildManagedSection(`
84
79
  ## FRAIM
@@ -89,7 +84,7 @@ Use FRAIM as the repo's execution framework.
89
84
  - FRAIM rules are always-on constraints and conventions.
90
85
  - Repo-specific overrides and learnings live under \`${personalizedRootPath}/\`.
91
86
  - Use the stubs to identify which job to invoke before fetching full content with FRAIM MCP tools.
92
- - **Job stubs are for discovery only.** Never execute a job from stub content — always call \`get_fraim_job({ job: "<job-name>" })\` first.
87
+ - **Job stubs are for discovery only.** Never execute a job from stub content - always call \`get_fraim_job({ job: "<job-name>" })\` first.
93
88
  `);
94
89
  const fraimReadme = `# FRAIM Catalog
95
90
 
@@ -103,25 +98,20 @@ This directory is the repository-visible FRAIM surface.
103
98
 
104
99
  Use the stubs here to discover which FRAIM job, skill, or rule is relevant, then load the full content through FRAIM MCP tools.
105
100
  `;
106
- const claudeCommand = `The user wants to run FRAIM. The requested job or topic is: $ARGUMENTS
101
+ const vscodePrompt = `# FRAIM
107
102
 
108
- Follow this process:
109
-
110
- 1. **If no argument was given** (the line above ends with ": "): scan all stub files under \`${employeeJobsPath}/\` and \`${managerJobsPath}/\`. List each by filename and its Intent line. Ask the user which job they want to run, then proceed to step 2.
111
-
112
- 2. **Find the job**: search \`${employeeJobsPath}/\` and \`${managerJobsPath}/\` for a stub whose filename (without \`.md\`) matches or closely resembles the argument. Read the stub's Intent/Outcome to confirm it matches what the user wants.
113
-
114
- 3. **Load the full job**: call \`get_fraim_job({ job: "<matched-job-name>" })\` — never execute from stub content.
115
-
116
- 4. **Execute**: follow the phased instructions returned by \`get_fraim_job\`, using \`seekMentoring\` at phase transitions where indicated.
117
- `;
103
+ ${ide_invocation_surfaces_1.FRAIM_INVOCATION_BODY}`;
118
104
  return [
119
105
  { path: 'AGENTS.md', content: markdownBody },
120
106
  { path: 'CLAUDE.md', content: markdownBody },
121
107
  { path: path_1.default.join('.github', 'copilot-instructions.md'), content: copilotBody },
122
108
  { path: CURSOR_RULE_PATH, content: cursorManagedBody },
109
+ { path: VSCODE_FRAIM_PROMPT_PATH, content: vscodePrompt },
123
110
  { path: path_1.default.join(project_fraim_paths_1.WORKSPACE_FRAIM_DIRNAME, 'README.md'), content: fraimReadme },
124
- { path: CLAUDE_FRAIM_COMMAND_PATH, content: claudeCommand }
111
+ { path: CLAUDE_FRAIM_COMMAND_PATH, content: (0, ide_invocation_surfaces_1.buildClaudeSlashCommandContent)() },
112
+ { path: CODEX_FRAIM_SKILL_PATH, content: (0, ide_invocation_surfaces_1.buildCodexSkillContent)() },
113
+ { path: WINDSURF_FRAIM_COMMAND_PATH, content: (0, ide_invocation_surfaces_1.buildWindsurfCommandContent)() },
114
+ { path: KIRO_FRAIM_COMMAND_PATH, content: (0, ide_invocation_surfaces_1.buildKiroCommandContent)() }
125
115
  ];
126
116
  }
127
117
  function ensureAgentAdapterFiles(projectRoot) {
@@ -135,7 +125,12 @@ function ensureAgentAdapterFiles(projectRoot) {
135
125
  const existing = fs_1.default.existsSync(fullPath) ? fs_1.default.readFileSync(fullPath, 'utf8') : '';
136
126
  const next = file.path === CURSOR_RULE_PATH
137
127
  ? mergeCursorRule(existing, file.content)
138
- : file.path.endsWith('README.md') || file.path === CLAUDE_FRAIM_COMMAND_PATH
128
+ : file.path.endsWith('README.md')
129
+ || file.path === VSCODE_FRAIM_PROMPT_PATH
130
+ || file.path === CLAUDE_FRAIM_COMMAND_PATH
131
+ || file.path === CODEX_FRAIM_SKILL_PATH
132
+ || file.path === WINDSURF_FRAIM_COMMAND_PATH
133
+ || file.path === KIRO_FRAIM_COMMAND_PATH
139
134
  ? file.content
140
135
  : mergeManagedSection(existing, file.content);
141
136
  if (existing !== next) {
package/package.json CHANGED
@@ -1,146 +1,146 @@
1
- {
2
- "name": "fraim-framework",
3
- "version": "2.0.116",
4
- "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
- "main": "index.js",
6
- "bin": {
7
- "fraim": "./index.js",
8
- "fraim-framework": "./index.js"
9
- },
10
- "scripts": {
11
- "dev": "tsx --watch src/fraim-mcp-server.ts > server.log 2>&1",
12
- "dev:prod": "npm run build && node dist/src/fraim-mcp-server.js > server.log 2>&1",
13
- "build": "tsc && npm run build:stubs && npm run build:fraim-brain && node scripts/copy-registry.js && node -e \"require('fs').copyFileSync('src/core/types.ts', 'registry/templates/manager/fraim-config-schema.ts')\" && npm run validate:registry && npm run validate:fraim-pro-assets && tsx scripts/validate-purity.ts",
14
- "build:stubs": "tsx scripts/build-stub-registry.ts",
15
- "build:fraim-brain": "node scripts/generate-fraim-brain.js",
16
- "test-all": "npm run test && npm run test:isolated && npm run test:ui",
17
- "test": "node scripts/test-with-server.js",
18
- "test:isolated": "npx tsx --test --test-reporter=spec tests/isolated/test-*.ts",
19
- "test:smoke": "node scripts/test-with-server.js --tags=smoke",
20
- "test:coverage": "node scripts/test-with-server.js --tags=smoke --coverage",
21
- "test:stripe": "node scripts/test-with-server.js tests/test-stripe-payment-complete.ts",
22
- "test:stripe:ui": "playwright test tests/ui/test-payment-ui.spec.ts",
23
- "test:perf": "node scripts/test-with-server.js tests/performance/analytics-perf.ts",
24
- "test:ui": "playwright test",
25
- "test:ui:headed": "playwright test --headed",
26
- "start:fraim": "tsx src/fraim-mcp-server.ts",
27
- "dev:fraim": "tsx --watch src/fraim-mcp-server.ts",
28
- "serve:website": "node fraim-pro/serve.js",
29
- "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts > prodlogs.log 2>&1",
30
- "manage-keys": "tsx scripts/fraim/manage-keys.ts",
31
- "manage-teams": "tsx scripts/fraim/manage-teams.ts",
32
- "partner-discounts": "tsx scripts/fraim/manage-partner-discounts.ts",
33
- "fix-key": "tsx scripts/fraim/fix-expired-key.ts",
34
- "setup-stripe-webhook": "tsx scripts/fraim/setup-stripe-webhook.ts",
35
- "view-signups": "tsx scripts/view-signups.ts",
36
- "fraim:init": "npm run build && node index.js init",
37
- "fraim:sync": "node index.js sync --local",
38
- "postinstall": "fraim sync --skip-updates || echo 'FRAIM setup skipped.'",
39
- "prepublishOnly": "npm run build",
40
- "release": "npm version patch && npm run publish-both",
41
- "publish-both": "node scripts/publish-both.js",
42
- "publish-fraim-only": "node scripts/publish-fraim.js",
43
- "publish-both-manual": "node scripts/publish-both.js",
44
- "validate:registry": "tsx scripts/verify-registry-paths.ts && npm run validate:jobs && npm run validate:skills && npm run validate:registry-references && npm run validate:platform-agnostic && npm run validate:template-namespaces && npm run validate:config-fallbacks && npm run validate:bootstrap-config-coverage && npm run validate:provider-action-mappings && npm run validate:fidelity && npm run validate:config-tokens && npm run validate:brain-mapping && npm run validate:template-syntax",
45
- "validate:registry-references": "tsx scripts/validate-registry-references.ts",
46
- "validate:brain-mapping": "tsx scripts/validate-brain-mapping.ts",
47
- "validate:fraim-pro-assets": "tsx scripts/validate-fraim-pro-assets.ts",
48
- "validate:jobs": "tsx scripts/validate-jobs.ts",
49
- "validate:platform-agnostic": "tsx scripts/validate-platform-agnostic.ts",
50
- "validate:skills": "tsx scripts/validate-skills.ts",
51
- "validate:template-namespaces": "tsx scripts/validate-template-namespaces.ts",
52
- "validate:config-fallbacks": "tsx scripts/validate-config-fallbacks.ts",
53
- "validate:bootstrap-config-coverage": "tsx scripts/validate-bootstrap-config-coverage.ts",
54
- "validate:provider-action-mappings": "tsx scripts/validate-provider-action-mappings.ts",
55
- "validate:fidelity": "tsx scripts/validate-fidelity.ts",
56
- "validate:config-tokens": "tsx scripts/validate-config-tokens.ts",
57
- "validate:template-syntax": "tsx scripts/validate-template-syntax.ts",
58
- "validate:backup": "bash scripts/backup/validate-pitr-restore.sh"
59
- },
60
- "repository": {
61
- "type": "git",
62
- "url": "git+https://github.com/mathursrus/FRAIM.git"
63
- },
64
- "keywords": [
65
- "fraim",
66
- "ai-management",
67
- "ai-coordination",
68
- "ai-agents",
69
- "multi-agent",
70
- "github",
71
- "automation",
72
- "gitops",
73
- "cursor",
74
- "claude",
75
- "windsurf",
76
- "rigor",
77
- "enterprise",
78
- "framework",
79
- "ai-managers"
80
- ],
81
- "author": "Sid Mathur <sid.mathur@gmail.com>",
82
- "license": "MIT",
83
- "bugs": {
84
- "url": "https://github.com/mathursrus/FRAIM/issues"
85
- },
86
- "homepage": "https://github.com/mathursrus/FRAIM#readme",
87
- "engines": {
88
- "node": ">=16.0.0"
89
- },
90
- "devDependencies": {
91
- "@playwright/test": "^1.58.2",
92
- "@types/adm-zip": "^0.5.7",
93
- "@types/cors": "^2.8.19",
94
- "@types/express": "^5.0.6",
95
- "@types/node": "^20.0.0",
96
- "@types/node-fetch": "^2.6.13",
97
- "@types/prompts": "^2.4.9",
98
- "@types/semver": "^7.7.1",
99
- "fast-glob": "^3.3.3",
100
- "html-to-docx": "^1.8.0",
101
- "markdown-it": "^14.1.1",
102
- "markdown-it-highlightjs": "^4.3.0",
103
- "playwright": "^1.58.2",
104
- "pptxgenjs": "^4.0.1",
105
- "puppeteer": "^24.36.1",
106
- "qrcode": "^1.5.4",
107
- "sharp": "^0.34.5",
108
- "tsx": "^4.0.0",
109
- "typescript": "^5.0.0"
110
- },
111
- "files": [
112
- "dist/src/local-mcp-server/",
113
- "dist/src/cli/",
114
- "dist/src/core/",
115
- "bin/fraim.js",
116
- "bin/fraim-mcp.js",
117
- "index.js",
118
- "README.md",
119
- "CHANGELOG.md",
120
- "LICENSE",
121
- "package.json"
122
- ],
123
- "publishConfig": {
124
- "access": "public"
125
- },
126
- "dependencies": {
127
- "@octokit/rest": "^22.0.1",
128
- "adm-zip": "^0.5.16",
129
- "axios": "^1.7.0",
130
- "chalk": "4.1.2",
131
- "commander": "^14.0.2",
132
- "cors": "^2.8.5",
133
- "dotenv": "^16.4.7",
134
- "express": "^5.2.1",
135
- "mongodb": "^7.0.0",
136
- "node-edge-tts": "^1.2.10",
137
- "nodemailer": "^8.0.3",
138
- "prompts": "^2.4.2",
139
- "resend": "^6.9.3",
140
- "semver": "^7.7.4",
141
- "stripe": "^20.3.1",
142
- "toml": "^3.0.0",
143
- "tree-kill": "^1.2.2",
144
- "xml2js": "^0.6.2"
145
- }
146
- }
1
+ {
2
+ "name": "fraim-framework",
3
+ "version": "2.0.119",
4
+ "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "fraim": "./index.js",
8
+ "fraim-framework": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "dev": "tsx --watch src/fraim-mcp-server.ts > server.log 2>&1",
12
+ "dev:prod": "npm run build && node dist/src/fraim-mcp-server.js > server.log 2>&1",
13
+ "build": "tsc && npm run build:stubs && npm run build:fraim-brain && node scripts/copy-registry.js && node -e \"require('fs').copyFileSync('src/core/types.ts', 'registry/templates/manager/fraim-config-schema.ts')\" && npm run validate:registry && npm run validate:fraim-pro-assets && tsx scripts/validate-purity.ts",
14
+ "build:stubs": "tsx scripts/build-stub-registry.ts",
15
+ "build:fraim-brain": "node scripts/generate-fraim-brain.js",
16
+ "test-all": "npm run test && npm run test:isolated && npm run test:ui",
17
+ "test": "node scripts/test-with-server.js",
18
+ "test:isolated": "npx tsx --test --test-reporter=spec tests/isolated/test-*.ts",
19
+ "test:smoke": "node scripts/test-with-server.js --tags=smoke",
20
+ "test:coverage": "node scripts/test-with-server.js --tags=smoke --coverage",
21
+ "test:stripe": "node scripts/test-with-server.js tests/test-stripe-payment-complete.ts",
22
+ "test:stripe:ui": "playwright test tests/ui/test-payment-ui.spec.ts",
23
+ "test:perf": "node scripts/test-with-server.js tests/performance/analytics-perf.ts",
24
+ "test:ui": "playwright test",
25
+ "test:ui:headed": "playwright test --headed",
26
+ "start:fraim": "tsx src/fraim-mcp-server.ts",
27
+ "dev:fraim": "tsx --watch src/fraim-mcp-server.ts",
28
+ "serve:website": "node fraim-pro/serve.js",
29
+ "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts > prodlogs.log 2>&1",
30
+ "manage-keys": "tsx scripts/fraim/manage-keys.ts",
31
+ "manage-teams": "tsx scripts/fraim/manage-teams.ts",
32
+ "partner-discounts": "tsx scripts/fraim/manage-partner-discounts.ts",
33
+ "fix-key": "tsx scripts/fraim/fix-expired-key.ts",
34
+ "setup-stripe-webhook": "tsx scripts/fraim/setup-stripe-webhook.ts",
35
+ "view-signups": "tsx scripts/view-signups.ts",
36
+ "fraim:init": "npm run build && node index.js init",
37
+ "fraim:sync": "node index.js sync --local",
38
+ "postinstall": "fraim sync --skip-updates || echo 'FRAIM setup skipped.'",
39
+ "prepublishOnly": "npm run build",
40
+ "release": "npm version patch && npm run publish-both",
41
+ "publish-both": "node scripts/publish-both.js",
42
+ "publish-fraim-only": "node scripts/publish-fraim.js",
43
+ "publish-both-manual": "node scripts/publish-both.js",
44
+ "validate:registry": "tsx scripts/verify-registry-paths.ts && npm run validate:jobs && npm run validate:skills && npm run validate:registry-references && npm run validate:platform-agnostic && npm run validate:template-namespaces && npm run validate:config-fallbacks && npm run validate:bootstrap-config-coverage && npm run validate:provider-action-mappings && npm run validate:fidelity && npm run validate:config-tokens && npm run validate:brain-mapping && npm run validate:template-syntax",
45
+ "validate:registry-references": "tsx scripts/validate-registry-references.ts",
46
+ "validate:brain-mapping": "tsx scripts/validate-brain-mapping.ts",
47
+ "validate:fraim-pro-assets": "tsx scripts/validate-fraim-pro-assets.ts",
48
+ "validate:jobs": "tsx scripts/validate-jobs.ts",
49
+ "validate:platform-agnostic": "tsx scripts/validate-platform-agnostic.ts",
50
+ "validate:skills": "tsx scripts/validate-skills.ts",
51
+ "validate:template-namespaces": "tsx scripts/validate-template-namespaces.ts",
52
+ "validate:config-fallbacks": "tsx scripts/validate-config-fallbacks.ts",
53
+ "validate:bootstrap-config-coverage": "tsx scripts/validate-bootstrap-config-coverage.ts",
54
+ "validate:provider-action-mappings": "tsx scripts/validate-provider-action-mappings.ts",
55
+ "validate:fidelity": "tsx scripts/validate-fidelity.ts",
56
+ "validate:config-tokens": "tsx scripts/validate-config-tokens.ts",
57
+ "validate:template-syntax": "tsx scripts/validate-template-syntax.ts",
58
+ "validate:backup": "bash scripts/backup/validate-pitr-restore.sh"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/mathursrus/FRAIM.git"
63
+ },
64
+ "keywords": [
65
+ "fraim",
66
+ "ai-management",
67
+ "ai-coordination",
68
+ "ai-agents",
69
+ "multi-agent",
70
+ "github",
71
+ "automation",
72
+ "gitops",
73
+ "cursor",
74
+ "claude",
75
+ "windsurf",
76
+ "rigor",
77
+ "enterprise",
78
+ "framework",
79
+ "ai-managers"
80
+ ],
81
+ "author": "Sid Mathur <sid.mathur@gmail.com>",
82
+ "license": "MIT",
83
+ "bugs": {
84
+ "url": "https://github.com/mathursrus/FRAIM/issues"
85
+ },
86
+ "homepage": "https://github.com/mathursrus/FRAIM#readme",
87
+ "engines": {
88
+ "node": ">=16.0.0"
89
+ },
90
+ "devDependencies": {
91
+ "@playwright/test": "^1.58.2",
92
+ "@types/adm-zip": "^0.5.7",
93
+ "@types/cors": "^2.8.19",
94
+ "@types/express": "^5.0.6",
95
+ "@types/node": "^20.0.0",
96
+ "@types/node-fetch": "^2.6.13",
97
+ "@types/prompts": "^2.4.9",
98
+ "@types/semver": "^7.7.1",
99
+ "fast-glob": "^3.3.3",
100
+ "html-to-docx": "^1.8.0",
101
+ "markdown-it": "^14.1.1",
102
+ "markdown-it-highlightjs": "^4.3.0",
103
+ "playwright": "^1.58.2",
104
+ "pptxgenjs": "^4.0.1",
105
+ "puppeteer": "^24.36.1",
106
+ "qrcode": "^1.5.4",
107
+ "sharp": "^0.34.5",
108
+ "tsx": "^4.0.0",
109
+ "typescript": "^5.0.0"
110
+ },
111
+ "files": [
112
+ "dist/src/local-mcp-server/",
113
+ "dist/src/cli/",
114
+ "dist/src/core/",
115
+ "bin/fraim.js",
116
+ "bin/fraim-mcp.js",
117
+ "index.js",
118
+ "README.md",
119
+ "CHANGELOG.md",
120
+ "LICENSE",
121
+ "package.json"
122
+ ],
123
+ "publishConfig": {
124
+ "access": "public"
125
+ },
126
+ "dependencies": {
127
+ "@octokit/rest": "^22.0.1",
128
+ "adm-zip": "^0.5.16",
129
+ "axios": "^1.7.0",
130
+ "chalk": "4.1.2",
131
+ "commander": "^14.0.2",
132
+ "cors": "^2.8.5",
133
+ "dotenv": "^16.4.7",
134
+ "express": "^5.2.1",
135
+ "mongodb": "^7.0.0",
136
+ "node-edge-tts": "^1.2.10",
137
+ "nodemailer": "^8.0.3",
138
+ "prompts": "^2.4.2",
139
+ "resend": "^6.9.3",
140
+ "semver": "^7.7.4",
141
+ "stripe": "^20.3.1",
142
+ "toml": "^3.0.0",
143
+ "tree-kill": "^1.2.2",
144
+ "xml2js": "^0.6.2"
145
+ }
146
+ }