🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@dean0x/showme

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dean0x/showme - npm Package Compare versions

Comparing version
0.1.8
to
0.2.0
+1
-1
dist/cli.d.ts

@@ -5,4 +5,4 @@ #!/usr/bin/env node

* Usage: showme file <path> [--line <number>]
* showme diff [--base <ref>] [--target <ref>] [--files <files...>]
* showme diff [--files <files...>]
*/
export {};

@@ -5,3 +5,3 @@ #!/usr/bin/env node

* Usage: showme file <path> [--line <number>]
* showme diff [--base <ref>] [--target <ref>] [--files <files...>]
* showme diff [--files <files...>]
*/

@@ -39,25 +39,2 @@ import { ShowFileHandler } from "./handlers/show-file-handler.js";

}
else if (arg === '--base' || arg === '-b') {
const next = args[i + 1];
if (next && !next.startsWith('-')) {
parsed.base = next;
i++;
}
}
else if (arg === '--target' || arg === '-t') {
const next = args[i + 1];
if (next && !next.startsWith('-')) {
parsed.target = next;
i++;
}
}
else if (arg === '--staged' || arg === '-s') {
parsed.staged = true;
}
else if (arg === '--unstaged' || arg === '-u') {
parsed.unstaged = true;
}
else if (arg === '--reuse-window' || arg === '-r') {
parsed.reuseWindow = true;
}
else if (arg === '--files' || arg === '-f') {

@@ -114,19 +91,9 @@ parsed.files = [];

-l, --line <number> Line number to highlight and jump to (single file only)
-r, --reuse-window Open in current VS Code window instead of new window
Diff command:
showme diff Show working directory changes
showme diff -b main Show diff from main branch
showme diff -b HEAD~1 -t HEAD Show diff between commits
showme diff Show all changes since last commit
showme diff -f src/index.ts Show diff for specific files
showme diff --staged Show only staged changes
showme diff --unstaged Show only unstaged changes
Options:
-b, --base <ref> Base commit, branch, or tag
-t, --target <ref> Target commit, branch, or working directory
-f, --files <files...> Specific files to include in diff
-s, --staged Show only staged changes
-u, --unstaged Show only unstaged changes
-r, --reuse-window Open in current VS Code window instead of new window

@@ -156,14 +123,18 @@ Global options:

const handler = ShowFileHandler.create(logger);
// Determine if we're handling single or multiple files
const isMultiple = args.paths && args.paths.length > 1;
const result = await handler.handleFileRequest(isMultiple
? {
paths: args.paths,
reuseWindow: args.reuseWindow
}
: {
path: args.path || (args.paths && args.paths[0]),
line_highlight: args.line,
reuseWindow: args.reuseWindow
});
// Collect files to open
const files = [];
if (args.paths && args.paths.length > 0) {
files.push(...args.paths);
}
else if (args.path) {
files.push(args.path);
}
if (files.length === 0) {
console.error('Error: No files specified');
process.exit(1);
}
const result = await handler.handleFileRequest({
files,
line: args.line
});
console.log(result.content[0].text);

@@ -174,8 +145,3 @@ }

const result = await handler.handleDiffRequest({
base: args.base,
target: args.target,
files: args.files,
staged: args.staged,
unstaged: args.unstaged,
reuseWindow: args.reuseWindow
files: args.files
});

@@ -182,0 +148,0 @@ console.log(result.content[0].text);

@@ -14,8 +14,3 @@ /**

export interface ShowDiffRequest {
base?: string;
target?: string;
files?: string[];
staged?: boolean;
unstaged?: boolean;
reuseWindow?: boolean;
}

@@ -56,3 +51,3 @@ /**

/**
* Generate git diff based on parameters
* Generate git diff comparing HEAD to working directory
*/

@@ -59,0 +54,0 @@ private generateDiff;

