@lensmcp/nx-plugin 1.18.4 → 1.18.6

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.
@@ -1,166 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = agentVerifyExecutor;
4
- const node_child_process_1 = require("node:child_process");
5
- const node_fs_1 = require("node:fs");
6
- const node_path_1 = require("node:path");
7
- /**
8
- * `agent-verify` — the deterministic verification loop the agent runs
9
- * to know "did my fix work?".
10
- *
11
- * Phase 2 covers the frontend slice: typecheck (`tsc --noEmit`), lint
12
- * (`eslint`), and build (`vite build`). Each stage runs sequentially;
13
- * a stage failure marks the verify as `failed` but later stages still
14
- * run so the agent gets a complete picture, not just the first error.
15
- *
16
- * The report is written to `.lensmcp/verifications/<project>-<ts>.json`
17
- * and to `.lensmcp/verifications/latest.json` for easy resource access.
18
- * The MCP-side `agent://latest-verification` resource (Phase 2.5) reads
19
- * from `latest.json`.
20
- */
21
- async function agentVerifyExecutor(options, context) {
22
- const opts = {
23
- kind: options.kind ?? 'vite-react',
24
- skipTypecheck: options.skipTypecheck ?? false,
25
- skipLint: options.skipLint ?? false,
26
- skipBuild: options.skipBuild ?? false,
27
- projectRoot: options.projectRoot,
28
- };
29
- const projectName = context.projectName ?? null;
30
- const projectRoot = opts.projectRoot
31
- ? (0, node_path_1.resolve)(context.root, opts.projectRoot)
32
- : projectName && context.projectsConfigurations
33
- ? (0, node_path_1.resolve)(context.root, context.projectsConfigurations.projects[projectName]?.root ?? '.')
34
- : context.root;
35
- const startedAt = Date.now();
36
- const stages = [];
37
- // 1. typecheck — `tsc --noEmit -p <root>` if a tsconfig is present.
38
- if (!opts.skipTypecheck) {
39
- const tscBin = locateBin('tsc', [projectRoot, context.root]);
40
- const tsconfig = resolveFirstExisting(['tsconfig.json'], projectRoot);
41
- if (!tscBin || !tsconfig) {
42
- stages.push({ name: 'typecheck', result: 'skipped', durationMs: 0, summary: 'no tsc/tsconfig' });
43
- }
44
- else {
45
- const t0 = Date.now();
46
- const r = runStageCommand('tsc --noEmit', tscBin, ['--noEmit', '-p', tsconfig], projectRoot);
47
- stages.push({
48
- name: 'typecheck',
49
- result: r.status === 0 ? 'passed' : 'failed',
50
- durationMs: Date.now() - t0,
51
- exitCode: r.status ?? -1,
52
- });
53
- }
54
- }
55
- else {
56
- stages.push({ name: 'typecheck', result: 'skipped', durationMs: 0 });
57
- }
58
- // 2. lint — `eslint .` if eslint is present.
59
- if (!opts.skipLint) {
60
- const eslintBin = locateBin('eslint', [projectRoot, context.root]);
61
- if (!eslintBin) {
62
- stages.push({ name: 'lint', result: 'skipped', durationMs: 0, summary: 'no eslint' });
63
- }
64
- else {
65
- const t0 = Date.now();
66
- const r = runStageCommand('eslint .', eslintBin, ['.'], projectRoot);
67
- stages.push({
68
- name: 'lint',
69
- result: r.status === 0 ? 'passed' : 'failed',
70
- durationMs: Date.now() - t0,
71
- exitCode: r.status ?? -1,
72
- });
73
- }
74
- }
75
- else {
76
- stages.push({ name: 'lint', result: 'skipped', durationMs: 0 });
77
- }
78
- // 3. build — `vite build` if vite is present.
79
- if (!opts.skipBuild) {
80
- const viteBin = locateBin('vite', [projectRoot, context.root]);
81
- if (!viteBin) {
82
- stages.push({ name: 'build', result: 'skipped', durationMs: 0, summary: 'no vite' });
83
- }
84
- else {
85
- const viteConfig = resolveFirstExisting(['vite.config.ts', 'vite.config.js', 'vite.config.mjs'], projectRoot) ??
86
- (0, node_path_1.join)(projectRoot, 'vite.config.ts');
87
- const t0 = Date.now();
88
- const r = runStageCommand('vite build', viteBin, ['build', '--config', viteConfig, projectRoot], projectRoot);
89
- stages.push({
90
- name: 'build',
91
- result: r.status === 0 ? 'passed' : 'failed',
92
- durationMs: Date.now() - t0,
93
- exitCode: r.status ?? -1,
94
- });
95
- }
96
- }
97
- else {
98
- stages.push({ name: 'build', result: 'skipped', durationMs: 0 });
99
- }
100
- const status = stages.some((s) => s.result === 'failed') ? 'failed' : 'passed';
101
- const report = {
102
- schemaVersion: 1,
103
- project: projectName,
104
- timestamp: new Date(startedAt).toISOString(),
105
- durationMs: Date.now() - startedAt,
106
- status,
107
- stages,
108
- };
109
- const outDir = (0, node_path_1.join)(context.root, '.lensmcp', 'verifications');
110
- try {
111
- (0, node_fs_1.mkdirSync)(outDir, { recursive: true });
112
- const ts = startedAt;
113
- // Project names like `@lensmcp/example-web` contain `/`. Flatten so
114
- // the report filename is a single path segment.
115
- const safeName = (projectName ?? 'app').replace(/[^a-zA-Z0-9_-]+/g, '-');
116
- const filename = `${safeName}-${ts}.json`;
117
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, filename), JSON.stringify(report, null, 2) + '\n');
118
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(outDir, 'latest.json'), JSON.stringify(report, null, 2) + '\n');
119
- }
120
- catch (e) {
121
- console.warn(`[agent-verify] could not write report: ${e.message}`);
122
- }
123
- for (const s of report.stages) {
124
- console.log(`[agent-verify] ${s.name}: ${s.result}${s.exitCode !== undefined ? ` (exit ${s.exitCode})` : ''}`);
125
- }
126
- console.log(`[agent-verify] ${status} in ${report.durationMs}ms`);
127
- return { success: status === 'passed' };
128
- }
129
- /**
130
- * Run a verify sub-command, capturing its output and re-emitting it INDENTED.
131
- *
132
- * agent-verify deliberately runs commands that may fail — it IS the agent's
133
- * edit→verify loop, so a failing stage is a normal, expected outcome. In CI a
134
- * raw tool error printed at column 0 (`file(line,col): error TS....`) is picked
135
- * up by GitHub's problem matchers and turned into a spurious build *annotation*.
136
- * Indenting every line by two spaces keeps the output fully readable while
137
- * defeating the matchers (they anchor on a non-space first character), so a
138
- * verify failure never masquerades as a CI error.
139
- */
140
- function runStageCommand(label, bin, args, cwd) {
141
- console.log(`[agent-verify] ${label}`);
142
- const r = (0, node_child_process_1.spawnSync)(bin, args, { cwd, encoding: 'utf8' });
143
- const out = `${r.stdout ?? ''}${r.stderr ?? ''}`;
144
- if (out.trim())
145
- process.stdout.write(out.replace(/^(?=.)/gm, ' '));
146
- return r;
147
- }
148
- function locateBin(name, roots) {
149
- for (const root of roots) {
150
- const candidate = (0, node_path_1.join)(root, 'node_modules', '.bin', name);
151
- if ((0, node_fs_1.existsSync)(candidate))
152
- return candidate;
153
- }
154
- return undefined;
155
- }
156
- function resolveFirstExisting(names, root) {
157
- for (const n of names) {
158
- const p = (0, node_path_1.join)(root, n);
159
- if ((0, node_fs_1.existsSync)(p))
160
- return p;
161
- }
162
- return undefined;
163
- }
164
- // Unused helpers, kept available for future re-use by tools that may
165
- // drive the report differently.
166
- void node_path_1.dirname;
1
+ "use strict";var k=Object.defineProperty;var d=(s,e)=>k(s,"name",{value:e,configurable:!0});var g=Object.defineProperty,l=d((s,e)=>g(s,"name",{value:e,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=agentVerifyExecutor;const node_child_process_1=require("node:child_process"),node_fs_1=require("node:fs"),node_path_1=require("node:path");async function agentVerifyExecutor(s,e){const i={kind:s.kind??"vite-react",skipTypecheck:s.skipTypecheck??!1,skipLint:s.skipLint??!1,skipBuild:s.skipBuild??!1,projectRoot:s.projectRoot},n=e.projectName??null,o=i.projectRoot?(0,node_path_1.resolve)(e.root,i.projectRoot):n&&e.projectsConfigurations?(0,node_path_1.resolve)(e.root,e.projectsConfigurations.projects[n]?.root??"."):e.root,c=Date.now(),r=[];if(i.skipTypecheck)r.push({name:"typecheck",result:"skipped",durationMs:0});else{const t=locateBin("tsc",[o,e.root]),a=resolveFirstExisting(["tsconfig.json"],o);if(!t||!a)r.push({name:"typecheck",result:"skipped",durationMs:0,summary:"no tsc/tsconfig"});else{const u=Date.now(),p=runStageCommand("tsc --noEmit",t,["--noEmit","-p",a],o);r.push({name:"typecheck",result:p.status===0?"passed":"failed",durationMs:Date.now()-u,exitCode:p.status??-1})}}if(i.skipLint)r.push({name:"lint",result:"skipped",durationMs:0});else{const t=locateBin("eslint",[o,e.root]);if(!t)r.push({name:"lint",result:"skipped",durationMs:0,summary:"no eslint"});else{const a=Date.now(),u=runStageCommand("eslint .",t,["."],o);r.push({name:"lint",result:u.status===0?"passed":"failed",durationMs:Date.now()-a,exitCode:u.status??-1})}}if(i.skipBuild)r.push({name:"build",result:"skipped",durationMs:0});else{const t=locateBin("vite",[o,e.root]);if(!t)r.push({name:"build",result:"skipped",durationMs:0,summary:"no vite"});else{const a=resolveFirstExisting(["vite.config.ts","vite.config.js","vite.config.mjs"],o)??(0,node_path_1.join)(o,"vite.config.ts"),u=Date.now(),p=runStageCommand("vite build",t,["build","--config",a,o],o);r.push({name:"build",result:p.status===0?"passed":"failed",durationMs:Date.now()-u,exitCode:p.status??-1})}}const m=r.some(t=>t.result==="failed")?"failed":"passed",f={schemaVersion:1,project:n,timestamp:new Date(c).toISOString(),durationMs:Date.now()-c,status:m,stages:r},y=(0,node_path_1.join)(e.root,".lensmcp","verifications");try{(0,node_fs_1.mkdirSync)(y,{recursive:!0});const t=c,a=`${(n??"app").replace(/[^a-zA-Z0-9_-]+/g,"-")}-${t}.json`;(0,node_fs_1.writeFileSync)((0,node_path_1.join)(y,a),JSON.stringify(f,null,2)+`
2
+ `),(0,node_fs_1.writeFileSync)((0,node_path_1.join)(y,"latest.json"),JSON.stringify(f,null,2)+`
3
+ `)}catch(t){console.warn(`[agent-verify] could not write report: ${t.message}`)}for(const t of f.stages)console.log(`[agent-verify] ${t.name}: ${t.result}${t.exitCode!==void 0?` (exit ${t.exitCode})`:""}`);return console.log(`[agent-verify] ${m} in ${f.durationMs}ms`),{success:m==="passed"}}d(agentVerifyExecutor,"agentVerifyExecutor"),l(agentVerifyExecutor,"agentVerifyExecutor");function runStageCommand(s,e,i,n){console.log(`[agent-verify] ${s}`);const o=(0,node_child_process_1.spawnSync)(e,i,{cwd:n,encoding:"utf8"}),c=`${o.stdout??""}${o.stderr??""}`;return c.trim()&&process.stdout.write(c.replace(/^(?=.)/gm," ")),o}d(runStageCommand,"runStageCommand"),l(runStageCommand,"runStageCommand");function locateBin(s,e){for(const i of e){const n=(0,node_path_1.join)(i,"node_modules",".bin",s);if((0,node_fs_1.existsSync)(n))return n}}d(locateBin,"locateBin"),l(locateBin,"locateBin");function resolveFirstExisting(s,e){for(const i of s){const n=(0,node_path_1.join)(e,i);if((0,node_fs_1.existsSync)(n))return n}}d(resolveFirstExisting,"resolveFirstExisting"),l(resolveFirstExisting,"resolveFirstExisting"),node_path_1.dirname;
@@ -1,167 +1,9 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initGenerator = initGenerator;
4
- const devkit_1 = require("@nx/devkit");
5
- /** Workspace-wide config defaults, written into `nx.json#lensmcp`. */
6
- const DEFAULT_LENSMCP_CONFIG = {
7
- schemaVersion: 1,
8
- storage: 'memory',
9
- transport: 'stdio',
10
- retention: {
11
- sessions: 5,
12
- events: 5000,
13
- screenshots: 50,
14
- buildReports: 20,
15
- visualFrames: 200,
16
- heapSnapshots: 3,
17
- tracesWithErrors: 20,
18
- verifications: 10,
19
- },
20
- redaction: {
21
- headers: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],
22
- paths: ['password', 'token', 'secret', 'apiKey', 'api_key'],
23
- },
24
- channels: {
25
- blockingStatusChanged: { enabled: true, minSeverity: 'error' },
26
- memoryLeakSuspected: { enabled: true, minSeverity: 'warning' },
27
- bundleRegression: { enabled: true, minSeverity: 'warning' },
28
- visualViolationBlocking: { enabled: true, minSeverity: 'error' },
29
- verificationCompleted: { enabled: true, minSeverity: 'info' },
30
- },
31
- // Tunable detection thresholds — when each signal fires. Edit to taste;
32
- // the server reads these at boot (see @lensmcp/core resolveThresholds).
33
- thresholds: {
34
- dbInLoop: 5,
35
- nPlusOne: 3,
36
- renderStorm: 5,
37
- slowRouteMs: 500,
38
- slowRenderMs: 16,
39
- },
40
- };
41
- const LENSMCP_GITIGNORE_MARKER = '# LensMCP runtime artifacts (per-session storage)';
42
- const LENSMCP_GITIGNORE_ENTRY = '.lensmcp/';
43
- async function initGenerator(tree, rawOptions = {}) {
44
- const options = {
45
- skipFormat: rawOptions.skipFormat ?? false,
46
- registerHostConfig: rawOptions.registerHostConfig ?? 'auto',
47
- };
48
- const steps = [];
49
- // 1. Register the plugin + lensmcp config in nx.json (idempotent).
50
- if (tree.exists('nx.json')) {
51
- (0, devkit_1.updateJson)(tree, 'nx.json', (nx) => {
52
- nx.plugins = Array.isArray(nx.plugins) ? nx.plugins : [];
53
- const alreadyRegistered = nx.plugins.some((p) => {
54
- if (typeof p === 'string')
55
- return p === '@lensmcp/nx-plugin';
56
- if (p && typeof p === 'object' && 'plugin' in p) {
57
- return p.plugin === '@lensmcp/nx-plugin';
58
- }
59
- return false;
60
- });
61
- if (!alreadyRegistered) {
62
- nx.plugins.push('@lensmcp/nx-plugin');
63
- steps.push('nx.json: registered @lensmcp/nx-plugin');
64
- }
65
- const existing = (nx.lensmcp ?? {});
66
- nx.lensmcp = mergeDeep(DEFAULT_LENSMCP_CONFIG, existing);
67
- if (!('lensmcp' in nx) || Object.keys(existing).length === 0) {
68
- steps.push('nx.json: added workspace-wide `lensmcp` config block');
69
- }
70
- return nx;
71
- });
72
- }
73
- else {
74
- throw new Error('No nx.json found at the workspace root. Are you in a Nx workspace?');
75
- }
76
- // 2. .gitignore — add `.lensmcp/` line (idempotent).
77
- const gitignorePath = '.gitignore';
78
- const existing = tree.exists(gitignorePath) ? tree.read(gitignorePath, 'utf-8') ?? '' : '';
79
- if (!existing.split('\n').some((l) => l.trim() === LENSMCP_GITIGNORE_ENTRY)) {
80
- const trailingNewline = existing.endsWith('\n') ? '' : '\n';
81
- tree.write(gitignorePath, `${existing}${trailingNewline}\n${LENSMCP_GITIGNORE_MARKER}\n${LENSMCP_GITIGNORE_ENTRY}\n`);
82
- steps.push(`${gitignorePath}: added \`${LENSMCP_GITIGNORE_ENTRY}\``);
83
- }
84
- // 3. Reserve the .lensmcp/ directory with a sentinel file.
85
- const keepPath = (0, devkit_1.joinPathFragments)('.lensmcp', '.keep');
86
- if (!tree.exists(keepPath)) {
87
- tree.write(keepPath, '');
88
- steps.push(`${keepPath}: created`);
89
- }
90
- // 4. Write an install trail so subsequent re-runs can read what was
91
- // done and (in future generators) un-install precisely.
92
- const trailPath = (0, devkit_1.joinPathFragments)('.lensmcp', 'install-trail.json');
93
- const existingTrail = tree.exists(trailPath)
94
- ? JSON.parse(tree.read(trailPath, 'utf-8') ?? '{}')
95
- : undefined;
96
- const trail = existingTrail ?? {
97
- schemaVersion: 1,
98
- installedAt: new Date().toISOString(),
99
- steps: [],
100
- };
101
- trail.steps.push(...steps);
102
- tree.write(trailPath, JSON.stringify(trail, null, 2) + '\n');
103
- // 5. Register the LensMCP MCP server in the workspace `.mcp.json` so any
104
- // coding agent (Claude Code, Cursor, …) opening this project gets the
105
- // lens automatically. Workspace-local edits happen here, in the Nx
106
- // tree (idempotent). Global agent configs (~/.claude, ~/.cursor) are
107
- // only touched by `lensmcp` when `registerHostConfig: 'always'`,
108
- // since those live outside the workspace.
109
- if (options.registerHostConfig !== 'never') {
110
- registerWorkspaceMcpConfig(tree, steps);
111
- }
112
- if (!options.skipFormat) {
113
- await (0, devkit_1.formatFiles)(tree);
114
- }
115
- }
116
- /** The stdio MCP server entry a host agent uses to launch LensMCP. */
117
- const LENSMCP_MCP_SERVER_ENTRY = {
118
- command: 'npx',
119
- args: ['-y', 'lensmcp', 'mcp'],
120
- };
121
- /**
122
- * Ensure `<workspace>/.mcp.json` has a `lensmcp` MCP server entry.
123
- * Idempotent: creates the file if absent, merges into existing
124
- * `mcpServers`, and never clobbers a `lensmcp` entry the user already
125
- * customised.
126
- */
127
- function registerWorkspaceMcpConfig(tree, steps) {
128
- const path = '.mcp.json';
129
- let config = {};
130
- if (tree.exists(path)) {
131
- try {
132
- config = JSON.parse(tree.read(path, 'utf-8') ?? '{}');
133
- }
134
- catch {
135
- config = {};
136
- }
137
- }
138
- if (!config || typeof config !== 'object')
139
- config = {};
140
- if (!config.mcpServers || typeof config.mcpServers !== 'object') {
141
- config.mcpServers = {};
142
- }
143
- if (config.mcpServers['lensmcp'])
144
- return; // already registered — leave it
145
- config.mcpServers['lensmcp'] = { ...LENSMCP_MCP_SERVER_ENTRY };
146
- tree.write(path, JSON.stringify(config, null, 2) + '\n');
147
- steps.push('.mcp.json: registered lensmcp MCP server (stdio)');
148
- }
149
- exports.default = initGenerator;
150
- // -------- helpers --------
151
- function mergeDeep(defaults, overrides) {
152
- const out = { ...defaults };
153
- for (const [key, value] of Object.entries(overrides)) {
154
- if (value !== null &&
155
- typeof value === 'object' &&
156
- !Array.isArray(value) &&
157
- out[key] !== null &&
158
- typeof out[key] === 'object' &&
159
- !Array.isArray(out[key])) {
160
- out[key] = mergeDeep(out[key], value);
161
- }
162
- else {
163
- out[key] = value;
164
- }
165
- }
166
- return out;
167
- }
1
+ "use strict";var f=Object.defineProperty;var p=(n,t)=>f(n,"name",{value:t,configurable:!0});var m=Object.defineProperty,a=p((n,t)=>m(n,"name",{value:t,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.initGenerator=initGenerator;const devkit_1=require("@nx/devkit"),DEFAULT_LENSMCP_CONFIG={schemaVersion:1,storage:"memory",transport:"stdio",retention:{sessions:5,events:5e3,screenshots:50,buildReports:20,visualFrames:200,heapSnapshots:3,tracesWithErrors:20,verifications:10},redaction:{headers:["authorization","cookie","set-cookie","x-api-key"],paths:["password","token","secret","apiKey","api_key"]},channels:{blockingStatusChanged:{enabled:!0,minSeverity:"error"},memoryLeakSuspected:{enabled:!0,minSeverity:"warning"},bundleRegression:{enabled:!0,minSeverity:"warning"},visualViolationBlocking:{enabled:!0,minSeverity:"error"},verificationCompleted:{enabled:!0,minSeverity:"info"}},thresholds:{dbInLoop:5,nPlusOne:3,renderStorm:5,slowRouteMs:500,slowRenderMs:16}},LENSMCP_GITIGNORE_MARKER="# LensMCP runtime artifacts (per-session storage)",LENSMCP_GITIGNORE_ENTRY=".lensmcp/";async function initGenerator(n,t={}){const s={skipFormat:t.skipFormat??!1,registerHostConfig:t.registerHostConfig??"auto"},e=[];if(n.exists("nx.json"))(0,devkit_1.updateJson)(n,"nx.json",r=>{r.plugins=Array.isArray(r.plugins)?r.plugins:[],r.plugins.some(o=>typeof o=="string"?o==="@lensmcp/nx-plugin":o&&typeof o=="object"&&"plugin"in o?o.plugin==="@lensmcp/nx-plugin":!1)||(r.plugins.push("@lensmcp/nx-plugin"),e.push("nx.json: registered @lensmcp/nx-plugin"));const d=r.lensmcp??{};return r.lensmcp=mergeDeep(DEFAULT_LENSMCP_CONFIG,d),(!("lensmcp"in r)||Object.keys(d).length===0)&&e.push("nx.json: added workspace-wide `lensmcp` config block"),r});else throw new Error("No nx.json found at the workspace root. Are you in a Nx workspace?");const i=".gitignore",c=n.exists(i)?n.read(i,"utf-8")??"":"";if(!c.split(`
2
+ `).some(r=>r.trim()===LENSMCP_GITIGNORE_ENTRY)){const r=c.endsWith(`
3
+ `)?"":`
4
+ `;n.write(i,`${c}${r}
5
+ ${LENSMCP_GITIGNORE_MARKER}
6
+ ${LENSMCP_GITIGNORE_ENTRY}
7
+ `),e.push(`${i}: added \`${LENSMCP_GITIGNORE_ENTRY}\``)}const l=(0,devkit_1.joinPathFragments)(".lensmcp",".keep");n.exists(l)||(n.write(l,""),e.push(`${l}: created`));const u=(0,devkit_1.joinPathFragments)(".lensmcp","install-trail.json"),g=(n.exists(u)?JSON.parse(n.read(u,"utf-8")??"{}"):void 0)??{schemaVersion:1,installedAt:new Date().toISOString(),steps:[]};g.steps.push(...e),n.write(u,JSON.stringify(g,null,2)+`
8
+ `),s.registerHostConfig!=="never"&&registerWorkspaceMcpConfig(n,e),s.skipFormat||await(0,devkit_1.formatFiles)(n)}p(initGenerator,"initGenerator"),a(initGenerator,"initGenerator");const LENSMCP_MCP_SERVER_ENTRY={command:"npx",args:["-y","lensmcp","mcp"]};function registerWorkspaceMcpConfig(n,t){const s=".mcp.json";let e={};if(n.exists(s))try{e=JSON.parse(n.read(s,"utf-8")??"{}")}catch{e={}}(!e||typeof e!="object")&&(e={}),(!e.mcpServers||typeof e.mcpServers!="object")&&(e.mcpServers={}),!e.mcpServers.lensmcp&&(e.mcpServers.lensmcp={...LENSMCP_MCP_SERVER_ENTRY},n.write(s,JSON.stringify(e,null,2)+`
9
+ `),t.push(".mcp.json: registered lensmcp MCP server (stdio)"))}p(registerWorkspaceMcpConfig,"registerWorkspaceMcpConfig"),a(registerWorkspaceMcpConfig,"registerWorkspaceMcpConfig"),exports.default=initGenerator;function mergeDeep(n,t){const s={...n};for(const[e,i]of Object.entries(t))i!==null&&typeof i=="object"&&!Array.isArray(i)&&s[e]!==null&&typeof s[e]=="object"&&!Array.isArray(s[e])?s[e]=mergeDeep(s[e],i):s[e]=i;return s}p(mergeDeep,"mergeDeep"),a(mergeDeep,"mergeDeep");
@@ -1,281 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupNestGenerator = setupNestGenerator;
4
- exports.findAppModule = findAppModule;
5
- exports.patchMainBootstrap = patchMainBootstrap;
6
- exports.patchAppModule = patchAppModule;
7
- const devkit_1 = require("@nx/devkit");
8
- const BOOTSTRAP_IMPORT = `import { createLensmcpNestApp } from '@lensmcp/nest-instrumentation';`;
9
- // Legacy module-style wiring, kept for the manual-fallback hint.
10
- const IMPORT_LINE = `import { LensmcpModule } from '@lensmcp/nest-instrumentation';`;
11
- const MODULE_CALL = "LensmcpModule.forRoot({ projectName: '__PROJECT__' })";
12
- /**
13
- * Wires LensMCP into a host NestJS project with **no app-code edits**.
14
- * Idempotent.
15
- *
16
- * Zero-config strategy (Phase 8): rewrite the bootstrap in `src/main.ts`
17
- * so `NestFactory.create(AppModule, opts)` becomes
18
- * `createLensmcpNestApp(AppModule, { projectName: '<project>', nestOptions: opts })`.
19
- * `createLensmcpNestApp` wires `LensmcpModule` + the provider tracker +
20
- * auto-instruments every provider's methods under the hood, so the app
21
- * module and the providers stay untouched.
22
- *
23
- * 1. Find the project's `src/main.ts` (the conventional Nest entry).
24
- * 2. Replace the `NestFactory.create(...)` call with
25
- * `createLensmcpNestApp(...)`, threading the original 2nd arg through
26
- * as `nestOptions` and adding the `@lensmcp/nest-instrumentation`
27
- * import (dropping the now-unused `NestFactory` import when nothing
28
- * else uses it). String/AST-lite — if the file doesn't follow the
29
- * canonical shape we print a precise hint and exit non-zero.
30
- * 3. Add an `agent-dev` Nx target.
31
- */
32
- async function setupNestGenerator(tree, rawOptions) {
33
- const options = {
34
- project: rawOptions.project,
35
- skipFormat: rawOptions.skipFormat ?? false,
36
- };
37
- const project = (0, devkit_1.readProjectConfiguration)(tree, options.project);
38
- const mainPath = findMain(tree, project.root);
39
- if (!mainPath) {
40
- throw new Error(`setup-nest: no main.ts found under ${project.root}/src.`);
41
- }
42
- const original = tree.read(mainPath, 'utf-8') ?? '';
43
- const patched = patchMainBootstrap(original, options.project);
44
- if (patched === null) {
45
- throw new Error(`setup-nest: could not safely patch ${mainPath}.\n` +
46
- `Expected a \`NestFactory.create(AppModule)\` bootstrap call. ` +
47
- `Edit manually: replace it with \`createLensmcpNestApp(AppModule, ` +
48
- `{ projectName: '${options.project}' })\` and import it from ` +
49
- `'@lensmcp/nest-instrumentation'.\n` +
50
- `(Or use the module form: add \`${IMPORT_LINE}\` and push ` +
51
- `${MODULE_CALL.replace('__PROJECT__', options.project)} into the ` +
52
- `@Module imports array.)`);
53
- }
54
- if (patched !== original) {
55
- tree.write(mainPath, patched);
56
- }
57
- const targets = { ...(project.targets ?? {}) };
58
- if (!targets['agent-dev']) {
59
- targets['agent-dev'] = {
60
- executor: '@lensmcp/nx-plugin:agent-dev',
61
- options: {
62
- kind: 'nestjs',
63
- },
64
- };
65
- project.targets = targets;
66
- (0, devkit_1.updateProjectConfiguration)(tree, options.project, project);
67
- }
68
- if (!options.skipFormat) {
69
- await (0, devkit_1.formatFiles)(tree);
70
- }
71
- }
72
- exports.default = setupNestGenerator;
73
- // ---------- helpers ----------
74
- function findMain(tree, root) {
75
- for (const name of ['src/main.ts', 'main.ts', 'src/index.ts']) {
76
- const p = (0, devkit_1.joinPathFragments)(root, name);
77
- if (tree.exists(p))
78
- return p;
79
- }
80
- return undefined;
81
- }
82
- function findAppModule(tree, root) {
83
- for (const name of ['src/app.module.ts', 'app.module.ts']) {
84
- const p = (0, devkit_1.joinPathFragments)(root, name);
85
- if (tree.exists(p))
86
- return p;
87
- }
88
- return undefined;
89
- }
90
- const CREATE_CALL_RE = /NestFactory\s*\.\s*create\s*(?:<[\s\S]*?>)?\s*\(/;
91
- /**
92
- * Rewrite a Nest `main.ts` bootstrap to the zero-config form. Returns the
93
- * original string unchanged when already converted (idempotent), `null`
94
- * when no `NestFactory.create(...)` call is present (caller decides), or
95
- * the rewritten source otherwise.
96
- */
97
- function patchMainBootstrap(src, projectName) {
98
- // Already zero-config — nothing to do.
99
- if (/createLensmcpNestApp\s*\(/.test(src))
100
- return src;
101
- const m = CREATE_CALL_RE.exec(src);
102
- if (!m)
103
- return null;
104
- const openParen = m.index + m[0].length - 1; // index of the '('
105
- const closeParen = matchingParen(src, openParen);
106
- if (closeParen === -1)
107
- return null;
108
- const argsStr = src.slice(openParen + 1, closeParen);
109
- const { moduleArg, restArg } = splitTopLevelArgs(argsStr);
110
- if (!moduleArg.trim())
111
- return null;
112
- const proj = projectName.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
113
- const rest = restArg.trim();
114
- const optsObject = rest
115
- ? `{ projectName: '${proj}', nestOptions: ${rest} }`
116
- : `{ projectName: '${proj}' }`;
117
- const replacement = `createLensmcpNestApp(${moduleArg.trim()}, ${optsObject})`;
118
- let out = src.slice(0, m.index) + replacement + src.slice(closeParen + 1);
119
- out = addBootstrapImport(out);
120
- out = dropUnusedNestFactoryImport(out);
121
- return out;
122
- }
123
- /** Insert the `createLensmcpNestApp` import after the last import, unless
124
- * the symbol is already imported from `@lensmcp/nest-instrumentation`. */
125
- function addBootstrapImport(src) {
126
- const already = /createLensmcpNestApp[\s\S]*?from\s*['"]@lensmcp\/nest-instrumentation['"]/.test(src);
127
- if (already)
128
- return src;
129
- const importLines = [...src.matchAll(/^\s*import .+;?\s*$/gm)];
130
- const last = importLines.length ? importLines[importLines.length - 1] : null;
131
- if (last && last.index !== undefined) {
132
- const at = last.index + last[0].length;
133
- return src.slice(0, at) + `\n${BOOTSTRAP_IMPORT}` + src.slice(at);
134
- }
135
- return `${BOOTSTRAP_IMPORT}\n${src}`;
136
- }
137
- /** Strip `NestFactory` from its `@nestjs/core` named import when nothing
138
- * in the file references `NestFactory` any more. */
139
- function dropUnusedNestFactoryImport(src) {
140
- // Reference check excludes the import statement itself.
141
- if (/\bNestFactory\b/.test(stripNestFactoryImportSpan(src).rest))
142
- return src;
143
- const { match } = stripNestFactoryImportSpan(src);
144
- if (!match)
145
- return src;
146
- const names = match.names.filter((n) => n !== 'NestFactory');
147
- if (names.length === 0) {
148
- // Remove the whole import statement (and its trailing newline).
149
- return src.slice(0, match.start) + src.slice(match.end).replace(/^\n/, '');
150
- }
151
- const rebuilt = `import { ${names.join(', ')} } from '@nestjs/core';`;
152
- return src.slice(0, match.start) + rebuilt + src.slice(match.end);
153
- }
154
- /** Locate the `@nestjs/core` named import; return its span + names and the
155
- * source with that span removed (so the caller can test references that
156
- * live *outside* the import). */
157
- function stripNestFactoryImportSpan(src) {
158
- const re = /import\s*\{([^}]*)\}\s*from\s*['"]@nestjs\/core['"];?/;
159
- const m = re.exec(src);
160
- if (!m)
161
- return { match: null, rest: src };
162
- const names = m[1]
163
- .split(',')
164
- .map((s) => s.trim())
165
- .filter(Boolean);
166
- const start = m.index;
167
- const end = m.index + m[0].length;
168
- const rest = src.slice(0, start) + src.slice(end);
169
- return { match: { start, end, names }, rest };
170
- }
171
- /** Index of the `)` matching the `(` at `openIdx`, skipping strings,
172
- * template literals and comments. Returns -1 if unbalanced. */
173
- function matchingParen(src, openIdx) {
174
- let depth = 0;
175
- for (let i = openIdx; i < src.length; i++) {
176
- const skip = skipNonCode(src, i);
177
- if (skip > i) {
178
- i = skip - 1;
179
- continue;
180
- }
181
- const ch = src[i];
182
- if (ch === '(')
183
- depth++;
184
- else if (ch === ')') {
185
- depth--;
186
- if (depth === 0)
187
- return i;
188
- }
189
- }
190
- return -1;
191
- }
192
- /** Split call arguments at the first top-level comma. */
193
- function splitTopLevelArgs(argsStr) {
194
- let depth = 0;
195
- for (let i = 0; i < argsStr.length; i++) {
196
- const skip = skipNonCode(argsStr, i);
197
- if (skip > i) {
198
- i = skip - 1;
199
- continue;
200
- }
201
- const ch = argsStr[i];
202
- if (ch === '(' || ch === '[' || ch === '{')
203
- depth++;
204
- else if (ch === ')' || ch === ']' || ch === '}')
205
- depth--;
206
- else if (ch === ',' && depth === 0) {
207
- return {
208
- moduleArg: argsStr.slice(0, i),
209
- restArg: argsStr.slice(i + 1),
210
- };
211
- }
212
- }
213
- return { moduleArg: argsStr, restArg: '' };
214
- }
215
- /** If position `i` starts a string/template/comment, return the index just
216
- * past it; otherwise return `i`. */
217
- function skipNonCode(src, i) {
218
- const ch = src[i];
219
- if (ch === '"' || ch === "'" || ch === '`') {
220
- for (let j = i + 1; j < src.length; j++) {
221
- if (src[j] === '\\') {
222
- j++;
223
- continue;
224
- }
225
- if (src[j] === ch)
226
- return j + 1;
227
- }
228
- return src.length;
229
- }
230
- if (ch === '/' && src[i + 1] === '/') {
231
- const nl = src.indexOf('\n', i);
232
- return nl === -1 ? src.length : nl;
233
- }
234
- if (ch === '/' && src[i + 1] === '*') {
235
- const end = src.indexOf('*/', i + 2);
236
- return end === -1 ? src.length : end + 2;
237
- }
238
- return i;
239
- }
240
- /**
241
- * Legacy module-style wiring. Adds `LensmcpModule.forRoot(...)` into the
242
- * `@Module({ imports: [...] })` array. Superseded by the `main.ts`
243
- * bootstrap rewrite ({@link patchMainBootstrap}) but kept for hosts whose
244
- * entry file doesn't follow the canonical `NestFactory.create` shape.
245
- * Idempotent. Returns `null` when no `@Module` imports array is found.
246
- */
247
- function patchAppModule(src, projectName) {
248
- const hasImport = src.includes("from '@lensmcp/nest-instrumentation'") ||
249
- src.includes('from "@lensmcp/nest-instrumentation"');
250
- const hasModuleCall = /LensmcpModule\s*\.\s*forRoot\s*\(/.test(src);
251
- if (hasImport && hasModuleCall)
252
- return src;
253
- const importsAnchor = src.indexOf('imports:');
254
- if (importsAnchor === -1)
255
- return null;
256
- const bracketStart = src.indexOf('[', importsAnchor);
257
- if (bracketStart === -1)
258
- return null;
259
- let withImport = src;
260
- if (!hasImport) {
261
- const importLines = [...src.matchAll(/^\s*import .+;?\s*$/gm)];
262
- const lastImport = importLines.length > 0 ? importLines[importLines.length - 1] : null;
263
- if (lastImport && lastImport.index !== undefined) {
264
- const insertAt = lastImport.index + lastImport[0].length;
265
- withImport = src.slice(0, insertAt) + `\n${IMPORT_LINE}` + src.slice(insertAt);
266
- }
267
- else {
268
- withImport = `${IMPORT_LINE}\n` + src;
269
- }
270
- }
271
- if (hasModuleCall)
272
- return withImport;
273
- const adjBracket = withImport.indexOf('[', withImport.indexOf('imports:'));
274
- const before = withImport.slice(0, adjBracket + 1);
275
- const after = withImport.slice(adjBracket + 1);
276
- const trimmedAfter = after.replace(/^\s*/, '');
277
- const startsClosed = trimmedAfter.startsWith(']');
278
- const separator = startsClosed ? '' : ', ';
279
- const call = MODULE_CALL.replace('__PROJECT__', projectName);
280
- return `${before}${call}${separator}${after}`;
281
- }
1
+ "use strict";var h=Object.defineProperty;var c=(e,r)=>h(e,"name",{value:r,configurable:!0});var A=Object.defineProperty,i=c((e,r)=>A(e,"name",{value:r,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.setupNestGenerator=setupNestGenerator,exports.findAppModule=findAppModule,exports.patchMainBootstrap=patchMainBootstrap,exports.patchAppModule=patchAppModule;const devkit_1=require("@nx/devkit"),BOOTSTRAP_IMPORT="import { createLensmcpNestApp } from '@lensmcp/nest-instrumentation';",IMPORT_LINE="import { LensmcpModule } from '@lensmcp/nest-instrumentation';",MODULE_CALL="LensmcpModule.forRoot({ projectName: '__PROJECT__' })";async function setupNestGenerator(e,r){const n={project:r.project,skipFormat:r.skipFormat??!1},t=(0,devkit_1.readProjectConfiguration)(e,n.project),s=findMain(e,t.root);if(!s)throw new Error(`setup-nest: no main.ts found under ${t.root}/src.`);const o=e.read(s,"utf-8")??"",p=patchMainBootstrap(o,n.project);if(p===null)throw new Error(`setup-nest: could not safely patch ${s}.
2
+ Expected a \`NestFactory.create(AppModule)\` bootstrap call. Edit manually: replace it with \`createLensmcpNestApp(AppModule, { projectName: '${n.project}' })\` and import it from '@lensmcp/nest-instrumentation'.
3
+ (Or use the module form: add \`${IMPORT_LINE}\` and push ${MODULE_CALL.replace("__PROJECT__",n.project)} into the @Module imports array.)`);p!==o&&e.write(s,p);const a={...t.targets??{}};a["agent-dev"]||(a["agent-dev"]={executor:"@lensmcp/nx-plugin:agent-dev",options:{kind:"nestjs"}},t.targets=a,(0,devkit_1.updateProjectConfiguration)(e,n.project,t)),n.skipFormat||await(0,devkit_1.formatFiles)(e)}c(setupNestGenerator,"setupNestGenerator"),i(setupNestGenerator,"setupNestGenerator"),exports.default=setupNestGenerator;function findMain(e,r){for(const n of["src/main.ts","main.ts","src/index.ts"]){const t=(0,devkit_1.joinPathFragments)(r,n);if(e.exists(t))return t}}c(findMain,"findMain"),i(findMain,"findMain");function findAppModule(e,r){for(const n of["src/app.module.ts","app.module.ts"]){const t=(0,devkit_1.joinPathFragments)(r,n);if(e.exists(t))return t}}c(findAppModule,"findAppModule"),i(findAppModule,"findAppModule");const CREATE_CALL_RE=/NestFactory\s*\.\s*create\s*(?:<[\s\S]*?>)?\s*\(/;function patchMainBootstrap(e,r){if(/createLensmcpNestApp\s*\(/.test(e))return e;const n=CREATE_CALL_RE.exec(e);if(!n)return null;const t=n.index+n[0].length-1,s=matchingParen(e,t);if(s===-1)return null;const o=e.slice(t+1,s),{moduleArg:p,restArg:a}=splitTopLevelArgs(o);if(!p.trim())return null;const u=r.replace(/\\/g,"\\\\").replace(/'/g,"\\'"),m=a.trim(),d=m?`{ projectName: '${u}', nestOptions: ${m} }`:`{ projectName: '${u}' }`,f=`createLensmcpNestApp(${p.trim()}, ${d})`;let l=e.slice(0,n.index)+f+e.slice(s+1);return l=addBootstrapImport(l),l=dropUnusedNestFactoryImport(l),l}c(patchMainBootstrap,"patchMainBootstrap"),i(patchMainBootstrap,"patchMainBootstrap");function addBootstrapImport(e){if(/createLensmcpNestApp[\s\S]*?from\s*['"]@lensmcp\/nest-instrumentation['"]/.test(e))return e;const r=[...e.matchAll(/^\s*import .+;?\s*$/gm)],n=r.length?r[r.length-1]:null;if(n&&n.index!==void 0){const t=n.index+n[0].length;return e.slice(0,t)+`
4
+ ${BOOTSTRAP_IMPORT}`+e.slice(t)}return`${BOOTSTRAP_IMPORT}
5
+ ${e}`}c(addBootstrapImport,"addBootstrapImport"),i(addBootstrapImport,"addBootstrapImport");function dropUnusedNestFactoryImport(e){if(/\bNestFactory\b/.test(stripNestFactoryImportSpan(e).rest))return e;const{match:r}=stripNestFactoryImportSpan(e);if(!r)return e;const n=r.names.filter(s=>s!=="NestFactory");if(n.length===0)return e.slice(0,r.start)+e.slice(r.end).replace(/^\n/,"");const t=`import { ${n.join(", ")} } from '@nestjs/core';`;return e.slice(0,r.start)+t+e.slice(r.end)}c(dropUnusedNestFactoryImport,"dropUnusedNestFactoryImport"),i(dropUnusedNestFactoryImport,"dropUnusedNestFactoryImport");function stripNestFactoryImportSpan(e){const r=/import\s*\{([^}]*)\}\s*from\s*['"]@nestjs\/core['"];?/.exec(e);if(!r)return{match:null,rest:e};const n=r[1].split(",").map(p=>p.trim()).filter(Boolean),t=r.index,s=r.index+r[0].length,o=e.slice(0,t)+e.slice(s);return{match:{start:t,end:s,names:n},rest:o}}c(stripNestFactoryImportSpan,"stripNestFactoryImportSpan"),i(stripNestFactoryImportSpan,"stripNestFactoryImportSpan");function matchingParen(e,r){let n=0;for(let t=r;t<e.length;t++){const s=skipNonCode(e,t);if(s>t){t=s-1;continue}const o=e[t];if(o==="(")n++;else if(o===")"&&(n--,n===0))return t}return-1}c(matchingParen,"matchingParen"),i(matchingParen,"matchingParen");function splitTopLevelArgs(e){let r=0;for(let n=0;n<e.length;n++){const t=skipNonCode(e,n);if(t>n){n=t-1;continue}const s=e[n];if(s==="("||s==="["||s==="{")r++;else if(s===")"||s==="]"||s==="}")r--;else if(s===","&&r===0)return{moduleArg:e.slice(0,n),restArg:e.slice(n+1)}}return{moduleArg:e,restArg:""}}c(splitTopLevelArgs,"splitTopLevelArgs"),i(splitTopLevelArgs,"splitTopLevelArgs");function skipNonCode(e,r){const n=e[r];if(n==='"'||n==="'"||n==="`"){for(let t=r+1;t<e.length;t++){if(e[t]==="\\"){t++;continue}if(e[t]===n)return t+1}return e.length}if(n==="/"&&e[r+1]==="/"){const t=e.indexOf(`
6
+ `,r);return t===-1?e.length:t}if(n==="/"&&e[r+1]==="*"){const t=e.indexOf("*/",r+2);return t===-1?e.length:t+2}return r}c(skipNonCode,"skipNonCode"),i(skipNonCode,"skipNonCode");function patchAppModule(e,r){const n=e.includes("from '@lensmcp/nest-instrumentation'")||e.includes('from "@lensmcp/nest-instrumentation"'),t=/LensmcpModule\s*\.\s*forRoot\s*\(/.test(e);if(n&&t)return e;const s=e.indexOf("imports:");if(s===-1||e.indexOf("[",s)===-1)return null;let o=e;if(!n){const f=[...e.matchAll(/^\s*import .+;?\s*$/gm)],l=f.length>0?f[f.length-1]:null;if(l&&l.index!==void 0){const g=l.index+l[0].length;o=e.slice(0,g)+`
7
+ ${IMPORT_LINE}`+e.slice(g)}else o=`${IMPORT_LINE}
8
+ `+e}if(t)return o;const p=o.indexOf("[",o.indexOf("imports:")),a=o.slice(0,p+1),u=o.slice(p+1),m=u.replace(/^\s*/,"").startsWith("]")?"":", ",d=MODULE_CALL.replace("__PROJECT__",r);return`${a}${d}${m}${u}`}c(patchAppModule,"patchAppModule"),i(patchAppModule,"patchAppModule");