@forcefield/mcp-server 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../setup/wizard.ts","../../setup/ide-detect.ts","../../setup/ide-adapters/claude-code.ts","../../setup/ide-adapters/mcp-runtime.ts","../../setup/ide-adapters/cursor.ts","../../setup/ide-adapters/codex.ts","../../setup/ide-adapters/windsurf.ts","../../setup/ide-adapters/index.ts","../../setup/auth.ts","../../setup/index.ts"],"sourcesContent":["/**\n * setup/wizard.ts — Interactive setup wizard using @clack/prompts.\n *\n * Orchestrates the full setup flow:\n * 1. Welcome banner\n * 2. Existing setup check\n * 3. IDE detection + selection\n * 4. Mode selection (Core vs Full)\n * 5. Authentication\n * 6. Configure + install\n * 7. Verify + success\n */\n\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport figlet from 'figlet';\nimport gradient from 'gradient-string';\nimport { readFile, writeFile, access } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Ide, SetupMode, SetupConfig, CliArgs } from './types.js';\nimport { detectIde } from './ide-detect.js';\nimport { getAdapter, IDE_CHOICES } from './ide-adapters/index.js';\nimport {\n authenticateAccountAndCreateApiKey,\n authenticateLocalAndCreateApiKey,\n authenticateWithRetry,\n} from './auth.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst WORKFLOW_DIR_CANDIDATES = [\n join(__dirname, '..', 'workflows'),\n join(__dirname, '..', '..', 'workflows'),\n];\nconst CONFIG_FILE = '.forcefield.json';\nconst VERSION = '0.1.0';\nconst BANNER_TEXT = 'Forcefield';\nconst BANNER_FONT = 'Rowan Cap';\nconst BANNER_MIN_COLUMNS = 80;\nconst BANNER_FALLBACK_TEXT = ' Forcefield';\nconst BANNER_SUBTITLE = ' Corporate compliance copilot\\n';\n// rainbow2-like palette cycling red -> violet for the terminal banner\nconst BANNER_GRADIENT_STOPS = ['#ff3b3b', '#ff9f1a', '#ffe74c', '#2ed573', '#1e90ff', '#7d5fff', '#e056fd'];\nconst bannerGradient = gradient(BANNER_GRADIENT_STOPS);\n\nasync function resolveWorkflowsDir(): Promise<string> {\n for (const dir of WORKFLOW_DIR_CANDIDATES) {\n try {\n await access(dir);\n return dir;\n } catch {\n // try next candidate\n }\n }\n throw new Error(\n `Unable to locate workflow templates. Checked: ${WORKFLOW_DIR_CANDIDATES.join(', ')}`,\n );\n}\n\n/**\n * Run the interactive setup wizard.\n */\nexport async function runWizard(projectDir: string, args: CliArgs): Promise<void> {\n // --status: read-only check\n if (args.status) {\n await showStatus(projectDir);\n return;\n }\n\n // Non-interactive mode\n if (args.ide && args.mode && (args.token || args.localAuth || (args.email && args.password))) {\n await runNonInteractive(projectDir, args);\n return;\n }\n\n // Interactive mode\n await runInteractive(projectDir, args);\n}\n\n// === Welcome Banner ===\n\nfunction showBanner(): void {\n const cols = process.stdout.columns || 80;\n\n if (cols >= BANNER_MIN_COLUMNS) {\n try {\n const banner = figlet.textSync(BANNER_TEXT, { font: BANNER_FONT });\n const colored = applyBannerGradient(banner);\n console.log(colored);\n } catch {\n // figlet font not available — use fallback\n console.log(pc.bold(applyBannerGradient(BANNER_FALLBACK_TEXT)));\n }\n } else {\n console.log(pc.bold(applyBannerGradient(BANNER_FALLBACK_TEXT)));\n }\n console.log(pc.dim(BANNER_SUBTITLE));\n}\n\nfunction applyBannerGradient(text: string): string {\n return bannerGradient.multiline(text);\n}\n\n// === Status Check ===\n\nasync function showStatus(projectDir: string): Promise<void> {\n const config = await readConfig(projectDir);\n\n if (!config) {\n console.log(pc.yellow('No Forcefield setup found in this directory.'));\n console.log(`Run ${pc.cyan('forcefield-setup')} to get started.`);\n process.exit(1);\n }\n\n console.log(pc.bold('Forcefield Setup Status'));\n console.log(` IDE: ${pc.cyan(config.ide)}`);\n console.log(` Mode: ${pc.cyan(config.mode)}`);\n console.log(` Version: ${pc.cyan(config.version)}`);\n console.log(` Installed: ${pc.dim(config.installed_at)}`);\n}\n\n// === Non-Interactive Mode ===\n\nasync function runNonInteractive(projectDir: string, args: CliArgs): Promise<void> {\n const ide = args.ide!;\n const mode = args.mode!;\n let apiKey = args.token;\n\n if (args.localAuth) {\n if (!args.email || !args.password) {\n console.error(pc.red('Non-interactive local auth requires --email and --password.'));\n process.exit(1);\n }\n\n const auth = await authenticateLocalAndCreateApiKey(args.email, args.password, {\n label: 'Setup Wizard (Local)',\n });\n apiKey = auth.apiKey;\n console.log(`Created local API key for ${auth.email}.`);\n } else if (!apiKey && args.email && args.password) {\n const auth = await authenticateAccountAndCreateApiKey(args.email, args.password, {\n label: 'Setup Wizard Key',\n localDev: isLocalSetupMode(args),\n });\n apiKey = auth.apiKey;\n console.log(`Created API key for ${auth.email}.`);\n } else if (apiKey) {\n const verified = await authenticateWithRetry(async () => apiKey ?? null, {\n localDev: isLocalSetupMode(args),\n });\n if (!verified.valid) {\n console.error(pc.red(verified.error ?? 'API key validation failed.'));\n process.exit(1);\n }\n apiKey = verified.apiKey;\n }\n\n if (!apiKey) {\n console.error(pc.red('Missing auth input. Pass --token, --local-auth, or --email/--password.'));\n process.exit(1);\n }\n\n console.log(`Setting up Forcefield for ${ide} in ${mode} mode...`);\n\n await configure(projectDir, ide, mode, apiKey);\n\n console.log(pc.green('Setup complete!'));\n for (const line of getPostSetupInstructions(ide, mode, projectDir)) {\n console.log(line);\n }\n}\n\n// === Interactive Mode ===\n\nasync function runInteractive(projectDir: string, preArgs: CliArgs): Promise<void> {\n showBanner();\n\n p.intro(pc.bgCyan(pc.black(' Forcefield Setup ')));\n\n // Step 1: Check existing setup\n const existingConfig = await readConfig(projectDir);\n if (existingConfig) {\n const updateChoice = await p.select({\n message: `Forcefield is already set up (${existingConfig.ide}, ${existingConfig.mode} mode).`,\n options: [\n { value: 'update', label: 'Update existing setup', hint: 'Refresh workflows + auth' },\n { value: 'fresh', label: 'Start fresh', hint: 'Remove existing config and reconfigure' },\n ],\n });\n\n if (p.isCancel(updateChoice)) {\n p.cancel('Setup cancelled.');\n process.exit(0);\n }\n\n if (updateChoice === 'update') {\n // Re-run configure with existing settings\n const apiKey = await promptApiKey();\n if (!apiKey) return;\n await configure(projectDir, existingConfig.ide, existingConfig.mode, apiKey);\n showSuccess(existingConfig.ide, existingConfig.mode, projectDir);\n return;\n }\n // fresh → continue with full wizard\n }\n\n // Step 2: IDE detection + selection\n const ide = preArgs.ide ?? await promptIde(projectDir);\n if (!ide) return;\n\n // Step 3: Mode selection\n const mode = preArgs.mode ?? await promptMode();\n if (!mode) return;\n\n // Step 4: Authentication\n const apiKey = preArgs.token ?? await promptApiKey(preArgs);\n if (!apiKey) return;\n\n // Step 5: Configure + install\n const s = p.spinner();\n s.start('Configuring Forcefield...');\n\n try {\n await configure(projectDir, ide, mode, apiKey);\n s.stop('Configuration complete.');\n } catch (err) {\n s.stop('Configuration failed.');\n p.cancel(err instanceof Error ? err.message : 'Unknown error');\n process.exit(1);\n }\n\n // Step 6: Success\n showSuccess(ide, mode, projectDir);\n}\n\n// === Prompts ===\n\nasync function promptIde(projectDir: string): Promise<Ide | null> {\n const detection = await detectIde(projectDir);\n\n if (detection.detected) {\n const adapter = getAdapter(detection.detected);\n const confirm = await p.confirm({\n message: `Detected ${adapter.name} (${detection.markers[0]!.marker} found). Use ${adapter.name}?`,\n });\n\n if (p.isCancel(confirm)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n\n if (confirm) return detection.detected;\n }\n\n const choice = await p.select({\n message: 'Which IDE do you use?',\n options: IDE_CHOICES,\n });\n\n if (p.isCancel(choice)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n\n return choice as Ide;\n}\n\nasync function promptMode(): Promise<SetupMode | null> {\n const choice = await p.select({\n message: 'Installation type:',\n options: [\n { value: 'full' as const, label: 'Full — MCP server + guided workflows', hint: 'Recommended' },\n { value: 'core' as const, label: 'Core — MCP server only', hint: 'Minimal' },\n ],\n });\n\n if (p.isCancel(choice)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n\n return choice as SetupMode;\n}\n\nasync function promptApiKey(preArgs?: CliArgs): Promise<string | null> {\n if (preArgs?.localAuth) {\n const preEmail = preArgs.email ?? await promptTextValue('Local test email:', 'alice@test.local');\n const prePassword = preArgs.password ?? await promptTextValue('Local test password:', 'password123', true);\n if (!preEmail || !prePassword) return null;\n\n try {\n const result = await authenticateLocalAndCreateApiKey(preEmail, prePassword, {\n label: 'Setup Wizard (Local)',\n });\n p.log.success(`Created local API key for ${result.email}.`);\n return result.apiKey;\n } catch (err) {\n p.log.error(err instanceof Error ? err.message : 'Failed local auth');\n return null;\n }\n }\n\n const localMode = isLocalSetupMode(preArgs);\n const methodOptions = [\n { value: 'account', label: 'Sign in / create account + API key', hint: 'Recommended' },\n { value: 'paste', label: 'Paste existing API key', hint: 'Use an existing ff_live_... key' },\n ];\n\n if (localMode) {\n methodOptions.push({\n value: 'local',\n label: 'Create local account + API key',\n hint: 'For local Supabase testing',\n });\n }\n\n const method = await p.select({\n message: 'How do you want to authenticate?',\n options: methodOptions,\n });\n\n if (p.isCancel(method)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n\n if (method === 'account') {\n const email = await promptTextValue('Account email:', preArgs?.email ?? 'you@company.com');\n const password = await promptTextValue('Account password:', '••••••••', true);\n if (!email || !password) return null;\n\n try {\n const result = await authenticateAccountAndCreateApiKey(email, password, {\n label: 'Setup Wizard Key',\n localDev: localMode,\n });\n p.log.success(`Created API key for ${result.email}.`);\n return result.apiKey;\n } catch (err) {\n p.log.error(err instanceof Error ? err.message : 'Account authentication failed');\n return null;\n }\n }\n\n if (method === 'local') {\n const email = await promptTextValue('Local test email:', 'alice@test.local');\n const password = await promptTextValue('Local test password:', 'password123', true);\n if (!email || !password) return null;\n\n try {\n const result = await authenticateLocalAndCreateApiKey(email, password, {\n label: 'Setup Wizard (Local)',\n });\n p.log.success(`Created local API key for ${result.email}.`);\n return result.apiKey;\n } catch (err) {\n p.log.error(err instanceof Error ? err.message : 'Failed local auth');\n return null;\n }\n }\n\n const auth = await authenticateWithRetry(\n async () => {\n const key = await p.text({\n message: 'Enter your Forcefield API key:',\n placeholder: 'ff_live_...',\n validate: (value) => {\n if (!value) return 'API key is required.';\n if (!value.startsWith('ff_live_')) return 'Key must start with \"ff_live_\".';\n if (value.length < 20) return 'Key seems too short.';\n return undefined;\n },\n });\n\n if (p.isCancel(key)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n\n return key;\n },\n {\n localDev: localMode,\n },\n );\n\n if (!auth.valid) {\n p.log.error(auth.error ?? 'Authentication failed. Check your credentials and retry.');\n return null;\n }\n\n return auth.apiKey;\n}\n\nfunction isLocalSetupMode(preArgs?: CliArgs): boolean {\n if (preArgs?.localAuth) return true;\n const localValue = process.env.FORCEFIELD_LOCAL_DEV;\n if (localValue) {\n const normalized = localValue.trim().toLowerCase();\n if (['1', 'true', 'yes', 'on'].includes(normalized)) {\n return true;\n }\n }\n\n const supabaseUrl = process.env.SUPABASE_URL;\n if (!supabaseUrl) return false;\n try {\n const parsed = new URL(supabaseUrl);\n return parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost';\n } catch {\n return false;\n }\n}\n\nasync function promptTextValue(\n message: string,\n placeholder: string,\n secret = false,\n): Promise<string | null> {\n const value = await p.text({\n message,\n placeholder,\n ...(secret ? { placeholder: '••••••••' } : {}),\n validate: (input) => {\n if (!input) return 'This field is required.';\n return undefined;\n },\n });\n\n if (p.isCancel(value)) {\n p.cancel('Setup cancelled.');\n return null;\n }\n return value;\n}\n\n// === Configure ===\n\nasync function configure(\n projectDir: string,\n ide: Ide,\n mode: SetupMode,\n apiKey: string,\n): Promise<void> {\n const adapter = getAdapter(ide);\n\n // Configure MCP\n await adapter.configureMcp(projectDir, apiKey);\n\n // Install workflows (full mode only)\n let workflowCount = 0;\n if (mode === 'full') {\n const workflowsDir = await resolveWorkflowsDir();\n workflowCount = await adapter.installWorkflows(projectDir, workflowsDir);\n }\n\n // Write config\n const config: SetupConfig = {\n ide,\n mode,\n installed_at: new Date().toISOString(),\n version: VERSION,\n };\n await writeConfig(projectDir, config);\n\n void workflowCount; // Used in logging below\n}\n\nfunction getPostSetupInstructions(ide: Ide, mode: SetupMode, projectDir: string): string[] {\n const repo = pc.cyan(projectDir);\n const dashboardHint =\n `Optional beta companion: run local dashboard in the Forcefield repo via ${pc.cyan('pnpm run dev:dashboard')} and sign in at ${pc.cyan('http://localhost:3000')}.`;\n\n if (mode === 'core') {\n return [\n `Next: open your coding agent in ${repo} and use Forcefield MCP tools directly (e.g. ${pc.cyan('ff_system(action: \"health\")')}).`,\n dashboardHint,\n ];\n }\n\n const common = [\n `Next: start your coding agent in ${repo}.`,\n `Then in IDE chat (not terminal), run ${pc.cyan('/ff-onboard')} to set up your company profile and deadlines.`,\n `Optional: run ${pc.cyan('/ff-start')} only if you want a command primer or connection troubleshooting.`,\n dashboardHint,\n ];\n\n if (ide === 'claude-code') {\n return [\n ...common,\n `If tools are missing, run ${pc.cyan('/mcp')} and confirm ${pc.cyan('forcefield')} is connected.`,\n ];\n }\n\n return common;\n}\n\nfunction showSuccess(ide: Ide, mode: SetupMode, projectDir: string): void {\n const adapter = getAdapter(ide);\n const workflowStatus =\n mode === 'full'\n ? `${pc.green('✓')} Project workflow commands installed`\n : `${pc.yellow('•')} Core mode selected (no slash-command workflows installed)`;\n\n p.note(\n [\n `${pc.green('✓')} MCP server configured`,\n workflowStatus,\n `${pc.green('✓')} IDE: ${adapter.name}`,\n ].join('\\n'),\n 'Setup Complete',\n );\n\n p.outro(getPostSetupInstructions(ide, mode, projectDir).join('\\n'));\n}\n\n// === Config File ===\n\nasync function readConfig(projectDir: string): Promise<SetupConfig | null> {\n const configPath = join(projectDir, CONFIG_FILE);\n try {\n await access(configPath);\n const raw = await readFile(configPath, 'utf-8');\n return JSON.parse(raw) as SetupConfig;\n } catch {\n return null;\n }\n}\n\nasync function writeConfig(projectDir: string, config: SetupConfig): Promise<void> {\n const configPath = join(projectDir, CONFIG_FILE);\n await writeFile(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport {\n readConfig,\n writeConfig,\n CONFIG_FILE,\n VERSION,\n BANNER_FONT,\n BANNER_MIN_COLUMNS,\n BANNER_GRADIENT_STOPS,\n showBanner,\n applyBannerGradient,\n getPostSetupInstructions,\n};\n","/**\n * setup/ide-detect.ts — Auto-detect IDE from project directory markers.\n */\n\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Ide } from './types.js';\n\ninterface DetectionResult {\n detected: Ide | null;\n markers: Array<{ ide: Ide; marker: string }>;\n}\n\nconst IDE_MARKERS: Array<{ ide: Ide; marker: string; isDir: boolean }> = [\n { ide: 'claude-code', marker: '.claude', isDir: true },\n { ide: 'cursor', marker: '.cursor', isDir: true },\n { ide: 'codex', marker: 'AGENTS.md', isDir: false },\n { ide: 'windsurf', marker: '.windsurfrules', isDir: false },\n];\n\nexport async function detectIde(projectDir: string): Promise<DetectionResult> {\n const found: Array<{ ide: Ide; marker: string }> = [];\n\n for (const { ide, marker } of IDE_MARKERS) {\n const markerPath = join(projectDir, marker);\n try {\n await access(markerPath);\n found.push({ ide, marker });\n } catch {\n // Marker not found — skip\n }\n }\n\n return {\n detected: found.length === 1 ? found[0]!.ide : null,\n markers: found,\n };\n}\n\nexport { IDE_MARKERS };\n","/**\n * IDE adapter for Claude Code.\n * Workflows → .claude/commands/ff-*.md\n * MCP → project-scoped .mcp.json entry\n */\n\nimport { mkdir, readdir, copyFile, rm, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IdeAdapter } from '../types.js';\nimport { buildMcpEnv, getMcpLaunchConfig } from './mcp-runtime.js';\n\nexport const claudeCodeAdapter: IdeAdapter = {\n name: 'Claude Code',\n\n async configureMcp(projectDir: string, apiKey: string): Promise<void> {\n // Ensure Claude config folder exists for slash-command workflows.\n const claudeDir = join(projectDir, '.claude');\n await mkdir(claudeDir, { recursive: true });\n\n // Configure project-scoped MCP entry used by Claude Code.\n const launch = await getMcpLaunchConfig();\n const mcpPath = join(projectDir, '.mcp.json');\n\n let existing: Record<string, unknown> = {};\n try {\n existing = JSON.parse(await readFile(mcpPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n // file missing/invalid: start fresh\n }\n\n const existingServers =\n typeof existing.mcpServers === 'object' && existing.mcpServers != null\n ? (existing.mcpServers as Record<string, unknown>)\n : {};\n\n existingServers.forcefield = {\n command: launch.command,\n args: launch.args,\n env: buildMcpEnv(apiKey),\n };\n\n const nextConfig = {\n ...existing,\n mcpServers: existingServers,\n };\n\n await writeFile(mcpPath, JSON.stringify(nextConfig, null, 2) + '\\n', 'utf-8');\n },\n\n async installWorkflows(projectDir: string, workflowsDir: string): Promise<number> {\n const commandsDir = join(projectDir, '.claude', 'commands');\n await mkdir(commandsDir, { recursive: true });\n\n const files = await readdir(workflowsDir);\n const mdFiles = files.filter((f) => f.startsWith('ff-') && f.endsWith('.md'));\n\n for (const file of mdFiles) {\n await copyFile(join(workflowsDir, file), join(commandsDir, file));\n }\n\n return mdFiles.length;\n },\n\n async removeWorkflows(projectDir: string): Promise<void> {\n const commandsDir = join(projectDir, '.claude', 'commands');\n try {\n const files = await readdir(commandsDir);\n for (const file of files) {\n if (file.startsWith('ff-') && file.endsWith('.md')) {\n await rm(join(commandsDir, file));\n }\n }\n } catch {\n // Directory doesn't exist — nothing to remove\n }\n },\n};\n","import { access } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst LOCAL_ENTRY_CANDIDATES = [\n join(__dirname, '..', '..', 'build', 'index.js'),\n join(__dirname, '..', '..', 'index.js'),\n];\n\nconst FORWARDED_ENV_KEYS = [\n 'SUPABASE_URL',\n 'SUPABASE_ANON_KEY',\n 'FORCEFIELD_LOCAL_DEV',\n 'FORCEFIELD_DISABLE_GATING',\n 'FORCEFIELD_LOCAL_DATA_FALLBACK',\n] as const;\n\nfunction isTruthy(value: string | undefined): boolean {\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}\n\nasync function resolveLocalEntry(): Promise<string | null> {\n for (const candidate of LOCAL_ENTRY_CANDIDATES) {\n try {\n await access(candidate);\n return candidate;\n } catch {\n // try next candidate\n }\n }\n return null;\n}\n\nexport async function getMcpLaunchConfig(): Promise<{ command: string; args: string[] }> {\n if (isTruthy(process.env.FORCEFIELD_LOCAL_DEV) || isTruthy(process.env.FORCEFIELD_SETUP_USE_LOCAL_BUILD)) {\n const localEntry = await resolveLocalEntry();\n if (localEntry) {\n return {\n command: process.execPath,\n args: [localEntry],\n };\n }\n }\n\n return {\n command: 'npx',\n args: ['-y', '@forcefield/mcp-server', 'forcefield-mcp'],\n };\n}\n\nexport function buildMcpEnv(apiKey: string): Record<string, string> {\n const env: Record<string, string> = {\n FORCEFIELD_API_KEY: apiKey,\n };\n\n for (const key of FORWARDED_ENV_KEYS) {\n const value = process.env[key];\n if (value && value.trim().length > 0) {\n env[key] = value;\n }\n }\n\n return env;\n}\n","/**\n * IDE adapter for Cursor.\n * Workflows → .cursor/rules/ff-*.md\n * MCP → .cursor/mcp.json\n */\n\nimport { mkdir, readdir, copyFile, rm, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IdeAdapter } from '../types.js';\nimport { buildMcpEnv, getMcpLaunchConfig } from './mcp-runtime.js';\n\ntype CursorMcpConfig = {\n mcpServers?: Record<string, { command: string; args: string[]; env?: Record<string, string> }>;\n};\n\nexport const cursorAdapter: IdeAdapter = {\n name: 'Cursor',\n\n async configureMcp(projectDir: string, apiKey: string): Promise<void> {\n const cursorDir = join(projectDir, '.cursor');\n await mkdir(cursorDir, { recursive: true });\n\n const configPath = join(cursorDir, 'mcp.json');\n const launch = await getMcpLaunchConfig();\n\n let existing: CursorMcpConfig = {};\n try {\n existing = JSON.parse(await readFile(configPath, 'utf-8')) as CursorMcpConfig;\n } catch {\n // file missing/invalid: overwrite with fresh config\n }\n\n const existingServers =\n typeof existing.mcpServers === 'object' && existing.mcpServers != null\n ? existing.mcpServers\n : {};\n\n existingServers.forcefield = {\n command: launch.command,\n args: launch.args,\n env: buildMcpEnv(apiKey),\n };\n\n const merged: CursorMcpConfig = {\n ...existing,\n mcpServers: existingServers,\n };\n\n await writeFile(configPath, JSON.stringify(merged, null, 2) + '\\n', 'utf-8');\n },\n\n async installWorkflows(projectDir: string, workflowsDir: string): Promise<number> {\n const rulesDir = join(projectDir, '.cursor', 'rules');\n await mkdir(rulesDir, { recursive: true });\n\n const files = await readdir(workflowsDir);\n const mdFiles = files.filter((f) => f.startsWith('ff-') && f.endsWith('.md'));\n\n for (const file of mdFiles) {\n await copyFile(join(workflowsDir, file), join(rulesDir, file));\n }\n\n return mdFiles.length;\n },\n\n async removeWorkflows(projectDir: string): Promise<void> {\n const rulesDir = join(projectDir, '.cursor', 'rules');\n try {\n const files = await readdir(rulesDir);\n for (const file of files) {\n if (file.startsWith('ff-') && file.endsWith('.md')) {\n await rm(join(rulesDir, file));\n }\n }\n } catch {\n // Directory doesn't exist\n }\n },\n};\n","/**\n * IDE adapter for Codex (OpenAI).\n * Workflows → appended to AGENTS.md with markers\n * MCP → .codex/config.toml or project-level config\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IdeAdapter } from '../types.js';\nimport { buildMcpEnv, getMcpLaunchConfig } from './mcp-runtime.js';\n\nconst START_MARKER = '<!-- forcefield:start -->';\nconst END_MARKER = '<!-- forcefield:end -->';\nconst MCP_START_MARKER = '# forcefield:mcp:start';\nconst MCP_END_MARKER = '# forcefield:mcp:end';\n\nexport const codexAdapter: IdeAdapter = {\n name: 'Codex',\n\n async configureMcp(projectDir: string, apiKey: string): Promise<void> {\n const codexDir = join(projectDir, '.codex');\n await mkdir(codexDir, { recursive: true });\n\n const configPath = join(codexDir, 'config.toml');\n const launch = await getMcpLaunchConfig();\n const env = buildMcpEnv(apiKey);\n\n const forcefieldBlock = buildForcefieldTomlBlock(launch.command, launch.args, env);\n\n let existing = '';\n try {\n existing = await readFile(configPath, 'utf-8');\n } catch {\n // file missing — create new\n }\n\n const cleaned = removeTomlForcefieldSection(existing).trim();\n const final = cleaned ? `${cleaned}\\n\\n${forcefieldBlock}\\n` : `${forcefieldBlock}\\n`;\n await writeFile(configPath, final, 'utf-8');\n },\n\n async installWorkflows(projectDir: string, workflowsDir: string): Promise<number> {\n const agentsPath = join(projectDir, 'AGENTS.md');\n const { readdir } = await import('node:fs/promises');\n\n // Read all workflow files\n const files = await readdir(workflowsDir);\n const mdFiles = files.filter((f) => f.startsWith('ff-') && f.endsWith('.md'));\n\n const workflowContents: string[] = [];\n for (const file of mdFiles) {\n const content = await readFile(join(workflowsDir, file), 'utf-8');\n workflowContents.push(content);\n }\n\n const forcefieldSection = [\n START_MARKER,\n '',\n '# Forcefield Workflows',\n '',\n 'The following workflows are available for compliance management:',\n '',\n ...workflowContents.map((c) => c + '\\n\\n---\\n'),\n END_MARKER,\n ].join('\\n');\n\n // Read existing AGENTS.md or create new\n let existing = '';\n try {\n existing = await readFile(agentsPath, 'utf-8');\n } catch {\n // File doesn't exist — will create\n }\n\n // Remove existing Forcefield section if present\n const cleaned = removeSection(existing);\n\n // Append new section\n const final = cleaned.trim()\n ? cleaned.trim() + '\\n\\n' + forcefieldSection\n : forcefieldSection;\n\n await writeFile(agentsPath, final, 'utf-8');\n return mdFiles.length;\n },\n\n async removeWorkflows(projectDir: string): Promise<void> {\n const agentsPath = join(projectDir, 'AGENTS.md');\n try {\n const content = await readFile(agentsPath, 'utf-8');\n const cleaned = removeSection(content);\n await writeFile(agentsPath, cleaned, 'utf-8');\n } catch {\n // File doesn't exist — nothing to remove\n }\n },\n};\n\nfunction removeSection(content: string): string {\n const startIdx = content.indexOf(START_MARKER);\n const endIdx = content.indexOf(END_MARKER);\n\n if (startIdx === -1 || endIdx === -1) return content;\n\n const before = content.slice(0, startIdx).trimEnd();\n const after = content.slice(endIdx + END_MARKER.length).trimStart();\n\n return before + (after ? '\\n\\n' + after : '');\n}\n\nfunction removeTomlForcefieldSection(content: string): string {\n const markerStart = content.indexOf(MCP_START_MARKER);\n const markerEnd = content.indexOf(MCP_END_MARKER);\n\n if (markerStart !== -1 && markerEnd !== -1) {\n const before = content.slice(0, markerStart).trimEnd();\n const after = content.slice(markerEnd + MCP_END_MARKER.length).trimStart();\n return before + (after ? '\\n\\n' + after : '');\n }\n\n // Backward-compatible cleanup if a previous config wrote unmanaged sections.\n return content\n .replace(/^\\[mcp_servers\\.forcefield\\]\\n[\\s\\S]*?(?=^\\[|$)/gm, '')\n .replace(/^\\[mcp_servers\\.forcefield\\.env\\]\\n[\\s\\S]*?(?=^\\[|$)/gm, '')\n .trim();\n}\n\nfunction buildForcefieldTomlBlock(\n command: string,\n args: string[],\n env: Record<string, string>,\n): string {\n const envLines = Object.entries(env)\n .map(([key, value]) => `${key} = \"${escapeTomlString(value)}\"`);\n\n return [\n MCP_START_MARKER,\n '[mcp_servers.forcefield]',\n `command = \"${escapeTomlString(command)}\"`,\n `args = [${args.map((arg) => `\"${escapeTomlString(arg)}\"`).join(', ')}]`,\n '[mcp_servers.forcefield.env]',\n ...envLines,\n MCP_END_MARKER,\n ].join('\\n');\n}\n\nfunction escapeTomlString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n","/**\n * IDE adapter for Windsurf.\n * Workflows → appended to .windsurfrules with markers\n * MCP → Windsurf MCP config\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IdeAdapter } from '../types.js';\nimport { buildMcpEnv, getMcpLaunchConfig } from './mcp-runtime.js';\n\nconst START_MARKER = '<!-- forcefield:start -->';\nconst END_MARKER = '<!-- forcefield:end -->';\n\nexport const windsurfAdapter: IdeAdapter = {\n name: 'Windsurf',\n\n async configureMcp(projectDir: string, apiKey: string): Promise<void> {\n const configDir = join(projectDir, '.windsurf');\n await mkdir(configDir, { recursive: true });\n\n const configPath = join(configDir, 'mcp_config.json');\n const launch = await getMcpLaunchConfig();\n\n let existing: Record<string, unknown> = {};\n try {\n existing = JSON.parse(await readFile(configPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n // file missing/invalid: start fresh\n }\n\n const existingServers =\n typeof existing.mcpServers === 'object' && existing.mcpServers != null\n ? (existing.mcpServers as Record<string, unknown>)\n : {};\n\n existingServers.forcefield = {\n command: launch.command,\n args: launch.args,\n env: buildMcpEnv(apiKey),\n };\n\n const merged = {\n ...existing,\n mcpServers: existingServers,\n };\n\n await writeFile(configPath, JSON.stringify(merged, null, 2) + '\\n', 'utf-8');\n },\n\n async installWorkflows(projectDir: string, workflowsDir: string): Promise<number> {\n const rulesPath = join(projectDir, '.windsurfrules');\n const { readdir } = await import('node:fs/promises');\n\n const files = await readdir(workflowsDir);\n const mdFiles = files.filter((f) => f.startsWith('ff-') && f.endsWith('.md'));\n\n const workflowContents: string[] = [];\n for (const file of mdFiles) {\n const content = await readFile(join(workflowsDir, file), 'utf-8');\n workflowContents.push(content);\n }\n\n const forcefieldSection = [\n START_MARKER,\n '',\n '# Forcefield Workflows',\n '',\n ...workflowContents.map((c) => c + '\\n\\n---\\n'),\n END_MARKER,\n ].join('\\n');\n\n let existing = '';\n try {\n existing = await readFile(rulesPath, 'utf-8');\n } catch {\n // File doesn't exist\n }\n\n const cleaned = removeSection(existing);\n const final = cleaned.trim()\n ? cleaned.trim() + '\\n\\n' + forcefieldSection\n : forcefieldSection;\n\n await writeFile(rulesPath, final, 'utf-8');\n return mdFiles.length;\n },\n\n async removeWorkflows(projectDir: string): Promise<void> {\n const rulesPath = join(projectDir, '.windsurfrules');\n try {\n const content = await readFile(rulesPath, 'utf-8');\n const cleaned = removeSection(content);\n await writeFile(rulesPath, cleaned, 'utf-8');\n } catch {\n // File doesn't exist\n }\n },\n};\n\nfunction removeSection(content: string): string {\n const startIdx = content.indexOf(START_MARKER);\n const endIdx = content.indexOf(END_MARKER);\n\n if (startIdx === -1 || endIdx === -1) return content;\n\n const before = content.slice(0, startIdx).trimEnd();\n const after = content.slice(endIdx + END_MARKER.length).trimStart();\n\n return before + (after ? '\\n\\n' + after : '');\n}\n","/**\n * IDE adapter registry.\n */\n\nimport type { Ide, IdeAdapter } from '../types.js';\nimport { claudeCodeAdapter } from './claude-code.js';\nimport { cursorAdapter } from './cursor.js';\nimport { codexAdapter } from './codex.js';\nimport { windsurfAdapter } from './windsurf.js';\n\nconst adapters: Record<Ide, IdeAdapter> = {\n 'claude-code': claudeCodeAdapter,\n 'cursor': cursorAdapter,\n 'codex': codexAdapter,\n 'windsurf': windsurfAdapter,\n};\n\nexport function getAdapter(ide: Ide): IdeAdapter {\n return adapters[ide];\n}\n\nexport const IDE_CHOICES: Array<{ value: Ide; label: string; hint: string }> = [\n { value: 'claude-code', label: 'Claude Code', hint: '.claude/commands/' },\n { value: 'cursor', label: 'Cursor', hint: '.cursor/rules/' },\n { value: 'codex', label: 'Codex (OpenAI)', hint: 'AGENTS.md' },\n { value: 'windsurf', label: 'Windsurf', hint: '.windsurfrules' },\n];\n","/**\n * setup/auth.ts — Authentication and API key issuance for setup wizard.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport { createClient } from '@supabase/supabase-js';\n\nconst API_KEY_PREFIX = 'ff_live_';\nconst MAX_RETRIES = 3;\nconst LOCAL_SUPABASE_URL = 'http://127.0.0.1:54321';\nconst LOCAL_SUPABASE_ANON_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';\nconst LOCAL_SUPABASE_SERVICE_ROLE_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU';\n\nexport interface AuthResult {\n valid: boolean;\n apiKey: string;\n error?: string;\n}\n\nexport interface LocalAuthResult {\n apiKey: string;\n userId: string;\n email: string;\n}\n\ninterface SetupAuthOptions {\n supabaseUrl?: string;\n anonKey?: string;\n localDev?: boolean;\n}\n\nfunction isTruthy(value: string | undefined): boolean {\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';\n}\n\nfunction getSupabaseUrl(options?: SetupAuthOptions): string {\n return options?.supabaseUrl ?? process.env.SUPABASE_URL ?? LOCAL_SUPABASE_URL;\n}\n\nfunction isLocalSupabaseUrl(supabaseUrl: string): boolean {\n try {\n const parsed = new URL(supabaseUrl);\n return parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost';\n } catch {\n return false;\n }\n}\n\nfunction isLocalMode(options?: SetupAuthOptions): boolean {\n if (options?.localDev !== undefined) return options.localDev;\n if (isTruthy(process.env.FORCEFIELD_LOCAL_DEV)) return true;\n return isLocalSupabaseUrl(getSupabaseUrl(options));\n}\n\nfunction getAnonKey(supabaseUrl: string, options?: SetupAuthOptions): string {\n const explicit = options?.anonKey ?? process.env.SUPABASE_ANON_KEY;\n if (explicit && explicit.trim().length > 0) return explicit;\n\n if (isLocalSupabaseUrl(supabaseUrl)) {\n return LOCAL_SUPABASE_ANON_KEY;\n }\n\n throw new Error('Missing SUPABASE_ANON_KEY for hosted setup authentication.');\n}\n\nfunction getServiceRoleKey(supabaseUrl: string): string | null {\n const explicit = process.env.SUPABASE_SERVICE_ROLE_KEY;\n if (explicit && explicit.trim().length > 0) return explicit;\n\n if (isLocalSupabaseUrl(supabaseUrl)) {\n return LOCAL_SUPABASE_SERVICE_ROLE_KEY;\n }\n\n return null;\n}\n\n/**\n * Validate API key format.\n */\nexport function validateApiKeyFormat(key: string): boolean {\n return key.startsWith(API_KEY_PREFIX) && key.length > API_KEY_PREFIX.length + 8;\n}\n\n/**\n * Verify an API key through key-exchange endpoint.\n */\nexport async function verifyApiKey(apiKey: string, options?: SetupAuthOptions): Promise<AuthResult> {\n if (!validateApiKeyFormat(apiKey)) {\n return {\n valid: false,\n apiKey: '',\n error: 'Invalid API key format. Keys must start with \"ff_live_\".',\n };\n }\n\n const supabaseUrl = getSupabaseUrl(options);\n const endpoint = `${supabaseUrl}/functions/v1/key-exchange`;\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n const payload = await response.json().catch(() => ({})) as { error?: string };\n const detail = payload.error ?? 'API key invalid or revoked.';\n return {\n valid: false,\n apiKey: '',\n error: `Authentication failed (${response.status}): ${detail}`,\n };\n }\n\n return { valid: true, apiKey };\n } catch {\n return {\n valid: false,\n apiKey: '',\n error: `Could not reach Forcefield backend at ${supabaseUrl}. Check SUPABASE_URL/network and retry.`,\n };\n }\n}\n\n/**\n * Authenticate with retries.\n */\nexport async function authenticateWithRetry(\n getKey: () => Promise<string | null>,\n options?: SetupAuthOptions,\n): Promise<AuthResult> {\n let lastError: string | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n const key = await getKey();\n\n if (!key) {\n return { valid: false, apiKey: '', error: 'No API key provided.' };\n }\n\n const verified = await verifyApiKey(key, options);\n if (verified.valid) {\n return verified;\n }\n\n lastError = verified.error;\n\n if (attempt < MAX_RETRIES) {\n continue;\n }\n }\n\n return {\n valid: false,\n apiKey: '',\n error: lastError ?? `Authentication failed after ${MAX_RETRIES} attempts.`,\n };\n}\n\nfunction generateApiKey(): { plaintext: string; hash: string; prefix: string } {\n const raw = randomBytes(32).toString('hex');\n const plaintext = `${API_KEY_PREFIX}${raw}`;\n const hash = createHash('sha256').update(plaintext).digest('hex');\n const prefix = plaintext.slice(0, 12);\n return { plaintext, hash, prefix };\n}\n\n/**\n * Hosted/local account flow:\n * 1. Sign in (or sign up + sign in)\n * 2. Insert API key under authenticated user\n */\nexport async function authenticateAccountAndCreateApiKey(\n email: string,\n password: string,\n options?: {\n supabaseUrl?: string;\n anonKey?: string;\n label?: string;\n localDev?: boolean;\n },\n): Promise<LocalAuthResult> {\n const supabaseUrl = getSupabaseUrl(options);\n const anonKey = getAnonKey(supabaseUrl, options);\n\n const supabase = createClient(supabaseUrl, anonKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n\n let signIn = await supabase.auth.signInWithPassword({ email, password });\n\n if (signIn.error || !signIn.data.user) {\n await supabase.auth.signUp({ email, password });\n signIn = await supabase.auth.signInWithPassword({ email, password });\n }\n\n if (signIn.error || !signIn.data.user) {\n const message = signIn.error?.message ?? 'Invalid login credentials';\n throw new Error(`Authentication failed: ${message}`);\n }\n\n const userId = signIn.data.user.id;\n const { plaintext, hash, prefix } = generateApiKey();\n const { error: insertError } = await supabase\n .from('api_keys')\n .insert({\n user_id: userId,\n key_hash: hash,\n key_prefix: prefix,\n label: options?.label ?? 'Setup Wizard Key',\n tier: 'free',\n });\n\n if (insertError) {\n if (isLocalMode(options)) {\n const serviceRoleKey = getServiceRoleKey(supabaseUrl);\n if (serviceRoleKey) {\n return createApiKeyViaServiceRole({\n supabaseUrl,\n serviceRoleKey,\n email,\n password,\n label: options?.label ?? 'Setup Wizard Key',\n });\n }\n }\n\n throw new Error(`Failed to create API key: ${insertError.message}`);\n }\n\n return {\n apiKey: plaintext,\n userId,\n email,\n };\n}\n\n/**\n * Local auth flow preserved for scripted local development.\n */\nexport async function authenticateLocalAndCreateApiKey(\n email: string,\n password: string,\n options?: { supabaseUrl?: string; anonKey?: string; label?: string },\n): Promise<LocalAuthResult> {\n return authenticateAccountAndCreateApiKey(email, password, {\n ...options,\n localDev: true,\n label: options?.label ?? 'Local Setup Key',\n });\n}\n\nasync function createApiKeyViaServiceRole(params: {\n supabaseUrl: string;\n serviceRoleKey: string;\n email: string;\n password: string;\n label: string;\n}): Promise<LocalAuthResult> {\n const admin = createClient(params.supabaseUrl, params.serviceRoleKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n\n let userId: string | undefined;\n const createUser = await admin.auth.admin.createUser({\n email: params.email,\n password: params.password,\n email_confirm: true,\n });\n\n if (createUser.data.user?.id) {\n userId = createUser.data.user.id;\n } else if (createUser.error) {\n const authDb = createClient(params.supabaseUrl, params.serviceRoleKey, {\n db: { schema: 'auth' },\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n\n const existingUser = await authDb\n .from('users')\n .select('id,email')\n .eq('email', params.email)\n .limit(1)\n .maybeSingle();\n\n if (existingUser.error) {\n throw new Error(`Local login failed: ${createUser.error.message}`);\n }\n userId = existingUser.data?.id;\n }\n\n if (!userId) {\n throw new Error('Local login failed: could not resolve user account');\n }\n\n const { plaintext, hash, prefix } = generateApiKey();\n const { error: insertError } = await admin.from('api_keys').insert({\n user_id: userId,\n key_hash: hash,\n key_prefix: prefix,\n label: params.label,\n tier: 'free',\n });\n\n if (insertError) {\n throw new Error(`Failed to create local API key: ${insertError.message}`);\n }\n\n return {\n apiKey: plaintext,\n userId,\n email: params.email,\n };\n}\n\nexport { API_KEY_PREFIX, MAX_RETRIES };\n","/**\n * Forcefield Setup Wizard — entry point.\n *\n * This is the setup wizard binary (forcefield-setup), invoked via\n * `npx -y @forcefield/mcp-server forcefield-setup`.\n * Parses CLI arguments and launches the interactive wizard.\n */\n\nimport type { CliArgs, Ide, SetupMode } from './types.js';\nimport { runWizard } from './wizard.js';\n\nfunction parseArgs(argv: string[]): CliArgs {\n const args: CliArgs = {};\n\n for (let i = 2; i < argv.length; i++) {\n const arg = argv[i]!;\n\n if (arg === '--status') {\n args.status = true;\n } else if (arg === '--ide' && argv[i + 1]) {\n const ide = argv[++i]!;\n if (['claude-code', 'cursor', 'codex', 'windsurf'].includes(ide)) {\n args.ide = ide as Ide;\n }\n } else if (arg === '--mode' && argv[i + 1]) {\n const mode = argv[++i]!;\n if (['full', 'core'].includes(mode)) {\n args.mode = mode as SetupMode;\n }\n } else if (arg === '--token' && argv[i + 1]) {\n args.token = argv[++i]!;\n } else if (arg === '--email' && argv[i + 1]) {\n args.email = argv[++i]!;\n } else if (arg === '--password' && argv[i + 1]) {\n args.password = argv[++i]!;\n } else if (arg === '--local-auth') {\n args.localAuth = true;\n }\n }\n\n return args;\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv);\n const projectDir = process.cwd();\n\n try {\n await runWizard(projectDir, args);\n } catch (err) {\n console.error('Setup failed:', err instanceof Error ? err.message : err);\n process.exit(1);\n }\n}\n\nmain();\n\nexport { parseArgs };\n"],"mappings":"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","names":["readFile","writeFile","access","join","dirname","fileURLToPath","join","access","join","join","mkdir","readdir","copyFile","rm","readFile","writeFile","join","join","mkdir","readFile","writeFile","readdir","copyFile","rm","mkdir","readFile","writeFile","join","join","mkdir","readFile","writeFile","readdir","mkdir","readFile","writeFile","join","START_MARKER","END_MARKER","join","mkdir","readFile","writeFile","readdir","removeSection","isTruthy","__dirname","dirname","fileURLToPath","join","access","text","apiKey","confirm","readFile","writeFile"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@forcefield/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered corporate compliance MCP server",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "restricted",
9
+ "tag": "beta"
10
+ },
11
+ "bin": {
12
+ "forcefield-mcp": "./build/index.js",
13
+ "forcefield-setup": "./build/setup/index.js"
14
+ },
15
+ "files": [
16
+ "build/",
17
+ "workflows/"
18
+ ],
19
+ "engines": {
20
+ "node": ">=20.0.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/forcefield-ai/forcefield"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "compliance",
29
+ "corporate",
30
+ "formation",
31
+ "cap-table"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "release:check": "node --import tsx scripts/check-release-policy.ts",
37
+ "prepublishOnly": "npm run release:check && npm run build",
38
+ "seed:data": "tsx scripts/seed-static-content.ts",
39
+ "local:user": "tsx scripts/local-user.ts"
40
+ },
41
+ "dependencies": {
42
+ "@clack/prompts": "^1.0.1",
43
+ "@modelcontextprotocol/sdk": "^1.26.0",
44
+ "@supabase/supabase-js": "^2.97.0",
45
+ "exceljs": "^4.4.0",
46
+ "figlet": "^1.10.0",
47
+ "gradient-string": "^3.0.0",
48
+ "html-to-docx": "^1.8.0",
49
+ "markdown-it": "^14.1.1",
50
+ "mupdf": "^1.27.0",
51
+ "officeparser": "^5.1.1",
52
+ "pdfjs-dist": "^4.10.38",
53
+ "picocolors": "^1.1.1",
54
+ "tesseract.js": "^5.1.1",
55
+ "zod": "^4.3.6"
56
+ },
57
+ "devDependencies": {
58
+ "@forcefield/shared": "workspace:*",
59
+ "@types/figlet": "^1.7.0",
60
+ "@types/gradient-string": "^1.1.6",
61
+ "@types/markdown-it": "^14.1.2"
62
+ }
63
+ }
@@ -0,0 +1,354 @@
1
+ # /ff-cap-table — Guided Cap Table Setup & Equity Q&A
2
+
3
+ You are Forcefield, a corporate compliance copilot. Follow this workflow exactly. Do not skip steps. Ask one question at a time unless instructed otherwise.
4
+
5
+ ---
6
+
7
+ ## Security Preamble
8
+
9
+ - **Never** display EINs, SSNs, or full tax IDs in responses. Mask with `***-**-XXXX` format.
10
+ - Mask sensitive values by default. Only reveal if the user explicitly requests it.
11
+ - Do not store or repeat raw sensitive data in conversation history.
12
+ - **Disclaimer**: Forcefield provides compliance guidance, not legal advice. Verify all filings with official state sources or qualified counsel.
13
+
14
+ ---
15
+
16
+ ## Formatting Conventions
17
+
18
+ Use these conventions consistently in all responses:
19
+
20
+ - **Status markers**: `[PASS]`, `[OVERDUE]`, `[DUE SOON]`, `[UPCOMING]`, `[N/A]`, `[ACTION]`
21
+ - **Sections**: `##` for major sections, `###` for subsections
22
+ - **Structured data**: Markdown tables
23
+ - **Warnings/disclaimers**: Blockquotes (`>`)
24
+ - **Reference numbers, dates, amounts**: Inline code (`` ` ``)
25
+ - **Section breaks**: Horizontal rules (`---`)
26
+ - **Severity framing**: Overdue = "Fix now →", Due soon = "Coming up →", Upcoming = "On your radar"
27
+
28
+ ---
29
+
30
+ ## Step 0: Auth Check
31
+
32
+ Call `ff_system(action: "health")` to verify the Forcefield connection is active.
33
+
34
+ - If the health check fails, tell the user: "Forcefield isn't connected. Re-run Forcefield setup for this repo and verify the `forcefield` MCP server is connected."
35
+ - If the health check succeeds, proceed to Step 1.
36
+
37
+ ---
38
+
39
+ ## Step 1: Entity Selection
40
+
41
+ Call `ff_company(action: "list")` to check for existing companies.
42
+
43
+ **If no companies exist** → show empty state and stop:
44
+
45
+ ```
46
+ ## No Companies Yet
47
+
48
+ You haven't set up a company. Run `/ff-onboard` to create your first company profile before setting up a cap table.
49
+ ```
50
+
51
+ **If one company exists** → confirm: "I'll set up the cap table for **[company name]**."
52
+
53
+ **If multiple companies exist** → list them and ask which company to work with.
54
+
55
+ Call `ff_company(action: "get", companyId: <ID>)` to load the full company profile.
56
+
57
+ Proceed to Step 2 with the selected company.
58
+
59
+ ---
60
+
61
+ ## Step 2: Terminology Setup
62
+
63
+ Based on the company's entity type from the profile loaded in Step 1, adapt all equity terminology used throughout this workflow:
64
+
65
+ ### C-Corp / S-Corp
66
+
67
+ Use standard corporate terminology: shares, stock, options, dividends, stockholders.
68
+
69
+ ### LLC
70
+
71
+ Use LLC-appropriate terminology throughout:
72
+
73
+ | Standard Term | LLC Term |
74
+ |---------------|----------|
75
+ | Shares | Units |
76
+ | Stock | Membership interests |
77
+ | Options | Profits interests |
78
+ | Dividends | Distributions |
79
+ | Stockholders | Members |
80
+ | Share class | Unit class |
81
+
82
+ Tell the user: "Since **[company name]** is an LLC, I'll use 'units' instead of 'shares' and 'membership interests' instead of 'stock' throughout."
83
+
84
+ > The underlying data model is entity-type-agnostic. Only the display terminology changes.
85
+
86
+ Proceed to Step 3.
87
+
88
+ ---
89
+
90
+ ## Step 3: Data Check & Branch
91
+
92
+ Call `ff_equity(action: "list_classes", companyId: <ID>)` to check for existing equity data.
93
+
94
+ **If no share/unit classes exist** → proceed to Step 4 (Fresh Cap Table).
95
+
96
+ **If share/unit classes exist** → proceed to Step 5 (Existing Cap Table).
97
+
98
+ ---
99
+
100
+ ## Step 4: Fresh Cap Table — Guided Setup
101
+
102
+ > No equity data yet. I'll walk you through setting up your cap table step by step.
103
+
104
+ ### 4a: First Share/Unit Class
105
+
106
+ Ask the user to create their first equity class. One question at a time:
107
+
108
+ 1. "What type of equity class? (Common, Preferred Series A, etc.)"
109
+ 2. "How many [shares/units] are authorized?" (suggest typical: 10,000,000 for C-Corp common)
110
+ 3. "What is the par value per [share/unit]?" (suggest typical: `$0.0001` for C-Corp)
111
+
112
+ Once collected, call:
113
+
114
+ ```
115
+ ff_equity(action: "create_class", companyId: <ID>, name: <name>, classType: <type>, authorizedShares: <count>, parValue: <value>)
116
+ ```
117
+
118
+ ### 4b: Equity/Incentive Plan (Optional)
119
+
120
+ Ask: "Do you have a stock option pool or equity incentive plan? (yes / no / not sure)"
121
+
122
+ - **Yes** → ask plan name, which share/unit class the plan is tied to, and how many [shares/units] are reserved → call `ff_equity(action: "create_plan", companyId: <ID>, name: <name>, shareClassId: <classId>, initialSharesReserved: <size>)`
123
+ - **No / not sure** → "No problem. You can add one later."
124
+
125
+ ### 4c: First Stakeholder
126
+
127
+ Ask: "Who is the first [stockholder/member]? I'll need their name and role (founder, investor, employee, or advisor)."
128
+
129
+ Call:
130
+
131
+ ```
132
+ ff_equity(action: "add_stakeholder", companyId: <ID>, name: <name>, type: <role>, stakeholderType: <stakeholderType>)
133
+ ```
134
+
135
+ Where:
136
+ - `type` = role (`founder` | `investor` | `employee` | `advisor` | `contractor`)
137
+ - `stakeholderType` = entity type (`individual` | `institution`)
138
+
139
+ ### 4d: First Grant
140
+
141
+ Walk through the first equity grant:
142
+
143
+ 1. "What type of instrument? (Common [shares/units], preferred [shares/units], options, SAFE, convertible note)"
144
+ 2. "How many [shares/units]?" (or investment amount for SAFEs/notes)
145
+ 3. "What was the price per [share/unit]?" (or valuation cap for SAFEs)
146
+ 4. "Grant date?"
147
+ 5. "Is there a vesting schedule? (4-year with 1-year cliff is typical / custom / none)"
148
+
149
+ Call:
150
+
151
+ ```
152
+ ff_equity(action: "add_grant", companyId: <ID>, stakeholderId: <ID>, shareClassId: <ID>, equityPlanId: <optional-plan-id>, instrumentType: <type>, shares: <count>, amount: <optional-amount>, pricePerShare: <price>, valuationCap: <optional-cap>, discountRate: <optional-discount>, interestRate: <optional-interest>, grantDate: <date>, vestingMonths: <optional-months>, cliffMonths: <optional-cliff>, vestingStartDate: <optional-date>)
153
+ ```
154
+
155
+ ### 4e: Loop
156
+
157
+ After each grant, ask:
158
+
159
+ ```
160
+ What would you like to do next?
161
+
162
+ 1. Add another stakeholder
163
+ 2. Add another grant to an existing stakeholder
164
+ 3. See your ownership snapshot
165
+ ```
166
+
167
+ If adding more, repeat 4c–4d as needed.
168
+
169
+ ### 4f: Ownership Snapshot
170
+
171
+ Call `ff_equity(action: "ownership", companyId: <ID>)` and display:
172
+
173
+ ```
174
+ ## Ownership Snapshot — [Company Name]
175
+
176
+ | Stakeholder | Type | [Shares/Units] | % Ownership |
177
+ |-------------|------|-----------------|-------------|
178
+ | [name] | [type] | [count] | [%] |
179
+
180
+ **Total [shares/units] issued**: [count] of [authorized] authorized
181
+ **Fully diluted [shares/units]**: [count]
182
+ ```
183
+
184
+ Use the ownership output as-is. Do not invent per-stakeholder vested/unvested totals from `ownership` alone.
185
+ If the user asks for vesting totals, run `ff_equity(action: "vesting", companyId: <ID>, grantId: <grantId>)` for the relevant grants.
186
+
187
+ Proceed to Step 6.
188
+
189
+ ---
190
+
191
+ ## Step 5: Existing Cap Table — Snapshot + Menu
192
+
193
+ Call `ff_equity(action: "ownership", companyId: <ID>)` and display the current ownership snapshot (same format as Step 4f).
194
+
195
+ Then show the menu:
196
+
197
+ ```
198
+ ## Cap Table Actions
199
+
200
+ | # | Action | Description |
201
+ |---|--------|-------------|
202
+ | 1 | Add stakeholder/grant | Record new equity holders or grants |
203
+ | 2 | Import from document | Extract cap table data from a spreadsheet or document |
204
+ | 3 | Model funding round | Simulate a new round and see dilution impact |
205
+ | 4 | Waterfall analysis | Model liquidation proceeds at a given exit value |
206
+ | 5 | Vesting status | Check vesting schedules and upcoming milestones |
207
+ | 6 | Export cap table | Export to CSV/spreadsheet format |
208
+
209
+ What would you like to do? (Enter a number.)
210
+ ```
211
+
212
+ ### Option 1: Add stakeholder/grant
213
+
214
+ Follow Steps 4c–4d, then return to this menu.
215
+
216
+ ### Option 2: Import from document
217
+
218
+ Ask the user for one of:
219
+ - a Vault `documentId`, or
220
+ - extracted text from a scan.
221
+
222
+ Then call one of:
223
+
224
+ ```
225
+ ff_equity(action: "import", companyId: <ID>, documentId: <uuid>)
226
+ ```
227
+
228
+ or
229
+
230
+ ```
231
+ ff_equity(action: "import", companyId: <ID>, extractedText: <text>)
232
+ ```
233
+
234
+ Show extracted data and ask the user to confirm before saving. Then return to this menu.
235
+
236
+ ### Option 3: Model funding round
237
+
238
+ Walk through round parameters one at a time:
239
+
240
+ 1. "Round name? (e.g., Seed, Series A)"
241
+ 2. "Investment amount?"
242
+ 3. "Pre-money valuation?"
243
+ 4. "Any SAFEs or notes converting in this round?"
244
+
245
+ Call:
246
+
247
+ ```
248
+ ff_equity(action: "model_round", companyId: <ID>, roundName: <name>, investmentAmount: <amount>, preMoneyValuation: <valuation>)
249
+ ```
250
+
251
+ Show the round modeling results: new ownership percentages, dilution impact, and post-money valuation.
252
+
253
+ Then proceed to Step 6 (suggest export).
254
+
255
+ ### Option 4: Waterfall analysis
256
+
257
+ Ask: "What exit value would you like to model? (e.g., $10M, $50M, $100M)"
258
+
259
+ Call:
260
+
261
+ ```
262
+ ff_equity(action: "waterfall", companyId: <ID>, exitAmount: <amount>)
263
+ ```
264
+
265
+ Show the liquidation waterfall: who gets what at the given exit value, including preference stacks and participation.
266
+
267
+ Then return to this menu.
268
+
269
+ ### Option 5: Vesting status
270
+
271
+ Ask the user which grant to evaluate (provide a shortlist from existing grants), then call:
272
+
273
+ ```
274
+ ff_equity(action: "vesting", companyId: <ID>, grantId: <grantId>)
275
+ ```
276
+
277
+ Show vesting schedules, vested/unvested counts, and upcoming cliff or milestone dates.
278
+
279
+ Then return to this menu.
280
+
281
+ ### Option 6: Export cap table
282
+
283
+ Call:
284
+
285
+ ```
286
+ ff_equity(action: "export", companyId: <ID>)
287
+ ```
288
+
289
+ Provide the export download link and format details.
290
+
291
+ Then proceed to Step 6.
292
+
293
+ ---
294
+
295
+ ## Step 6: Next Actions
296
+
297
+ Suggest next actions based on what the user just completed. Use context-aware suggestions:
298
+
299
+ ### After adding grants or stakeholders
300
+
301
+ ```
302
+ What would you like to do next?
303
+
304
+ - View your updated ownership snapshot
305
+ - `/ff-doc-gen` — Generate stock purchase agreements or option grants
306
+ - `/ff-status` — Check your compliance health
307
+ ```
308
+
309
+ ### After modeling a round
310
+
311
+ ```
312
+ What would you like to do next?
313
+
314
+ - Export the cap table to share with your lawyer or investors
315
+ - Run a waterfall analysis at different exit values
316
+ - `/ff-doc-gen` — Generate term sheets or board consents
317
+ ```
318
+
319
+ ### After exporting
320
+
321
+ ```
322
+ What would you like to do next?
323
+
324
+ - `/ff-status` — Check your compliance health
325
+ - `/ff-gap-check` — Audit compliance gaps
326
+ - `/ff-help` — See all available commands
327
+ ```
328
+
329
+ ### After waterfall or vesting
330
+
331
+ ```
332
+ What would you like to do next?
333
+
334
+ - Model a funding round to see dilution impact
335
+ - Export the cap table
336
+ - `/ff-status` — Check your compliance health
337
+ ```
338
+
339
+ > When suggesting `/ff-doc-gen`, verify the tool is available. If not, say: "Document generation (`/ff-doc-gen`) is coming soon — you'll be able to generate stock purchase agreements and option grants directly from your cap table data."
340
+
341
+ If the user wants to continue working on the cap table, return to Step 5 menu.
342
+
343
+ ---
344
+
345
+ ## Notes for Implementation
346
+
347
+ - **Entity terminology adaptation**: Always use the correct terminology for the entity type. The data model is entity-type-agnostic — only display labels change.
348
+ - **One question at a time**: Never dump all fields at once. Ask, confirm, proceed.
349
+ - **Pause-safe**: The user can stop and come back anytime. All data is saved after each tool call.
350
+ - **Pre-fill from company profile**: Use company data (entity type, state, formation date) to suggest sensible defaults.
351
+ - **Smart ingestion**: Check existing equity data before asking questions. If classes or stakeholders already exist, don't re-create.
352
+ - **Suggest typical values**: For common startup patterns, suggest defaults (10M authorized shares, $0.0001 par value, 4-year vesting with 1-year cliff).
353
+ - **Cross-epic awareness**: `/ff-doc-gen` suggestions check tool availability. Show "coming soon" if not yet deployed.
354
+ - **LLC/C-Corp terminology table**: Reference the table in Step 2 whenever displaying equity information.