@@ -44,5 +44,5 @@ /**

const startTime = performance.now();
// Create executor with appropriate config for this request
// Always reuse window for diffs
this.vsCodeExecutor = createVSCodeExecutor({
reuseWindow: args.reuseWindow || false
reuseWindow: true
}, this.logger);

@@ -74,64 +74,26 @@ const result = await pipe(this.detectRepository.bind(this), this.generateDiff.bind(this), this.openDiffInVSCode.bind(this))({ ...args, workingPath: process.cwd() });

}
const result = {
workingPath: args.workingPath,
repository: detectionResult.value
};
if (args.base !== undefined)
result.base = args.base;
if (args.target !== undefined)
result.target = args.target;
if (args.files !== undefined)
result.files = args.files;
if (args.staged !== undefined)
result.staged = args.staged;
if (args.unstaged !== undefined)
result.unstaged = args.unstaged;
return {
ok: true,
value: result
value: {
workingPath: args.workingPath,
repository: detectionResult.value,
...(args.files && { files: args.files })
}
};
}
/**
* Generate git diff based on parameters
* Generate git diff comparing HEAD to working directory
*/
async generateDiff(data) {
try {
let diffResult;
let diffType;
// Handle staged/unstaged flags
if (data.staged && !data.base && !data.target) {
// Show only staged changes
diffResult = await this.gitDiffGenerator.getStagedDiff(data.workingPath, data.files);
diffType = 'staged changes';
}
else if (data.unstaged && !data.base && !data.target) {
// Show only unstaged changes
diffResult = await this.gitDiffGenerator.getUnstagedDiff(data.workingPath, data.files);
diffType = 'unstaged changes';
}
else if (data.base && data.target) {
// Commit range diff
diffResult = await this.gitDiffGenerator.generateDiff(data.workingPath, {
type: 'commit-range',
base: data.base,
target: data.target,
paths: data.files
});
diffType = `${data.base}..${data.target}`;
}
else if (data.base) {
// Base to working directory
diffResult = await this.gitDiffGenerator.getBranchDiff(data.workingPath, data.base, data.files);
diffType = `${data.base}..working`;
}
else {
// Default: Working directory changes (both staged and unstaged)
// This shows all changes when no flags are specified
diffResult = await this.gitDiffGenerator.getUnstagedDiff(data.workingPath, data.files);
diffType = 'working changes';
}
// Always compare HEAD to working directory (shows all changes)
const diffResult = await this.gitDiffGenerator.generateDiff(data.workingPath, {
type: 'branch',
target: 'HEAD',
paths: data.files
});
if (!diffResult.ok) {
return {
ok: false,
error: new ShowDiffError(`Git diff generation failed: ${diffResult.error.message}`, diffResult.error.code, { diffType, files: data.files })
error: new ShowDiffError(`Git diff generation failed: ${diffResult.error.message}`, diffResult.error.code, { diffType: 'HEAD vs working', files: data.files })
};

@@ -144,3 +106,3 @@ }

diffResult: diffResult.value,
diffType
diffType: 'HEAD vs working'
}

@@ -153,3 +115,3 @@ };

ok: false,
error: new ShowDiffError(`Diff generation error: ${err.message}`, 'DIFF_GENERATION_ERROR', { base: data.base, target: data.target, files: data.files })
error: new ShowDiffError(`Diff generation error: ${err.message}`, 'DIFF_GENERATION_ERROR', { files: data.files })
};

@@ -177,45 +139,18 @@ }

const isFirstOperation = i === 0;
if (data.staged) {
// For staged changes: compare HEAD to staged (index) version
const headTempResult = await this.tempManager.createGitTempFile('HEAD', filepath);
if (!headTempResult.ok) {
this.logger.warn(`Skipping staged diff for ${filepath}: ${headTempResult.error.message}`);
continue;
}
tempFiles.push(headTempResult.value);
// Get staged version from index (empty ref means use index)
const stagedTempResult = await this.tempManager.createGitTempFile('', filepath);
if (!stagedTempResult.ok) {
this.logger.warn(`Skipping staged diff for ${filepath}: no staged version`);
continue;
}
tempFiles.push(stagedTempResult.value);
const diffResult = await this.vsCodeExecutor.openDiff(headTempResult.value.filepath, stagedTempResult.value.filepath, `${filepath} (${data.diffType})`, isFirstOperation);
if (diffResult.ok) {
lastCommand = diffResult.value.command;
}
else {
allSuccess = false;
this.logger.warn(`Failed to open staged diff for ${filepath}: ${diffResult.error.message}`);
}
// Always compare HEAD version to working copy
const headTempResult = await this.tempManager.createGitTempFile('HEAD', filepath);
if (!headTempResult.ok) {
this.logger.warn(`Skipping diff for ${filepath}: ${headTempResult.error.message}`);
continue;
}
tempFiles.push(headTempResult.value);
// Use current working version as new file
const currentPath = `${data.workingPath}/${filepath}`;
const diffResult = await this.vsCodeExecutor.openDiff(headTempResult.value.filepath, currentPath, `${filepath} (changes since last commit)`, isFirstOperation);
if (diffResult.ok) {
lastCommand = diffResult.value.command;
}
else {
// For unstaged or regular diffs: compare old version to working copy
const oldRef = data.base || 'HEAD';
const oldTempResult = await this.tempManager.createGitTempFile(oldRef, filepath);
if (!oldTempResult.ok) {
this.logger.warn(`Skipping diff for ${filepath}: ${oldTempResult.error.message}`);
continue;
}
tempFiles.push(oldTempResult.value);
// Use current working version as new file
const currentPath = `${data.workingPath}/${filepath}`;
const diffResult = await this.vsCodeExecutor.openDiff(oldTempResult.value.filepath, currentPath, `${filepath} (${data.diffType})`, isFirstOperation);
if (diffResult.ok) {
lastCommand = diffResult.value.command;
}
else {
allSuccess = false;
this.logger.warn(`Failed to open diff for ${filepath}: ${diffResult.error.message}`);
}
allSuccess = false;
this.logger.warn(`Failed to open diff for ${filepath}: ${diffResult.error.message}`);
}

@@ -265,10 +200,5 @@ }

