@glasstrace/sdk 0.9.1 → 0.10.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.
- package/dist/{chunk-Y6V7BTF3.js → chunk-5MAHIPFH.js} +2 -2
- package/dist/chunk-KOYJ2UQE.js +417 -0
- package/dist/chunk-KOYJ2UQE.js.map +1 -0
- package/dist/{chunk-PQWAKVQ5.js → chunk-O3Y45VGV.js} +33 -102
- package/dist/chunk-O3Y45VGV.js.map +1 -0
- package/dist/chunk-ZRNG36LU.js +77 -0
- package/dist/chunk-ZRNG36LU.js.map +1 -0
- package/dist/cli/init.cjs +55 -18
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +3 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.js +2 -1
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/uninit.cjs +57 -18
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +34 -2
- package/dist/cli/uninit.d.ts +34 -2
- package/dist/cli/uninit.js +55 -18
- package/dist/cli/uninit.js.map +1 -1
- package/dist/index.cjs +15837 -15140
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +38 -404
- package/dist/index.js.map +1 -1
- package/dist/source-map-uploader-OA5NCDOK.js +25 -0
- package/dist/source-map-uploader-OA5NCDOK.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-PQWAKVQ5.js.map +0 -1
- /package/dist/{chunk-Y6V7BTF3.js.map → chunk-5MAHIPFH.js.map} +0 -0
package/dist/cli/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildImportGraph
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-5MAHIPFH.js";
|
|
5
5
|
import {
|
|
6
6
|
addCoverageMapEnv,
|
|
7
7
|
detectAgents,
|
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
} from "../chunk-4NDQPWDJ.js";
|
|
19
19
|
import {
|
|
20
20
|
getOrCreateAnonKey
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-ZRNG36LU.js";
|
|
22
|
+
import "../chunk-O3Y45VGV.js";
|
|
22
23
|
import {
|
|
23
24
|
MCP_ENDPOINT,
|
|
24
25
|
NEXT_CONFIG_NAMES,
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/init.ts","../../src/cli/monorepo.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n scaffoldMcpMarker,\n addCoverageMapEnv,\n} from \"./scaffolder.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 2: Ensure instrumentation.ts has registerGlasstrace()\n try {\n const instrResult = await scaffoldInstrumentation(projectRoot);\n switch (instrResult.action) {\n case \"created\":\n summary.push(\"Created instrumentation.ts\");\n break;\n case \"injected\":\n summary.push(\"Added registerGlasstrace() to existing instrumentation.ts\");\n break;\n case \"already-registered\":\n summary.push(\"Skipped instrumentation.ts (registerGlasstrace already present)\");\n break;\n case \"unrecognized\":\n warnings.push(\n \"instrumentation.ts exists but has no recognizable register() function.\\n\" +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n errors.push(`Failed to write instrumentation.ts: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n try {\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n const anonKey = await getOrCreateAnonKey(projectRoot);\n let anyConfigWritten = false;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if at least one config was successfully written.\n // Without this gate, a failed MCP setup would suppress future nudges,\n // leaving users stuck without MCP configuration.\n if (anyConfigWritten) {\n const markerCreated = await scaffoldMcpMarker(projectRoot, anonKey);\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map]\\n\" +\n \" glasstrace uninit [--dry-run]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/** Result of classifying the project root directory. */\nexport interface ProjectClassification {\n /** The directory to scaffold into (may differ from cwd for monorepos). */\n projectRoot: string;\n /** Whether this was auto-resolved from a monorepo root. */\n isMonorepo: boolean;\n /** If monorepo, the relative path from cwd to the resolved app. */\n appRelativePath?: string;\n}\n\n/**\n * Classifies the current directory and resolves the target project root.\n *\n * Classification logic:\n * 1. If the directory contains a Next.js config file, it is a Next.js app\n * directory. Returns it directly.\n * 1b. If no config file exists but package.json lists \"next\" as a dependency,\n * it is still a Next.js app (config files are optional since Next.js 12).\n * 2. If the directory contains monorepo markers (pnpm-workspace.yaml,\n * turbo.json, lerna.json, or a workspaces field in package.json),\n * scans workspace packages for Next.js apps.\n * 3. Otherwise, fails with a user-facing error.\n *\n * @param cwd - The current working directory\n * @returns The resolved project classification\n * @throws Error with a user-facing message if the location is invalid\n */\nexport function resolveProjectRoot(cwd: string): ProjectClassification {\n // Step 1: Check if cwd is a Next.js app directory (config file)\n if (hasNextConfig(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 1b: Check if cwd has \"next\" as a dependency (config is optional)\n if (hasNextDependency(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 2: Check for monorepo markers\n if (isMonorepoRoot(cwd)) {\n // findNextJsApps throws if no workspace globs are found (e.g., turbo.json\n // exists but no pnpm-workspace.yaml or workspaces in package.json)\n const apps = findNextJsApps(cwd);\n\n if (apps.length === 0) {\n throw new Error(\n \"This is a monorepo but no Next.js apps were found in workspace packages.\",\n );\n }\n\n if (apps.length === 1) {\n const appDir = apps[0];\n const relativePath = path.relative(cwd, appDir);\n return {\n projectRoot: appDir,\n isMonorepo: true,\n appRelativePath: relativePath,\n };\n }\n\n // Multiple apps found — cannot auto-resolve\n const appList = apps\n .map((app) => ` - ${path.relative(cwd, app)}`)\n .join(\"\\n\");\n throw new Error(\n `Found multiple Next.js apps:\\n${appList}\\nRun init from the specific app directory you want to instrument.`,\n );\n }\n\n // Step 3: Neither Next.js app nor monorepo\n throw new Error(\n \"No Next.js project found in the current directory.\\n\" +\n \"Run this command from your Next.js app directory, or from a monorepo root.\",\n );\n}\n\n/**\n * Checks whether the given directory contains a Next.js config file.\n */\nfunction hasNextConfig(dir: string): boolean {\n return NEXT_CONFIG_NAMES.some((name) =>\n fs.existsSync(path.join(dir, name)),\n );\n}\n\n/**\n * Checks whether the given directory's package.json lists \"next\" as a\n * dependency or devDependency. This handles the case where a Next.js app\n * has no explicit config file (config files are optional since Next.js 12).\n */\nfunction hasNextDependency(dir: string): boolean {\n const packageJsonPath = path.join(dir, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) return false;\n\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"];\n const devDeps = pkg[\"devDependencies\"];\n\n if (typeof deps === \"object\" && deps !== null && \"next\" in deps) return true;\n if (typeof devDeps === \"object\" && devDeps !== null && \"next\" in devDeps) return true;\n } catch {\n // Invalid JSON — not a Next.js indicator\n }\n\n return false;\n}\n\n/**\n * Detects monorepo markers in the given directory.\n *\n * Checks for:\n * - pnpm-workspace.yaml\n * - turbo.json\n * - lerna.json\n * - \"workspaces\" field in package.json\n */\nexport function isMonorepoRoot(dir: string): boolean {\n // Check for standalone monorepo marker files\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n if (fs.existsSync(path.join(dir, \"turbo.json\"))) return true;\n if (fs.existsSync(path.join(dir, \"lerna.json\"))) return true;\n\n // Check for \"workspaces\" field in package.json\n const packageJsonPath = path.join(dir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n if (pkg[\"workspaces\"] !== undefined) return true;\n } catch {\n // Invalid JSON — not a monorepo indicator\n }\n }\n\n return false;\n}\n\n/**\n * Finds Next.js apps in workspace packages.\n *\n * Parses workspace globs from:\n * - pnpm-workspace.yaml (packages array)\n * - package.json workspaces field (string[] or { packages: string[] })\n * - lerna.json packages field (string[])\n *\n * Expands the workspace globs using filesystem traversal and returns\n * absolute paths of directories that contain a Next.js config file or\n * have \"next\" as a dependency in package.json.\n *\n * @param monorepoRoot - Absolute path to the monorepo root directory\n * @returns Sorted array of absolute paths to Next.js app directories\n */\nexport function findNextJsApps(monorepoRoot: string): string[] {\n const { includeGlobs, negationPatterns } = collectWorkspaceGlobs(monorepoRoot);\n\n if (includeGlobs.length === 0) {\n throw new Error(\n \"Monorepo detected but no workspace configuration found.\\n\" +\n 'Add a \"workspaces\" field to package.json or create pnpm-workspace.yaml.',\n );\n }\n\n const workspaceDirs = expandGlobs(monorepoRoot, includeGlobs);\n\n // Apply negation patterns: filter out directories matching any exclusion\n const excludedDirs = expandGlobs(monorepoRoot, negationPatterns);\n const excludedSet = new Set(excludedDirs);\n\n // Deduplicate and filter for Next.js apps\n const seen = new Set<string>();\n const nextApps: string[] = [];\n\n for (const dir of workspaceDirs) {\n if (seen.has(dir)) continue;\n seen.add(dir);\n if (excludedSet.has(dir)) continue;\n if (hasNextConfig(dir) || hasNextDependency(dir)) {\n nextApps.push(dir);\n }\n }\n\n return nextApps.sort();\n}\n\n/** Workspace globs split into include and negation patterns. */\nexport interface WorkspaceGlobs {\n includeGlobs: string[];\n negationPatterns: string[];\n}\n\n/**\n * Collects workspace globs from all supported monorepo config sources.\n * Returns deduplicated include globs and negation patterns separately.\n */\nfunction collectWorkspaceGlobs(root: string): WorkspaceGlobs {\n const globs: string[] = [];\n const negations: string[] = [];\n\n // 1. pnpm-workspace.yaml\n const pnpmPath = path.join(root, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmPath)) {\n const content = fs.readFileSync(pnpmPath, \"utf-8\");\n const parsed = parsePnpmWorkspaceYaml(content);\n globs.push(...parsed.includeGlobs);\n negations.push(...parsed.negationPatterns);\n }\n\n // 2. package.json workspaces\n const packageJsonPath = path.join(root, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n globs.push(...parsePackageJsonWorkspaces(pkg));\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // 3. lerna.json packages\n const lernaPath = path.join(root, \"lerna.json\");\n if (fs.existsSync(lernaPath)) {\n try {\n const content = fs.readFileSync(lernaPath, \"utf-8\");\n const lerna = JSON.parse(content) as Record<string, unknown>;\n const packages = lerna[\"packages\"];\n if (Array.isArray(packages)) {\n for (const pkg of packages) {\n if (typeof pkg === \"string\") {\n globs.push(pkg);\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Deduplicate\n return {\n includeGlobs: [...new Set(globs)],\n negationPatterns: [...new Set(negations)],\n };\n}\n\n/**\n * Parses pnpm-workspace.yaml to extract workspace package globs.\n *\n * The format is simple enough to parse with string processing:\n * ```yaml\n * packages:\n * - \"apps/*\"\n * - packages/*\n * - '!packages/internal'\n * ```\n *\n * Handles both quoted and unquoted values. Negation patterns (lines\n * starting with !) are returned separately so callers can apply them\n * as exclusions after expanding include globs.\n *\n * @internal Exported for unit testing only.\n */\nexport function parsePnpmWorkspaceYaml(content: string): WorkspaceGlobs {\n const lines = content.split(\"\\n\");\n const includeGlobs: string[] = [];\n const negationPatterns: string[] = [];\n let inPackages = false;\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim();\n\n // Detect the `packages:` key\n if (/^packages\\s*:/.test(trimmed)) {\n inPackages = true;\n continue;\n }\n\n // Stop when we hit another top-level key (no leading whitespace before key)\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith(\"-\") && !rawLine.startsWith(\" \") && !rawLine.startsWith(\"\\t\")) {\n inPackages = false;\n continue;\n }\n\n if (!inPackages) continue;\n\n // Parse list items: ` - \"glob\"` or ` - glob` or ` - 'glob'`\n const itemMatch = /^\\s*-\\s+(.+)$/.exec(rawLine);\n if (!itemMatch) continue;\n\n // Strip surrounding quotes (single or double)\n const value = itemMatch[1].trim().replace(/^[\"']|[\"']$/g, \"\");\n\n // Skip empty values\n if (value.length === 0) continue;\n\n // Collect negation patterns separately (strip the leading !)\n if (value.startsWith(\"!\")) {\n negationPatterns.push(value.slice(1));\n continue;\n }\n\n includeGlobs.push(value);\n }\n\n return { includeGlobs, negationPatterns };\n}\n\n/**\n * Extracts workspace globs from a parsed package.json object.\n *\n * Handles both forms:\n * - `\"workspaces\": [\"packages/*\", \"apps/*\"]`\n * - `\"workspaces\": { \"packages\": [\"packages/*\", \"apps/*\"] }`\n */\nfunction parsePackageJsonWorkspaces(pkg: Record<string, unknown>): string[] {\n const workspaces = pkg[\"workspaces\"];\n if (workspaces === undefined || workspaces === null) return [];\n\n // Array form: string[]\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === \"string\");\n }\n\n // Object form: { packages: string[] }\n if (typeof workspaces === \"object\") {\n const obj = workspaces as Record<string, unknown>;\n const packages = obj[\"packages\"];\n if (Array.isArray(packages)) {\n return packages.filter((p): p is string => typeof p === \"string\");\n }\n }\n\n return [];\n}\n\n/**\n * Expands workspace globs into actual directory paths.\n *\n * Supports:\n * - `packages/*` — matches one level of directories under packages/\n * - `apps/*` — matches one level of directories under apps/\n * - `packages/foo` — matches a specific directory (literal path)\n * - `packages/**` — recursively walks for directories with package.json\n *\n * @param root - The monorepo root directory\n * @param globs - Workspace glob patterns to expand\n * @returns Array of absolute paths to matched directories\n */\nfunction expandGlobs(root: string, globs: string[]): string[] {\n const dirs: string[] = [];\n\n for (const glob of globs) {\n // Remove trailing slash if present\n const cleanGlob = glob.replace(/\\/+$/, \"\");\n\n if (cleanGlob.includes(\"**\")) {\n // Recursive glob — walk the directory tree\n const prefix = cleanGlob.split(\"**\")[0].replace(/\\/+$/, \"\");\n const baseDir = path.join(root, prefix);\n if (fs.existsSync(baseDir)) {\n dirs.push(...walkDirectories(baseDir));\n }\n } else if (cleanGlob.includes(\"*\")) {\n // Single-level wildcard — expand one directory level\n const parts = cleanGlob.split(\"*\");\n // For \"packages/*\", parts = [\"packages/\", \"\"]\n const baseDir = path.join(root, parts[0].replace(/\\/+$/, \"\"));\n const suffix = parts.slice(1).join(\"*\"); // Anything after the wildcard\n\n if (!fs.existsSync(baseDir)) continue;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // If there is a suffix pattern, the entry name must end with it\n if (suffix && !entry.name.endsWith(suffix)) continue;\n dirs.push(path.join(baseDir, entry.name));\n }\n } else {\n // Literal path — no wildcards\n const targetDir = path.join(root, cleanGlob);\n if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {\n dirs.push(targetDir);\n }\n }\n }\n\n return dirs;\n}\n\n/**\n * Recursively walks a directory tree and returns all subdirectories\n * that contain a package.json (indicating they are workspace packages).\n * Skips node_modules and hidden directories.\n */\nfunction walkDirectories(baseDir: string): string[] {\n const result: string[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n return result;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // Skip node_modules and hidden directories\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n\n const fullPath = path.join(baseDir, entry.name);\n\n // A workspace package should have a package.json\n if (fs.existsSync(path.join(fullPath, \"package.json\"))) {\n result.push(fullPath);\n }\n\n // Continue recursing for nested workspaces\n result.push(...walkDirectories(fullPath));\n }\n\n return result;\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/init.ts","../../src/cli/monorepo.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n scaffoldMcpMarker,\n addCoverageMapEnv,\n} from \"./scaffolder.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 2: Ensure instrumentation.ts has registerGlasstrace()\n try {\n const instrResult = await scaffoldInstrumentation(projectRoot);\n switch (instrResult.action) {\n case \"created\":\n summary.push(\"Created instrumentation.ts\");\n break;\n case \"injected\":\n summary.push(\"Added registerGlasstrace() to existing instrumentation.ts\");\n break;\n case \"already-registered\":\n summary.push(\"Skipped instrumentation.ts (registerGlasstrace already present)\");\n break;\n case \"unrecognized\":\n warnings.push(\n \"instrumentation.ts exists but has no recognizable register() function.\\n\" +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n errors.push(`Failed to write instrumentation.ts: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n try {\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n const anonKey = await getOrCreateAnonKey(projectRoot);\n let anyConfigWritten = false;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if at least one config was successfully written.\n // Without this gate, a failed MCP setup would suppress future nudges,\n // leaving users stuck without MCP configuration.\n if (anyConfigWritten) {\n const markerCreated = await scaffoldMcpMarker(projectRoot, anonKey);\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map]\\n\" +\n \" glasstrace uninit [--dry-run]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/** Result of classifying the project root directory. */\nexport interface ProjectClassification {\n /** The directory to scaffold into (may differ from cwd for monorepos). */\n projectRoot: string;\n /** Whether this was auto-resolved from a monorepo root. */\n isMonorepo: boolean;\n /** If monorepo, the relative path from cwd to the resolved app. */\n appRelativePath?: string;\n}\n\n/**\n * Classifies the current directory and resolves the target project root.\n *\n * Classification logic:\n * 1. If the directory contains a Next.js config file, it is a Next.js app\n * directory. Returns it directly.\n * 1b. If no config file exists but package.json lists \"next\" as a dependency,\n * it is still a Next.js app (config files are optional since Next.js 12).\n * 2. If the directory contains monorepo markers (pnpm-workspace.yaml,\n * turbo.json, lerna.json, or a workspaces field in package.json),\n * scans workspace packages for Next.js apps.\n * 3. Otherwise, fails with a user-facing error.\n *\n * @param cwd - The current working directory\n * @returns The resolved project classification\n * @throws Error with a user-facing message if the location is invalid\n */\nexport function resolveProjectRoot(cwd: string): ProjectClassification {\n // Step 1: Check if cwd is a Next.js app directory (config file)\n if (hasNextConfig(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 1b: Check if cwd has \"next\" as a dependency (config is optional)\n if (hasNextDependency(cwd)) {\n return { projectRoot: cwd, isMonorepo: false };\n }\n\n // Step 2: Check for monorepo markers\n if (isMonorepoRoot(cwd)) {\n // findNextJsApps throws if no workspace globs are found (e.g., turbo.json\n // exists but no pnpm-workspace.yaml or workspaces in package.json)\n const apps = findNextJsApps(cwd);\n\n if (apps.length === 0) {\n throw new Error(\n \"This is a monorepo but no Next.js apps were found in workspace packages.\",\n );\n }\n\n if (apps.length === 1) {\n const appDir = apps[0];\n const relativePath = path.relative(cwd, appDir);\n return {\n projectRoot: appDir,\n isMonorepo: true,\n appRelativePath: relativePath,\n };\n }\n\n // Multiple apps found — cannot auto-resolve\n const appList = apps\n .map((app) => ` - ${path.relative(cwd, app)}`)\n .join(\"\\n\");\n throw new Error(\n `Found multiple Next.js apps:\\n${appList}\\nRun init from the specific app directory you want to instrument.`,\n );\n }\n\n // Step 3: Neither Next.js app nor monorepo\n throw new Error(\n \"No Next.js project found in the current directory.\\n\" +\n \"Run this command from your Next.js app directory, or from a monorepo root.\",\n );\n}\n\n/**\n * Checks whether the given directory contains a Next.js config file.\n */\nfunction hasNextConfig(dir: string): boolean {\n return NEXT_CONFIG_NAMES.some((name) =>\n fs.existsSync(path.join(dir, name)),\n );\n}\n\n/**\n * Checks whether the given directory's package.json lists \"next\" as a\n * dependency or devDependency. This handles the case where a Next.js app\n * has no explicit config file (config files are optional since Next.js 12).\n */\nfunction hasNextDependency(dir: string): boolean {\n const packageJsonPath = path.join(dir, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) return false;\n\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"];\n const devDeps = pkg[\"devDependencies\"];\n\n if (typeof deps === \"object\" && deps !== null && \"next\" in deps) return true;\n if (typeof devDeps === \"object\" && devDeps !== null && \"next\" in devDeps) return true;\n } catch {\n // Invalid JSON — not a Next.js indicator\n }\n\n return false;\n}\n\n/**\n * Detects monorepo markers in the given directory.\n *\n * Checks for:\n * - pnpm-workspace.yaml\n * - turbo.json\n * - lerna.json\n * - \"workspaces\" field in package.json\n */\nexport function isMonorepoRoot(dir: string): boolean {\n // Check for standalone monorepo marker files\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n if (fs.existsSync(path.join(dir, \"turbo.json\"))) return true;\n if (fs.existsSync(path.join(dir, \"lerna.json\"))) return true;\n\n // Check for \"workspaces\" field in package.json\n const packageJsonPath = path.join(dir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n if (pkg[\"workspaces\"] !== undefined) return true;\n } catch {\n // Invalid JSON — not a monorepo indicator\n }\n }\n\n return false;\n}\n\n/**\n * Finds Next.js apps in workspace packages.\n *\n * Parses workspace globs from:\n * - pnpm-workspace.yaml (packages array)\n * - package.json workspaces field (string[] or { packages: string[] })\n * - lerna.json packages field (string[])\n *\n * Expands the workspace globs using filesystem traversal and returns\n * absolute paths of directories that contain a Next.js config file or\n * have \"next\" as a dependency in package.json.\n *\n * @param monorepoRoot - Absolute path to the monorepo root directory\n * @returns Sorted array of absolute paths to Next.js app directories\n */\nexport function findNextJsApps(monorepoRoot: string): string[] {\n const { includeGlobs, negationPatterns } = collectWorkspaceGlobs(monorepoRoot);\n\n if (includeGlobs.length === 0) {\n throw new Error(\n \"Monorepo detected but no workspace configuration found.\\n\" +\n 'Add a \"workspaces\" field to package.json or create pnpm-workspace.yaml.',\n );\n }\n\n const workspaceDirs = expandGlobs(monorepoRoot, includeGlobs);\n\n // Apply negation patterns: filter out directories matching any exclusion\n const excludedDirs = expandGlobs(monorepoRoot, negationPatterns);\n const excludedSet = new Set(excludedDirs);\n\n // Deduplicate and filter for Next.js apps\n const seen = new Set<string>();\n const nextApps: string[] = [];\n\n for (const dir of workspaceDirs) {\n if (seen.has(dir)) continue;\n seen.add(dir);\n if (excludedSet.has(dir)) continue;\n if (hasNextConfig(dir) || hasNextDependency(dir)) {\n nextApps.push(dir);\n }\n }\n\n return nextApps.sort();\n}\n\n/** Workspace globs split into include and negation patterns. */\nexport interface WorkspaceGlobs {\n includeGlobs: string[];\n negationPatterns: string[];\n}\n\n/**\n * Collects workspace globs from all supported monorepo config sources.\n * Returns deduplicated include globs and negation patterns separately.\n */\nfunction collectWorkspaceGlobs(root: string): WorkspaceGlobs {\n const globs: string[] = [];\n const negations: string[] = [];\n\n // 1. pnpm-workspace.yaml\n const pnpmPath = path.join(root, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmPath)) {\n const content = fs.readFileSync(pnpmPath, \"utf-8\");\n const parsed = parsePnpmWorkspaceYaml(content);\n globs.push(...parsed.includeGlobs);\n negations.push(...parsed.negationPatterns);\n }\n\n // 2. package.json workspaces\n const packageJsonPath = path.join(root, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n try {\n const content = fs.readFileSync(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n globs.push(...parsePackageJsonWorkspaces(pkg));\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // 3. lerna.json packages\n const lernaPath = path.join(root, \"lerna.json\");\n if (fs.existsSync(lernaPath)) {\n try {\n const content = fs.readFileSync(lernaPath, \"utf-8\");\n const lerna = JSON.parse(content) as Record<string, unknown>;\n const packages = lerna[\"packages\"];\n if (Array.isArray(packages)) {\n for (const pkg of packages) {\n if (typeof pkg === \"string\") {\n globs.push(pkg);\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Deduplicate\n return {\n includeGlobs: [...new Set(globs)],\n negationPatterns: [...new Set(negations)],\n };\n}\n\n/**\n * Parses pnpm-workspace.yaml to extract workspace package globs.\n *\n * The format is simple enough to parse with string processing:\n * ```yaml\n * packages:\n * - \"apps/*\"\n * - packages/*\n * - '!packages/internal'\n * ```\n *\n * Handles both quoted and unquoted values. Negation patterns (lines\n * starting with !) are returned separately so callers can apply them\n * as exclusions after expanding include globs.\n *\n * @internal Exported for unit testing only.\n */\nexport function parsePnpmWorkspaceYaml(content: string): WorkspaceGlobs {\n const lines = content.split(\"\\n\");\n const includeGlobs: string[] = [];\n const negationPatterns: string[] = [];\n let inPackages = false;\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim();\n\n // Detect the `packages:` key\n if (/^packages\\s*:/.test(trimmed)) {\n inPackages = true;\n continue;\n }\n\n // Stop when we hit another top-level key (no leading whitespace before key)\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith(\"-\") && !rawLine.startsWith(\" \") && !rawLine.startsWith(\"\\t\")) {\n inPackages = false;\n continue;\n }\n\n if (!inPackages) continue;\n\n // Parse list items: ` - \"glob\"` or ` - glob` or ` - 'glob'`\n const itemMatch = /^\\s*-\\s+(.+)$/.exec(rawLine);\n if (!itemMatch) continue;\n\n // Strip surrounding quotes (single or double)\n const value = itemMatch[1].trim().replace(/^[\"']|[\"']$/g, \"\");\n\n // Skip empty values\n if (value.length === 0) continue;\n\n // Collect negation patterns separately (strip the leading !)\n if (value.startsWith(\"!\")) {\n negationPatterns.push(value.slice(1));\n continue;\n }\n\n includeGlobs.push(value);\n }\n\n return { includeGlobs, negationPatterns };\n}\n\n/**\n * Extracts workspace globs from a parsed package.json object.\n *\n * Handles both forms:\n * - `\"workspaces\": [\"packages/*\", \"apps/*\"]`\n * - `\"workspaces\": { \"packages\": [\"packages/*\", \"apps/*\"] }`\n */\nfunction parsePackageJsonWorkspaces(pkg: Record<string, unknown>): string[] {\n const workspaces = pkg[\"workspaces\"];\n if (workspaces === undefined || workspaces === null) return [];\n\n // Array form: string[]\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === \"string\");\n }\n\n // Object form: { packages: string[] }\n if (typeof workspaces === \"object\") {\n const obj = workspaces as Record<string, unknown>;\n const packages = obj[\"packages\"];\n if (Array.isArray(packages)) {\n return packages.filter((p): p is string => typeof p === \"string\");\n }\n }\n\n return [];\n}\n\n/**\n * Expands workspace globs into actual directory paths.\n *\n * Supports:\n * - `packages/*` — matches one level of directories under packages/\n * - `apps/*` — matches one level of directories under apps/\n * - `packages/foo` — matches a specific directory (literal path)\n * - `packages/**` — recursively walks for directories with package.json\n *\n * @param root - The monorepo root directory\n * @param globs - Workspace glob patterns to expand\n * @returns Array of absolute paths to matched directories\n */\nfunction expandGlobs(root: string, globs: string[]): string[] {\n const dirs: string[] = [];\n\n for (const glob of globs) {\n // Remove trailing slash if present\n const cleanGlob = glob.replace(/\\/+$/, \"\");\n\n if (cleanGlob.includes(\"**\")) {\n // Recursive glob — walk the directory tree\n const prefix = cleanGlob.split(\"**\")[0].replace(/\\/+$/, \"\");\n const baseDir = path.join(root, prefix);\n if (fs.existsSync(baseDir)) {\n dirs.push(...walkDirectories(baseDir));\n }\n } else if (cleanGlob.includes(\"*\")) {\n // Single-level wildcard — expand one directory level\n const parts = cleanGlob.split(\"*\");\n // For \"packages/*\", parts = [\"packages/\", \"\"]\n const baseDir = path.join(root, parts[0].replace(/\\/+$/, \"\"));\n const suffix = parts.slice(1).join(\"*\"); // Anything after the wildcard\n\n if (!fs.existsSync(baseDir)) continue;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // If there is a suffix pattern, the entry name must end with it\n if (suffix && !entry.name.endsWith(suffix)) continue;\n dirs.push(path.join(baseDir, entry.name));\n }\n } else {\n // Literal path — no wildcards\n const targetDir = path.join(root, cleanGlob);\n if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {\n dirs.push(targetDir);\n }\n }\n }\n\n return dirs;\n}\n\n/**\n * Recursively walks a directory tree and returns all subdirectories\n * that contain a package.json (indicating they are workspace packages).\n * Skips node_modules and hidden directories.\n */\nfunction walkDirectories(baseDir: string): string[] {\n const result: string[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(baseDir, { withFileTypes: true });\n } catch {\n return result;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // Skip node_modules and hidden directories\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) continue;\n\n const fullPath = path.join(baseDir, entry.name);\n\n // A workspace package should have a package.json\n if (fs.existsSync(path.join(fullPath, \"package.json\"))) {\n result.push(fullPath);\n }\n\n // Continue recursing for nested workspaces\n result.push(...walkDirectories(fullPath));\n }\n\n return result;\n}\n"],"mappings":"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","names":["fs","path"]}
|
package/dist/cli/mcp-add.js
CHANGED
package/dist/cli/mcp-add.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/mcp-add.ts"],"sourcesContent":["import { execFile as execFileCb } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport {\n writeMcpConfig,\n injectInfoSection,\n updateGitignore,\n} from \"../agent-detection/inject.js\";\nimport { scaffoldMcpMarker } from \"./scaffolder.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\n\nconst execFileAsync = promisify(execFileCb);\n\n/** Options for the mcp add command. */\nexport interface McpAddOptions {\n force?: boolean;\n dryRun?: boolean;\n}\n\n/** Result of the mcp add command. */\nexport interface McpAddResult {\n exitCode: number;\n results: AgentResult[];\n messages: string[];\n}\n\n/**\n * Result of a single agent registration attempt.\n */\ninterface AgentResult {\n agent: DetectedAgent[\"name\"];\n success: boolean;\n method: \"cli\" | \"file\" | \"skipped\";\n message: string;\n}\n\n/**\n * Attempts CLI-based MCP registration for agents that support it.\n * Returns true if the CLI command succeeded.\n *\n * Note: anonymous keys are passed in process arguments for CLI registration.\n * This is acceptable because anon keys are non-secret identifiers (not\n * credentials) designed for semi-public use. They identify a project\n * but cannot be used to access user data.\n */\nasync function registerViaCli(\n agent: DetectedAgent,\n anonKey: string,\n): Promise<boolean> {\n if (!agent.cliAvailable) {\n return false;\n }\n\n try {\n switch (agent.name) {\n case \"claude\": {\n const payload = JSON.stringify({\n type: \"http\",\n url: MCP_ENDPOINT,\n headers: { Authorization: `Bearer ${anonKey}` },\n });\n await execFileAsync(\"claude\", [\n \"mcp\",\n \"add-json\",\n \"glasstrace\",\n payload,\n \"--scope\",\n \"project\",\n ]);\n return true;\n }\n\n case \"codex\": {\n await execFileAsync(\"codex\", [\n \"mcp\",\n \"add\",\n \"glasstrace\",\n \"--url\",\n MCP_ENDPOINT,\n ]);\n // Ensure .codex/config.toml has bearer_token_env_var\n const configPath = agent.mcpConfigPath;\n if (configPath !== null && fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"bearer_token_env_var\")) {\n const appendContent =\n content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(\n configPath,\n content +\n appendContent +\n 'bearer_token_env_var = \"GLASSTRACE_API_KEY\"\\n',\n \"utf-8\",\n );\n }\n }\n process.stderr.write(\n \" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\\n\",\n );\n return true;\n }\n\n case \"gemini\": {\n await execFileAsync(\"gemini\", [\n \"mcp\",\n \"add\",\n \"--transport\",\n \"http\",\n \"--header\",\n `Authorization: Bearer ${anonKey}`,\n \"glasstrace\",\n MCP_ENDPOINT,\n ]);\n return true;\n }\n\n default:\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Registers the Glasstrace MCP server with detected AI coding agents.\n *\n * For each agent, attempts native CLI registration first, then falls back\n * to file-based configuration. Creates a marker file on success to enable\n * idempotent re-runs.\n *\n * Returns a structured result instead of calling process.exit(), so the\n * CLI entry point can decide how to handle the outcome.\n *\n * @param options - Control flags for force and dry-run modes.\n */\nexport async function mcpAdd(options?: McpAddOptions): Promise<McpAddResult> {\n const force = options?.force ?? false;\n const dryRun = options?.dryRun ?? false;\n const projectRoot = process.cwd();\n const messages: string[] = [];\n\n // Step 1: Read anon key\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n return {\n exitCode: 1,\n results: [],\n messages: [\"Error: Run `glasstrace init` first to generate an API key.\"],\n };\n }\n\n // Step 2: Check marker file\n const markerPath = path.join(projectRoot, \".glasstrace\", \"mcp-connected\");\n if (fs.existsSync(markerPath) && !force) {\n return {\n exitCode: 0,\n results: [],\n messages: [\"MCP already configured. Use --force to reconfigure.\"],\n };\n }\n\n // Step 3: Detect agents\n const agents = await detectAgents(projectRoot);\n const detectedNonGeneric = agents.filter((a) => a.name !== \"generic\");\n\n // If no specific agents found, include the generic fallback so the command\n // still produces a usable .glasstrace/mcp.json (matching init behavior).\n const targetAgents =\n detectedNonGeneric.length > 0\n ? detectedNonGeneric\n : agents.filter((a) => a.name === \"generic\");\n\n if (dryRun) {\n messages.push(\"Dry run: would perform the following actions:\", \"\");\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n if (agent.cliAvailable) {\n messages.push(\n ` ${name}: Register via CLI (${agent.name} mcp add)`,\n );\n } else if (agent.mcpConfigPath !== null) {\n messages.push(\n ` ${name}: Write config to ${agent.mcpConfigPath}`,\n );\n }\n if (agent.infoFilePath !== null) {\n messages.push(\n ` ${name}: Inject info section into ${agent.infoFilePath}`,\n );\n }\n }\n messages.push(\n \"\",\n \" Update .gitignore with MCP config paths\",\n \" Create .glasstrace/mcp-connected marker\",\n );\n return { exitCode: 0, results: [], messages };\n }\n\n // Step 4: Register with each agent\n const results: AgentResult[] = [];\n\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n\n // Try CLI registration first (not applicable for generic)\n if (agent.name !== \"generic\") {\n const cliSuccess = await registerViaCli(agent, anonKey);\n if (cliSuccess) {\n // Still inject info section if applicable\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"cli\",\n message: `${name}: Registered via CLI`,\n });\n continue;\n }\n }\n\n // Fall back to file-based config\n if (agent.mcpConfigPath !== null) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config was written (writeMcpConfig swallows permission errors)\n if (fs.existsSync(agent.mcpConfigPath)) {\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"file\",\n message: `${name}: Configured via ${agent.mcpConfigPath}`,\n });\n continue;\n }\n\n // writeMcpConfig returned without throwing but file doesn't exist\n // (permission denied handled gracefully inside writeMcpConfig)\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`,\n });\n continue;\n } catch (err) {\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`,\n });\n continue;\n }\n }\n\n results.push({\n agent: agent.name,\n success: false,\n method: \"skipped\",\n message: `${name}: No registration method available`,\n });\n }\n\n // Step 5: Update gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Step 6: Create marker file if at least one succeeded\n const anySuccess = results.some((r) => r.success);\n\n if (anySuccess) {\n await scaffoldMcpMarker(projectRoot, anonKey);\n }\n\n // Step 7: Build summary messages\n messages.push(\"\", \"MCP registration summary:\");\n for (const result of results) {\n const icon = result.success ? \"+\" : \"-\";\n messages.push(` [${icon}] ${result.message}`);\n }\n\n if (results.length === 0) {\n messages.push(\n \" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project.\",\n );\n }\n\n if (!anySuccess && results.length > 0) {\n messages.push(\n \"\",\n \"All agent registrations failed. Check errors above.\",\n );\n return { exitCode: 1, results, messages };\n }\n\n if (anySuccess) {\n messages.push(\"\", \"MCP registration complete.\");\n }\n\n return { exitCode: 0, results, messages };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,kBAAkB;AACvC,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,iBAAiB;AAa1B,IAAM,gBAAgB,UAAU,UAAU;AAkC1C,eAAe,eACb,OACA,SACkB;AAClB,MAAI,CAAC,MAAM,cAAc;AACvB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK,UAAU;AACb,cAAM,UAAU,KAAK,UAAU;AAAA,UAC7B,MAAM;AAAA,UACN,KAAK;AAAA,UACL,SAAS,EAAE,eAAe,UAAU,OAAO,GAAG;AAAA,QAChD,CAAC;AACD,cAAM,cAAc,UAAU;AAAA,UAC5B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AACD,eAAO;AAAA,MACT;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,cAAc,SAAS;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,aAAa,MAAM;AACzB,YAAI,eAAe,QAAW,cAAW,UAAU,GAAG;AACpD,gBAAM,UAAa,gBAAa,YAAY,OAAO;AACnD,cAAI,CAAC,QAAQ,SAAS,sBAAsB,GAAG;AAC7C,kBAAM,gBACJ,QAAQ,SAAS,IAAI,IAAI,KAAK;AAChC,YAAG;AAAA,cACD;AAAA,cACA,UACE,gBACA;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,OAAO;AAAA,UACb;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MAEA,KAAK,UAAU;AACb,cAAM,cAAc,UAAU;AAAA,UAC5B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,yBAAyB,OAAO;AAAA,UAChC;AAAA,UACA;AAAA,QACF,CAAC;AACD,eAAO;AAAA,MACT;AAAA,MAEA;AACE,eAAO;AAAA,IACX;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,OAAO,SAAgD;AAC3E,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,WAAqB,CAAC;AAG5B,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,MAAI,YAAY,MAAM;AACpB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS,CAAC;AAAA,MACV,UAAU,CAAC,4DAA4D;AAAA,IACzE;AAAA,EACF;AAGA,QAAM,aAAkB,UAAK,aAAa,eAAe,eAAe;AACxE,MAAO,cAAW,UAAU,KAAK,CAAC,OAAO;AACvC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS,CAAC;AAAA,MACV,UAAU,CAAC,qDAAqD;AAAA,IAClE;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,aAAa,WAAW;AAC7C,QAAM,qBAAqB,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAIpE,QAAM,eACJ,mBAAmB,SAAS,IACxB,qBACA,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAE/C,MAAI,QAAQ;AACV,aAAS,KAAK,iDAAiD,EAAE;AACjE,eAAW,SAAS,cAAc;AAChC,YAAM,OAAO,gBAAgB,MAAM,IAAI;AACvC,UAAI,MAAM,cAAc;AACtB,iBAAS;AAAA,UACP,KAAK,IAAI,uBAAuB,MAAM,IAAI;AAAA,QAC5C;AAAA,MACF,WAAW,MAAM,kBAAkB,MAAM;AACvC,iBAAS;AAAA,UACP,KAAK,IAAI,qBAAqB,MAAM,aAAa;AAAA,QACnD;AAAA,MACF;AACA,UAAI,MAAM,iBAAiB,MAAM;AAC/B,iBAAS;AAAA,UACP,KAAK,IAAI,8BAA8B,MAAM,YAAY;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AACA,aAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG,SAAS;AAAA,EAC9C;AAGA,QAAM,UAAyB,CAAC;AAEhC,aAAW,SAAS,cAAc;AAChC,UAAM,OAAO,gBAAgB,MAAM,IAAI;AAGvC,QAAI,MAAM,SAAS,WAAW;AAC5B,YAAM,aAAa,MAAM,eAAe,OAAO,OAAO;AACtD,UAAI,YAAY;AAEd,cAAM,cAAc,oBAAoB,OAAO,YAAY;AAC3D,YAAI,gBAAgB,IAAI;AACtB,gBAAM,kBAAkB,OAAO,aAAa,WAAW;AAAA,QACzD;AACA,gBAAQ,KAAK;AAAA,UACX,OAAO,MAAM;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,SAAS,GAAG,IAAI;AAAA,QAClB,CAAC;AACD;AAAA,MACF;AAAA,IACF;AAGA,QAAI,MAAM,kBAAkB,MAAM;AAChC,UAAI;AACF,cAAM,gBAAgB,kBAAkB,OAAO,cAAc,OAAO;AACpE,cAAM,eAAe,OAAO,eAAe,WAAW;AAGtD,YAAO,cAAW,MAAM,aAAa,GAAG;AACtC,gBAAM,cAAc,oBAAoB,OAAO,YAAY;AAC3D,cAAI,gBAAgB,IAAI;AACtB,kBAAM,kBAAkB,OAAO,aAAa,WAAW;AAAA,UACzD;AACA,kBAAQ,KAAK;AAAA,YACX,OAAO,MAAM;AAAA,YACb,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,SAAS,GAAG,IAAI,oBAAoB,MAAM,aAAa;AAAA,UACzD,CAAC;AACD;AAAA,QACF;AAIA,gBAAQ,KAAK;AAAA,UACX,OAAO,MAAM;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,SAAS,GAAG,IAAI,+BAA+B,MAAM,aAAa;AAAA,QACpE,CAAC;AACD;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ,KAAK;AAAA,UACX,OAAO,MAAM;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,SAAS,GAAG,IAAI,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAChF,CAAC;AACD;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,KAAK;AAAA,MACX,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS,GAAG,IAAI;AAAA,IAClB,CAAC;AAAA,EACH;AAGA,QAAM;AAAA,IACJ,CAAC,aAAa,oBAAoB,yBAAyB,oBAAoB;AAAA,IAC/E;AAAA,EACF;AAGA,QAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO;AAEhD,MAAI,YAAY;AACd,UAAM,kBAAkB,aAAa,OAAO;AAAA,EAC9C;AAGA,WAAS,KAAK,IAAI,2BAA2B;AAC7C,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,OAAO,UAAU,MAAM;AACpC,aAAS,KAAK,MAAM,IAAI,KAAK,OAAO,OAAO,EAAE;AAAA,EAC/C;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,aAAS;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,cAAc,QAAQ,SAAS,GAAG;AACrC,aAAS;AAAA,MACP;AAAA,MACA;AAAA,IACF;AACA,WAAO,EAAE,UAAU,GAAG,SAAS,SAAS;AAAA,EAC1C;AAEA,MAAI,YAAY;AACd,aAAS,KAAK,IAAI,4BAA4B;AAAA,EAChD;AAEA,SAAO,EAAE,UAAU,GAAG,SAAS,SAAS;AAC1C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/mcp-add.ts"],"sourcesContent":["import { execFile as execFileCb } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport {\n writeMcpConfig,\n injectInfoSection,\n updateGitignore,\n} from \"../agent-detection/inject.js\";\nimport { scaffoldMcpMarker } from \"./scaffolder.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\n\nconst execFileAsync = promisify(execFileCb);\n\n/** Options for the mcp add command. */\nexport interface McpAddOptions {\n force?: boolean;\n dryRun?: boolean;\n}\n\n/** Result of the mcp add command. */\nexport interface McpAddResult {\n exitCode: number;\n results: AgentResult[];\n messages: string[];\n}\n\n/**\n * Result of a single agent registration attempt.\n */\ninterface AgentResult {\n agent: DetectedAgent[\"name\"];\n success: boolean;\n method: \"cli\" | \"file\" | \"skipped\";\n message: string;\n}\n\n/**\n * Attempts CLI-based MCP registration for agents that support it.\n * Returns true if the CLI command succeeded.\n *\n * Note: anonymous keys are passed in process arguments for CLI registration.\n * This is acceptable because anon keys are non-secret identifiers (not\n * credentials) designed for semi-public use. They identify a project\n * but cannot be used to access user data.\n */\nasync function registerViaCli(\n agent: DetectedAgent,\n anonKey: string,\n): Promise<boolean> {\n if (!agent.cliAvailable) {\n return false;\n }\n\n try {\n switch (agent.name) {\n case \"claude\": {\n const payload = JSON.stringify({\n type: \"http\",\n url: MCP_ENDPOINT,\n headers: { Authorization: `Bearer ${anonKey}` },\n });\n await execFileAsync(\"claude\", [\n \"mcp\",\n \"add-json\",\n \"glasstrace\",\n payload,\n \"--scope\",\n \"project\",\n ]);\n return true;\n }\n\n case \"codex\": {\n await execFileAsync(\"codex\", [\n \"mcp\",\n \"add\",\n \"glasstrace\",\n \"--url\",\n MCP_ENDPOINT,\n ]);\n // Ensure .codex/config.toml has bearer_token_env_var\n const configPath = agent.mcpConfigPath;\n if (configPath !== null && fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"bearer_token_env_var\")) {\n const appendContent =\n content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(\n configPath,\n content +\n appendContent +\n 'bearer_token_env_var = \"GLASSTRACE_API_KEY\"\\n',\n \"utf-8\",\n );\n }\n }\n process.stderr.write(\n \" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\\n\",\n );\n return true;\n }\n\n case \"gemini\": {\n await execFileAsync(\"gemini\", [\n \"mcp\",\n \"add\",\n \"--transport\",\n \"http\",\n \"--header\",\n `Authorization: Bearer ${anonKey}`,\n \"glasstrace\",\n MCP_ENDPOINT,\n ]);\n return true;\n }\n\n default:\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Registers the Glasstrace MCP server with detected AI coding agents.\n *\n * For each agent, attempts native CLI registration first, then falls back\n * to file-based configuration. Creates a marker file on success to enable\n * idempotent re-runs.\n *\n * Returns a structured result instead of calling process.exit(), so the\n * CLI entry point can decide how to handle the outcome.\n *\n * @param options - Control flags for force and dry-run modes.\n */\nexport async function mcpAdd(options?: McpAddOptions): Promise<McpAddResult> {\n const force = options?.force ?? false;\n const dryRun = options?.dryRun ?? false;\n const projectRoot = process.cwd();\n const messages: string[] = [];\n\n // Step 1: Read anon key\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n return {\n exitCode: 1,\n results: [],\n messages: [\"Error: Run `glasstrace init` first to generate an API key.\"],\n };\n }\n\n // Step 2: Check marker file\n const markerPath = path.join(projectRoot, \".glasstrace\", \"mcp-connected\");\n if (fs.existsSync(markerPath) && !force) {\n return {\n exitCode: 0,\n results: [],\n messages: [\"MCP already configured. Use --force to reconfigure.\"],\n };\n }\n\n // Step 3: Detect agents\n const agents = await detectAgents(projectRoot);\n const detectedNonGeneric = agents.filter((a) => a.name !== \"generic\");\n\n // If no specific agents found, include the generic fallback so the command\n // still produces a usable .glasstrace/mcp.json (matching init behavior).\n const targetAgents =\n detectedNonGeneric.length > 0\n ? detectedNonGeneric\n : agents.filter((a) => a.name === \"generic\");\n\n if (dryRun) {\n messages.push(\"Dry run: would perform the following actions:\", \"\");\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n if (agent.cliAvailable) {\n messages.push(\n ` ${name}: Register via CLI (${agent.name} mcp add)`,\n );\n } else if (agent.mcpConfigPath !== null) {\n messages.push(\n ` ${name}: Write config to ${agent.mcpConfigPath}`,\n );\n }\n if (agent.infoFilePath !== null) {\n messages.push(\n ` ${name}: Inject info section into ${agent.infoFilePath}`,\n );\n }\n }\n messages.push(\n \"\",\n \" Update .gitignore with MCP config paths\",\n \" Create .glasstrace/mcp-connected marker\",\n );\n return { exitCode: 0, results: [], messages };\n }\n\n // Step 4: Register with each agent\n const results: AgentResult[] = [];\n\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n\n // Try CLI registration first (not applicable for generic)\n if (agent.name !== \"generic\") {\n const cliSuccess = await registerViaCli(agent, anonKey);\n if (cliSuccess) {\n // Still inject info section if applicable\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"cli\",\n message: `${name}: Registered via CLI`,\n });\n continue;\n }\n }\n\n // Fall back to file-based config\n if (agent.mcpConfigPath !== null) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config was written (writeMcpConfig swallows permission errors)\n if (fs.existsSync(agent.mcpConfigPath)) {\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"file\",\n message: `${name}: Configured via ${agent.mcpConfigPath}`,\n });\n continue;\n }\n\n // writeMcpConfig returned without throwing but file doesn't exist\n // (permission denied handled gracefully inside writeMcpConfig)\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`,\n });\n continue;\n } catch (err) {\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`,\n });\n continue;\n }\n }\n\n results.push({\n agent: agent.name,\n success: false,\n method: \"skipped\",\n message: `${name}: No registration method available`,\n });\n }\n\n // Step 5: Update gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Step 6: Create marker file if at least one succeeded\n const anySuccess = results.some((r) => r.success);\n\n if (anySuccess) {\n await scaffoldMcpMarker(projectRoot, anonKey);\n }\n\n // Step 7: Build summary messages\n messages.push(\"\", \"MCP registration summary:\");\n for (const result of results) {\n const icon = result.success ? \"+\" : \"-\";\n messages.push(` [${icon}] ${result.message}`);\n }\n\n if (results.length === 0) {\n messages.push(\n \" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project.\",\n );\n }\n\n if (!anySuccess && results.length > 0) {\n messages.push(\n \"\",\n \"All agent registrations failed. Check errors above.\",\n );\n return { exitCode: 1, results, messages };\n }\n\n if (anySuccess) {\n messages.push(\"\", \"MCP registration complete.\");\n }\n\n return { exitCode: 0, results, messages };\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli/uninit.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/cli/uninit.ts
|
|
31
31
|
var uninit_exports = {};
|
|
32
32
|
__export(uninit_exports, {
|
|
33
|
+
findMatchingDelimiter: () => findMatchingDelimiter,
|
|
33
34
|
findMatchingParen: () => findMatchingParen,
|
|
34
35
|
isInitCreatedInstrumentation: () => isInitCreatedInstrumentation,
|
|
35
36
|
processJsonMcpConfig: () => processJsonMcpConfig,
|
|
@@ -38,6 +39,7 @@ __export(uninit_exports, {
|
|
|
38
39
|
removeMarkerSection: () => removeMarkerSection,
|
|
39
40
|
removeRegisterGlasstrace: () => removeRegisterGlasstrace,
|
|
40
41
|
runUninit: () => runUninit,
|
|
42
|
+
skipString: () => skipString,
|
|
41
43
|
unwrapCJSExport: () => unwrapCJSExport,
|
|
42
44
|
unwrapExport: () => unwrapExport
|
|
43
45
|
});
|
|
@@ -56,20 +58,60 @@ var AGENT_INFO_FILES = [
|
|
|
56
58
|
"codex.md",
|
|
57
59
|
".cursorrules"
|
|
58
60
|
];
|
|
59
|
-
function
|
|
61
|
+
function skipString(text, start, quote) {
|
|
62
|
+
let i = start + 1;
|
|
63
|
+
while (i < text.length) {
|
|
64
|
+
if (text[i] === "\\") {
|
|
65
|
+
i += 2;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (text[i] === quote) {
|
|
69
|
+
return i + 1;
|
|
70
|
+
}
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
return text.length;
|
|
74
|
+
}
|
|
75
|
+
function findMatchingDelimiter(text, openPos, openChar, closeChar) {
|
|
60
76
|
let depth = 0;
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
let i = openPos;
|
|
78
|
+
while (i < text.length) {
|
|
79
|
+
const ch = text[i];
|
|
80
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
81
|
+
i = skipString(text, i, ch);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
85
|
+
const newline = text.indexOf("\n", i);
|
|
86
|
+
if (newline === -1) {
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
i = newline + 1;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
93
|
+
const end = text.indexOf("*/", i + 2);
|
|
94
|
+
if (end === -1) {
|
|
95
|
+
return -1;
|
|
96
|
+
}
|
|
97
|
+
i = end + 2;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (ch === openChar) {
|
|
63
101
|
depth++;
|
|
64
|
-
} else if (
|
|
102
|
+
} else if (ch === closeChar) {
|
|
65
103
|
depth--;
|
|
66
104
|
if (depth === 0) {
|
|
67
105
|
return i;
|
|
68
106
|
}
|
|
69
107
|
}
|
|
108
|
+
i++;
|
|
70
109
|
}
|
|
71
110
|
return -1;
|
|
72
111
|
}
|
|
112
|
+
function findMatchingParen(text, openPos) {
|
|
113
|
+
return findMatchingDelimiter(text, openPos, "(", ")");
|
|
114
|
+
}
|
|
73
115
|
function unwrapExport(content) {
|
|
74
116
|
const pattern = /export\s+default\s+withGlasstraceConfig\s*\(/;
|
|
75
117
|
const match = pattern.exec(content);
|
|
@@ -200,18 +242,7 @@ function isInitCreatedInstrumentation(content) {
|
|
|
200
242
|
return topLevelBefore.length === 0 && topLevelAfter.length === 0;
|
|
201
243
|
}
|
|
202
244
|
function findMatchingBrace(text, openPos) {
|
|
203
|
-
|
|
204
|
-
for (let i = openPos; i < text.length; i++) {
|
|
205
|
-
if (text[i] === "{") {
|
|
206
|
-
depth++;
|
|
207
|
-
} else if (text[i] === "}") {
|
|
208
|
-
depth--;
|
|
209
|
-
if (depth === 0) {
|
|
210
|
-
return i;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return -1;
|
|
245
|
+
return findMatchingDelimiter(text, openPos, "{", "}");
|
|
215
246
|
}
|
|
216
247
|
function removeRegisterGlasstrace(content) {
|
|
217
248
|
let result = content;
|
|
@@ -521,16 +552,22 @@ async function runUninit(options) {
|
|
|
521
552
|
if (fs.existsSync(windsurfConfigPath)) {
|
|
522
553
|
const content = fs.readFileSync(windsurfConfigPath, "utf-8");
|
|
523
554
|
const windsurfResult = processJsonMcpConfig(content);
|
|
555
|
+
const home = os.homedir();
|
|
556
|
+
const displayPath = windsurfConfigPath.startsWith(home) ? "~" + windsurfConfigPath.slice(home.length) : windsurfConfigPath;
|
|
524
557
|
if (windsurfResult.action === "deleted") {
|
|
525
558
|
if (!dryRun) {
|
|
526
559
|
fs.unlinkSync(windsurfConfigPath);
|
|
527
560
|
}
|
|
528
|
-
summary.push(
|
|
561
|
+
summary.push(
|
|
562
|
+
`${prefix}Deleted global Windsurf config (${displayPath})`
|
|
563
|
+
);
|
|
529
564
|
} else if (windsurfResult.action === "removed-key" && windsurfResult.content !== void 0) {
|
|
530
565
|
if (!dryRun) {
|
|
531
566
|
fs.writeFileSync(windsurfConfigPath, windsurfResult.content, "utf-8");
|
|
532
567
|
}
|
|
533
|
-
summary.push(
|
|
568
|
+
summary.push(
|
|
569
|
+
`${prefix}Removed glasstrace from global Windsurf config (${displayPath})`
|
|
570
|
+
);
|
|
534
571
|
}
|
|
535
572
|
}
|
|
536
573
|
}
|
|
@@ -573,6 +610,7 @@ async function runUninit(options) {
|
|
|
573
610
|
}
|
|
574
611
|
// Annotate the CommonJS export names for ESM import in node:
|
|
575
612
|
0 && (module.exports = {
|
|
613
|
+
findMatchingDelimiter,
|
|
576
614
|
findMatchingParen,
|
|
577
615
|
isInitCreatedInstrumentation,
|
|
578
616
|
processJsonMcpConfig,
|
|
@@ -581,6 +619,7 @@ async function runUninit(options) {
|
|
|
581
619
|
removeMarkerSection,
|
|
582
620
|
removeRegisterGlasstrace,
|
|
583
621
|
runUninit,
|
|
622
|
+
skipString,
|
|
584
623
|
unwrapCJSExport,
|
|
585
624
|
unwrapExport
|
|
586
625
|
});
|