@tng-sh/js 0.2.1 → 0.2.4

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/bin/tng.js CHANGED
@@ -28,7 +28,7 @@ process.on('uncaughtException', (err) => {
28
28
  program
29
29
  .name('tng')
30
30
  .description('TNG - Advanced Code Audit, Test Generation, Visualization, Clone Detection, and Dead Code Analysis for JavaScript/TypeScript')
31
- .version('0.2.1');
31
+ .version('0.2.4');
32
32
 
33
33
  /**
34
34
  * Copy text to system clipboard
@@ -256,139 +256,172 @@ program
256
256
  .option('--all', 'Run dead code detection across the entire repo (respects .gitignore)')
257
257
  .option('--xray', 'Generate X-Ray visualization (Mermaid flowchart)')
258
258
  .option('--impact', 'Run impact analysis (blast radius check) on a method')
259
+ .option('--callsites', 'Find in-repo call sites for a method')
260
+ .option('--class <name>', 'Class/module name to disambiguate methods when multiple classes define the same method in a file')
259
261
  .option('--json', 'Output results as JSON events (machine-readable)')
260
262
 
261
263
  .action(async (options) => {
262
- if (options.method && options.file) {
263
- if (options.trace) {
264
- const { getSymbolicTrace } = require('../index');
265
- const GoUISession = require('../lib/goUiSession');
266
-
267
- try {
268
- const traceJson = getSymbolicTrace(
269
- path.resolve(options.file),
270
- options.method,
271
- null
272
- );
273
-
274
- if (options.json) {
275
- console.log(traceJson);
276
- return;
277
- }
278
-
279
- // Create temp file for the trace
280
- const tmpDir = require('os').tmpdir();
281
- const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
282
- fs.writeFileSync(tmpFile, traceJson);
264
+ if (options.callsites) {
265
+ if (!options.method) {
266
+ console.log(chalk.red('Error: --method is required for call site analysis.'));
267
+ process.exit(1);
268
+ }
283
269
 
284
- // Launch Go UI
285
- const session = new GoUISession();
286
- const binaryPath = session._binaryPath;
270
+ try {
271
+ const { findCallSites } = require('../index');
272
+ const projectRoot = process.cwd();
273
+ const resultJson = findCallSites(projectRoot, options.method);
287
274
 
288
- const { spawnSync } = require('child_process');
289
- spawnSync(binaryPath, ['js-trace-results', '--file', tmpFile], {
290
- stdio: 'inherit'
291
- });
275
+ if (options.json) {
276
+ console.log(resultJson);
277
+ return;
278
+ }
292
279
 
293
- // Cleanup
294
- try { fs.unlinkSync(tmpFile); } catch (e) { }
280
+ const parsed = JSON.parse(resultJson);
281
+ const sites = Array.isArray(parsed) ? parsed : (parsed.call_sites || parsed.sites || parsed);
282
+ if (!sites || sites.length === 0) {
283
+ console.log(chalk.green('\nNo call sites found in this project. You might be safe!'));
284
+ return;
285
+ }
295
286
 
296
- } catch (e) {
297
- console.log(chalk.red(`Trace failed: ${e.message}`));
298
- process.exit(1);
287
+ console.log(chalk.blue(`\nFound ${sites.length} call sites:\n`));
288
+ sites.slice(0, 200).forEach((site) => {
289
+ const file = site.file || 'unknown';
290
+ const line = site.line || 0;
291
+ const content = site.content || '';
292
+ console.log(`${chalk.yellow(file)}:${chalk.cyan(line)} ${content}`);
293
+ });
294
+ if (sites.length > 200) {
295
+ console.log(chalk.dim(`\n... ${sites.length - 200} more`));
299
296
  }
300
297
  return;
298
+ } catch (e) {
299
+ console.log(chalk.red(`Error: ${e.message}`));
300
+ process.exit(1);
301
301
  }
302
+ }
303
+ // 1. Symbolic Trace
304
+ if (options.method && options.file && options.trace) {
305
+ const { getSymbolicTrace } = require('../index');
306
+ const GoUISession = require('../lib/goUiSession');
302
307
 
303
- if (options.xray) {
304
- const config = loadConfig();
305
- const { generateTest: nativeGenerateTest } = require('../index');
308
+ try {
309
+ const traceJson = getSymbolicTrace(
310
+ path.resolve(options.file),
311
+ options.method,
312
+ options.class || null
313
+ );
306
314
 
307
- try {
308
- const resultJson = nativeGenerateTest(
309
- path.resolve(options.file),
310
- options.method,
311
- null,
312
- 'visualize',
313
- JSON.stringify(config),
314
- (msg, percent) => {
315
- if (options.json) return;
316
- if (percent % 20 === 0) console.log(chalk.dim(`[${percent}%] ${msg}`));
317
- }
318
- );
319
-
320
- if (options.json) {
321
- console.log(resultJson);
322
- return;
323
- }
315
+ if (options.json) {
316
+ console.log(traceJson);
317
+ return;
318
+ }
319
+
320
+ // Create temp file for the trace
321
+ const tmpDir = require('os').tmpdir();
322
+ const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
323
+ fs.writeFileSync(tmpFile, traceJson);
324
+
325
+ // Launch Go UI
326
+ const session = new GoUISession();
327
+ const binaryPath = session._binaryPath;
328
+
329
+ const { spawnSync } = require('child_process');
330
+ spawnSync(binaryPath, ['js-trace-results', '--file', tmpFile], {
331
+ stdio: 'inherit'
332
+ });
333
+
334
+ // Cleanup
335
+ try { fs.unlinkSync(tmpFile); } catch (e) { }
336
+
337
+ } catch (e) {
338
+ console.log(chalk.red(`Trace failed: ${e.message}`));
339
+ process.exit(1);
340
+ }
341
+ return;
342
+ }
324
343
 
325
- // For global --xray, we just launch the Premium UI directly if not JSON
326
- const GoUISession = require('../lib/goUiSession');
327
- const session = new GoUISession();
328
- let mermaidCode = "";
329
- let explanation = "";
330
- try {
331
- const parsed = JSON.parse(resultJson);
332
- mermaidCode = parsed.mermaid_code || resultJson;
333
- explanation = parsed.explanation || "";
334
- } catch (e) {
335
- mermaidCode = resultJson;
344
+ // 2. X-Ray
345
+ if (options.method && options.file && options.xray) {
346
+ const config = loadConfig();
347
+ const { generateTest: nativeGenerateTest } = require('../index');
348
+
349
+ try {
350
+ const resultJson = nativeGenerateTest(
351
+ path.resolve(options.file),
352
+ options.method,
353
+ options.class || null,
354
+ 'visualize',
355
+ JSON.stringify(config),
356
+ (msg, percent) => {
357
+ if (options.json) return;
358
+ if (percent % 20 === 0) console.log(chalk.dim(`[${percent}%] ${msg}`));
336
359
  }
360
+ );
337
361
 
338
- // Copy to clipboard
339
- copyToClipboard(mermaidCode);
362
+ if (options.json) {
363
+ console.log(resultJson);
364
+ return;
365
+ }
340
366
 
341
- await session.showXrayResults(options.method, '', mermaidCode, explanation);
367
+ const GoUISession = require('../lib/goUiSession');
368
+ const session = new GoUISession();
369
+ let mermaidCode = "";
370
+ let explanation = "";
371
+ try {
372
+ const parsed = JSON.parse(resultJson);
373
+ mermaidCode = parsed.mermaid_code || resultJson;
374
+ explanation = parsed.explanation || "";
342
375
  } catch (e) {
343
- console.log(chalk.red(`X-Ray failed: ${e.message}`));
344
- process.exit(1);
376
+ mermaidCode = resultJson;
345
377
  }
346
- return;
378
+
379
+ copyToClipboard(mermaidCode);
380
+ await session.showXrayResults(options.method, '', mermaidCode, explanation);
381
+ } catch (e) {
382
+ console.log(chalk.red(`X-Ray failed: ${e.message}`));
383
+ process.exit(1);
347
384
  }
385
+ return;
386
+ }
348
387
 
388
+ // 3. Impact Analysis
389
+ if (options.method && options.file && options.impact) {
390
+ await runImpact(options.file, options.method, options.json, options.class || null);
391
+ return;
392
+ }
349
393
 
350
- if (options.impact) {
351
- await runImpact(options.file, options.method, options.json);
352
- return;
353
- }
394
+ // 4. Clone Detection
395
+ if (options.file && options.clones) {
396
+ await runClones(options.file, options.level || 'all', options.json);
397
+ return;
398
+ }
354
399
 
355
- if (!options.type && !options.audit) {
356
- console.log(chalk.red('Error: --type <type> is required.'));
357
- process.exit(1);
358
- }
359
- generateTest(options.file, options.method, options.type, options.audit, options.json);
360
- } else if (options.file && !options.method) {
361
- if (options.clones) {
362
- await runClones(options.file, options.level || 'all', options.json);
363
- } else if (options.deadcode) {
364
- if (options.all) {
365
- await runDeadCodeRepo(options.json);
366
- } else {
367
- await runDeadCode(options.file, options.json);
368
- }
400
+ // 5. Dead Code Analysis
401
+ if (options.deadcode) {
402
+ if (options.all) {
403
+ await runDeadCodeRepo(options.json);
404
+ } else if (options.file) {
405
+ await runDeadCode(options.file, options.json);
369
406
  } else {
370
- console.log(chalk.yellow('Specify a method with -m, use --outline to see methods, or run "tng i" for full selection.'));
371
- }
372
- if (options.xray) {
373
- // ... (existing xray logic) ...
374
- return;
375
- }
376
-
377
- if (options.impact) {
378
- await runImpact(options.file, options.method, options.json);
379
- return;
407
+ console.log(chalk.red('Error: --file <path> or --all is required for deadcode analysis.'));
408
+ process.exit(1);
380
409
  }
410
+ return;
411
+ }
381
412
 
413
+ // 6. Test Generation / Audit (Method + File required)
414
+ if (options.method && options.file) {
382
415
  if (!options.type && !options.audit) {
383
416
  console.log(chalk.red('Error: --type <type> is required.'));
384
417
  process.exit(1);
385
418
  }
386
- generateTest(options.file, options.method, options.type, options.audit, options.json);
387
- } else if (options.deadcode && options.all && !options.file && !options.method) {
388
- await runDeadCodeRepo(options.json);
389
- } else if (options.file && !options.method) {
390
- launchInteractive();
419
+ generateTest(options.file, options.method, options.type, options.audit, options.json, options.class || null);
420
+ return;
391
421
  }
422
+
423
+ // 7. Interactive Fallback
424
+ launchInteractive();
392
425
  });
393
426
 
394
427
  /**
@@ -674,7 +707,7 @@ program
674
707
  /**
675
708
  * Logic to generate test or run audit (delegates to native binary)
676
709
  */
677
- async function generateTest(filePath, methodName, testType, auditMode = false, jsonMode = false) {
710
+ async function generateTest(filePath, methodName, testType, auditMode = false, jsonMode = false, className = null) {
678
711
  const config = loadConfig();
679
712
 
680
713
  // Initialize JSON session if requested
@@ -744,7 +777,7 @@ async function generateTest(filePath, methodName, testType, auditMode = false, j
744
777
  resultJson = runAudit(
745
778
  absolutePath,
746
779
  methodName,
747
- null, // class_name
780
+ className,
748
781
  testType || null,
749
782
  JSON.stringify(config),
750
783
  callback
@@ -753,7 +786,7 @@ async function generateTest(filePath, methodName, testType, auditMode = false, j
753
786
  resultJson = nativeGenerateTest(
754
787
  absolutePath,
755
788
  methodName,
756
- null, // class_name
789
+ className,
757
790
  testType || null,
758
791
  JSON.stringify(config),
759
792
  callback
@@ -824,7 +857,7 @@ program.on('--help', () => {
824
857
  /**
825
858
  * Logic to run impact analysis
826
859
  */
827
- async function runImpact(filePath, methodName, jsonMode = false) {
860
+ async function runImpact(filePath, methodName, jsonMode = false, className = null) {
828
861
  const { analyzeImpact } = require('../index');
829
862
  const absolutePath = path.resolve(filePath);
830
863
  const projectRoot = process.cwd();
@@ -836,7 +869,7 @@ async function runImpact(filePath, methodName, jsonMode = false) {
836
869
 
837
870
  if (jsonMode) {
838
871
  try {
839
- const resultJson = analyzeImpact(projectRoot, absolutePath, methodName);
872
+ const resultJson = analyzeImpact(projectRoot, absolutePath, methodName, className);
840
873
  console.log(resultJson);
841
874
  } catch (e) {
842
875
  console.error(JSON.stringify({ error: e.message }));
@@ -848,7 +881,7 @@ async function runImpact(filePath, methodName, jsonMode = false) {
848
881
  console.log(chalk.blue(`💥 Analyzing impact for ${methodName} in ${filePath}...`));
849
882
 
850
883
  try {
851
- const resultJson = analyzeImpact(projectRoot, absolutePath, methodName);
884
+ const resultJson = analyzeImpact(projectRoot, absolutePath, methodName, className);
852
885
  const result = JSON.parse(resultJson);
853
886
 
854
887
  if (result.status === 'error') {
Binary file
Binary file
Binary file
Binary file
package/index.d.ts CHANGED
@@ -69,4 +69,4 @@ export declare function listDeadcodeFiles(projectRoot: string): string
69
69
  * Analyzes the impact of changes in a specific method compared to git HEAD.
70
70
  * Returns a JSON string containing breaking changes and impacted files.
71
71
  */
72
- export declare function analyzeImpact(projectRoot: string, filePath: string, methodName: string): string
72
+ export declare function analyzeImpact(projectRoot: string, filePath: string, methodName: string, className?: string | undefined | null): string
@@ -6,7 +6,6 @@ const GoUISession = require('./goUiSession');
6
6
  const { getFileOutline, generateTest, ping, getUserStats } = require('../index');
7
7
  const { loadConfig } = require('./config');
8
8
  const { saveTestFile } = require('./saveFile');
9
- const { applyFix } = require('./fixApplier');
10
9
 
11
10
  class GenerateTestsUI {
12
11
  constructor(cliMode = false) {
@@ -127,7 +126,7 @@ class GenerateTestsUI {
127
126
 
128
127
  let title = 'Select JavaScript File';
129
128
  if (isAudit) title = 'Select JavaScript File to Audit';
130
- else if (isImpact) title = 'Select JavaScript File to Impact Audit';
129
+ else if (isImpact) title = 'Select JavaScript File for Regression Check';
131
130
  else if (isXray) title = 'Select File for X-Ray';
132
131
  else if (isTrace) title = 'Select File for Symbolic Trace';
133
132
 
@@ -160,14 +159,14 @@ class GenerateTestsUI {
160
159
 
161
160
  const fileName = path.basename(filePath);
162
161
  const items = methods.map(m => ({
163
- name: m.class_name ? `${m.class_name}.${m.name}` : m.name,
162
+ name: m.class_name ? `${m.class_name}#${m.name}` : m.name,
164
163
  path: `Function in ${fileName}`,
165
164
  methodData: m
166
165
  }));
167
166
 
168
167
  let title = 'Select Method';
169
168
  if (isAudit) title = `Select Method to Audit for ${fileName}`;
170
- else if (isImpact) title = `Select Method to Impact Audit for ${fileName}`;
169
+ else if (isImpact) title = `Select Method for Regression Check for ${fileName}`;
171
170
  else if (isXray) title = `Select Method to X-Ray for ${fileName}`;
172
171
  else if (isTrace) title = `Select Method to Trace for ${fileName}`;
173
172
 
@@ -179,7 +178,7 @@ class GenerateTestsUI {
179
178
 
180
179
  if (selectedMethod) {
181
180
  if (isTrace) {
182
- await this._launchTrace(filePath, selectedMethod.name);
181
+ await this._launchTrace(filePath, selectedMethod.name, selectedMethod.class_name || null);
183
182
  return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
184
183
  }
185
184
 
@@ -259,7 +258,7 @@ class GenerateTestsUI {
259
258
  // Artificial delay for UX
260
259
  await new Promise(r => setTimeout(r, 300));
261
260
 
262
- const json = analyzeImpact(projectRoot, filePath, method.name);
261
+ const json = analyzeImpact(projectRoot, filePath, method.name, method.class_name || null);
263
262
  progress.update('Comparing logic...', { percent: 80 });
264
263
 
265
264
  return json;
@@ -272,7 +271,7 @@ class GenerateTestsUI {
272
271
  }
273
272
 
274
273
  await this.goUiSession.showImpactResults(impactResult);
275
- return { message: 'Impact audit complete' };
274
+ return { message: 'Regression check complete' };
276
275
  }
277
276
 
278
277
  async _handleAuditFlow(filePath, method, testType) {
@@ -600,7 +599,7 @@ class GenerateTestsUI {
600
599
  }
601
600
  }
602
601
 
603
- async _launchTrace(filePath, methodName) {
602
+ async _launchTrace(filePath, methodName, className) {
604
603
  const { getSymbolicTrace } = require('../index');
605
604
  const fs = require('fs');
606
605
  const path = require('path');
@@ -610,7 +609,7 @@ class GenerateTestsUI {
610
609
  // 1. Generate Trace (with Spinner)
611
610
  const result = this.goUiSession.showSpinner(`Tracing ${methodName}...`, () => {
612
611
  try {
613
- const traceJson = getSymbolicTrace(filePath, methodName, null);
612
+ const traceJson = getSymbolicTrace(filePath, methodName, className || null);
614
613
  const tmpDir = require('os').tmpdir();
615
614
  const f = path.join(tmpDir, `trace-${Date.now()}.json`);
616
615
  fs.writeFileSync(f, traceJson);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tng-sh/js",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "TNG JavaScript CLI",
5
5
  "repository": {
6
6
  "type": "git",
Binary file
Binary file