success: vsCodeResult.value.success,
tempFiles
tempFiles,
...(data.files && { files: data.files })
};
if (data.base !== undefined)
result.base = data.base;
if (data.target !== undefined)
result.target = data.target;
if (data.files !== undefined)
result.files = data.files;
// No cleanup needed - temp files use deterministic names and get overwritten

@@ -302,3 +232,3 @@ return {

type: 'text',
text: `Opened ${data.files.length} diff tabs in VS Code: ${data.diffType}${changesText}`
text: `Opened ${data.files.length} diff tabs in VS Code (changes since last commit)${changesText}`
}

@@ -320,3 +250,3 @@ ]

type: 'text',
text: `Git diff opened in VS Code: ${data.diffType}${filesText}${changesText}`
text: `Git diff opened in VS Code (changes since last commit)${filesText}${changesText}`
}

@@ -323,0 +253,0 @@ ]

@@ -12,6 +12,4 @@ /**

export interface ShowFileRequest {
path?: string;
paths?: string[];
line_highlight?: number;
reuseWindow?: boolean;
files?: string[];
line?: number;
}

@@ -42,22 +40,10 @@ /**

/**
* Handle ShowFile request using pipe composition
* Handle ShowFile request
*/
handleFileRequest(args: ShowFileRequest): Promise<MCPResponse>;
/**
* Handle single file request
* Handle files request
*/
private handleSingleFile;
private handleFiles;
/**
* Handle multiple files request
*/
private handleMultipleFiles;
/**
* Validate file path
*/
private validatePath;
/**
* Open file in VS Code
*/
private openInVSCode;
/**
* Format success response

@@ -64,0 +50,0 @@ */

@@ -7,3 +7,2 @@ /**

import { createVSCodeExecutor } from '../utils/vscode-executor.js';
import { pipe } from '../utils/pipe.js';
import { ConsoleLogger } from '../utils/logger.js';

@@ -33,20 +32,20 @@ import { ShowFileError } from '../utils/error-handling.js';

/**
* Handle ShowFile request using pipe composition
* Handle ShowFile request
*/
async handleFileRequest(args) {
const startTime = performance.now();
// Create executor with appropriate config for this request
// Always reuse window for files
this.vsCodeExecutor = createVSCodeExecutor({
reuseWindow: args.reuseWindow || false
reuseWindow: true
}, this.logger);
// Determine if handling single or multiple files
const isMultiple = !!args.paths && args.paths.length > 0;
const result = isMultiple
? await this.handleMultipleFiles(args)
: await this.handleSingleFile(args);
// Validate we have files to open
if (!args.files || args.files.length === 0) {
return this.formatErrorResponse(new ShowFileError('No files specified', 'MISSING_FILES', {}));
}
const result = await this.handleFiles(args);
const duration = performance.now() - startTime;
this.logger.info('ShowFile request completed', {
path: args.path,
paths: args.paths,
fileCount: args.paths?.length || 1,
files: args.files,
fileCount: args.files.length,
line: args.line,
success: result.ok,

@@ -63,31 +62,19 @@ duration: Math.round(duration)

/**
* Handle single file request
* Handle files request
*/
async handleSingleFile(args) {
if (!args.path) {
async handleFiles(args) {
if (!args.files || args.files.length === 0) {
return {
ok: false,
error: new ShowFileError('No file path provided', 'MISSING_PATH', {})
error: new ShowFileError('No files provided', 'MISSING_FILES', {})
};
}
return pipe(this.validatePath.bind(this), this.openInVSCode.bind(this))(args);
}
/**
* Handle multiple files request
*/
async handleMultipleFiles(args) {
if (!args.paths || args.paths.length === 0) {
return {
ok: false,
error: new ShowFileError('No file paths provided', 'MISSING_PATHS', {})
};
}
// Validate all paths
const validatedPaths = [];
for (const path of args.paths) {
const validationResult = await this.pathValidator.validatePath(path, { checkAccess: true });
for (const file of args.files) {
const validationResult = await this.pathValidator.validatePath(file, { checkAccess: true });
if (!validationResult.ok) {
return {
ok: false,
error: new ShowFileError(`Path validation failed for ${path}: ${validationResult.error.message}`, validationResult.error.code, { path, paths: args.paths })
error: new ShowFileError(`Path validation failed for ${file}: ${validationResult.error.message}`, validationResult.error.code, { file, files: args.files })
};

@@ -97,3 +84,23 @@ }

}
// Open all files in VS Code
// Handle single file with line number
if (args.files.length === 1 && args.line) {
const vsCodeResult = await this.vsCodeExecutor.openFile(validatedPaths[0], args.line);
if (!vsCodeResult.ok) {
return {
ok: false,
error: new ShowFileError(`Failed to open file in VS Code: ${vsCodeResult.error.message}`, vsCodeResult.error.code, { files: args.files, line: args.line })
};
}
return {
ok: true,
value: {
files: args.files,
validatedPaths,
command: vsCodeResult.value.command,
line: args.line,
success: vsCodeResult.value.success
}
};
}
// Handle multiple files (or single file without line number)
const vsCodeResult = await this.vsCodeExecutor.openFiles(validatedPaths);

@@ -103,3 +110,3 @@ if (!vsCodeResult.ok) {

ok: false,
error: new ShowFileError(`Failed to open files in VS Code: ${vsCodeResult.error.message}`, vsCodeResult.error.code, { paths: args.paths, validatedPaths })
error: new ShowFileError(`Failed to open files in VS Code: ${vsCodeResult.error.message}`, vsCodeResult.error.code, { files: args.files, validatedPaths })
};

@@ -110,3 +117,3 @@ }

value: {
paths: args.paths,
files: args.files,
validatedPaths,

@@ -119,63 +126,7 @@ command: vsCodeResult.value.command,

/**
* Validate file path
*/
async validatePath(args) {
if (!args.path) {
return {
ok: false,
error: new ShowFileError('No file path provided', 'MISSING_PATH', {})
};
}
const validationResult = await this.pathValidator.validatePath(args.path, { checkAccess: true });
if (!validationResult.ok) {
return {
ok: false,
error: new ShowFileError(`Path validation failed: ${validationResult.error.message}`, validationResult.error.code, { path: args.path })
};
}
const result = {
path: args.path,
validatedPath: validationResult.value
};
if (args.line_highlight !== undefined)
result.line_highlight = args.line_highlight;
return {
ok: true,
value: result
};
}
/**
* Open file in VS Code
*/
async openInVSCode(data) {
if (!data.validatedPath) {
return {
ok: false,
error: new ShowFileError('No validated path available', 'MISSING_VALIDATED_PATH', { path: data.path })
};
}
const vsCodeResult = await this.vsCodeExecutor.openFile(data.validatedPath, data.line_highlight);
if (!vsCodeResult.ok) {
return {
ok: false,
error: new ShowFileError(`Failed to open file in VS Code: ${vsCodeResult.error.message}`, vsCodeResult.error.code, { path: data.path, validatedPath: data.validatedPath, line_highlight: data.line_highlight })
};
}
const result = {
path: data.path,
command: vsCodeResult.value.command,
success: vsCodeResult.value.success
};
if (data.line_highlight !== undefined)
result.line_highlight = data.line_highlight;
return {
ok: true,
value: result
};
}
/**
* Format success response
*/
formatSuccessResponse(data) {
if (data.paths && data.paths.length > 0) {
if (data.files.length === 1) {
const lineText = data.line ? ` at line ${data.line}` : '';
return {

@@ -185,3 +136,3 @@ content: [

type: 'text',
text: `${data.paths.length} files opened in VS Code as tabs`
text: `File opened in VS Code: ${data.files[0]}${lineText}`
}

@@ -191,13 +142,2 @@ ]

}
else if (data.path) {
const lineText = data.line_highlight ? ` at line ${data.line_highlight}` : '';
return {
content: [
{
type: 'text',
text: `File opened in VS Code: ${data.path}${lineText}`
}
]
};
}
else {

@@ -208,3 +148,3 @@ return {

type: 'text',
text: 'Files opened in VS Code'
text: `${data.files.length} files opened in VS Code as tabs`
}

@@ -211,0 +151,0 @@ ]

@@ -60,11 +60,7 @@ #!/usr/bin/env node

name: 'ShowFile',
description: 'Opens file(s) in VS Code. Only call this function if you were explicitly and immediately asked to show a file by the user in their last message to you. Don\'t provide any summary or additional commentary after calling the tool - the user will see the file directly.',
description: 'Opens files in VS Code. Only call this function if you were explicitly and immediately asked to show a file by the user in their last message to you. Don\'t provide any summary or commentary after calling the tool - the user will see the files directly.',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Single file path relative to workspace root (use this OR paths, not both)',
},
paths: {
files: {
type: 'array',

@@ -74,14 +70,11 @@ items: {

},
description: 'Multiple file paths to open as tabs in VS Code',
description: 'Files to open in VS Code. Required.',
},
line_highlight: {
line: {
type: 'number',
description: 'Optional line number to highlight and jump to (only works with single path)',
description: 'Line number to jump to (only applies to first file). Optional.',
minimum: 1,
},
reuseWindow: {
type: 'boolean',
description: 'Open in current VS Code window instead of new window (default: false)',
},
},
required: ['files'],
},

@@ -91,14 +84,6 @@ },

name: 'ShowDiff',
description: 'Opens git diff in VS Code. Only call this function if you were explicitly and immediately asked to show a changes by the user in their last message to you. Don\'t provide any summary or additional commentary after calling the tool - the user will see the file directly.',
description: 'Opens git diff in VS Code showing changes since last commit (HEAD vs working directory). Only call this function if you were explicitly and immediately asked to show changes by the user in their last message to you. Don\'t provide any summary or commentary after calling the tool - the user will see the diff directly.',
inputSchema: {
type: 'object',
properties: {
base: {
type: 'string',
description: 'Base commit, branch, or tag for comparison (omit for working directory changes)',
},
target: {
type: 'string',
description: 'Target commit or branch (omit for working directory)',
},
files: {

@@ -109,16 +94,4 @@ type: 'array',

},
description: 'Specific files to include in diff (optional)',
description: 'Files to show diff for. If not provided, shows all changed files. Optional.',
},
staged: {
type: 'boolean',
description: 'Show only staged changes (default: false). Only use this param if explicitly asked by user to show staged only changes.',
},
unstaged: {
type: 'boolean',
description: 'Show only unstaged changes (default: false). Only use this param if explicitly asked by user to show unstaged only changes.',
},
reuseWindow: {
type: 'boolean',
description: 'Open in current VS Code window instead of new window (default: false)',
},
},

@@ -147,12 +120,8 @@ },

const request = {};
// Handle single path or multiple paths
if (typeof args.path === 'string') {
request.path = args.path;
if (Array.isArray(args.files) && args.files.every(f => typeof f === 'string')) {
request.files = args.files;
}
else if (Array.isArray(args.paths) && args.paths.every(p => typeof p === 'string')) {
request.paths = args.paths;
if (typeof args.line === 'number') {
request.line = args.line;
}
if (typeof args.line_highlight === 'number') {
request.line_highlight = args.line_highlight;
}
return await showFileHandler.handleFileRequest(request);

@@ -162,13 +131,5 @@ }

const request = {};
if (typeof args.base === 'string')
request.base = args.base;
if (typeof args.target === 'string')
request.target = args.target;
if (Array.isArray(args.files) && args.files.every(f => typeof f === 'string')) {
request.files = args.files;
}
if (typeof args.staged === 'boolean')
request.staged = args.staged;
if (typeof args.unstaged === 'boolean')
request.unstaged = args.unstaged;
return await showDiffHandler.handleDiffRequest(request);

@@ -175,0 +136,0 @@ }

{
"name": "@dean0x/showme",
"version": "0.1.8",
"version": "0.2.0",
"description": "MCP server providing ShowFile and ShowDiff tools for VS Code integration with syntax highlighting",

@@ -5,0 +5,0 @@ "main": "dist/index.js",