📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Tool Checker Scripts
|
||||
|
||||
Checks for modern CLI tools and provides installation guidance.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Check all tools (text output)
|
||||
bun scripts/index.ts
|
||||
|
||||
# Check specific category
|
||||
bun scripts/index.ts --category search
|
||||
bun scripts/index.ts -c viewers
|
||||
|
||||
# JSON output
|
||||
bun scripts/index.ts --format json
|
||||
bun scripts/index.ts -f json
|
||||
|
||||
# Combine options
|
||||
bun scripts/index.ts -c navigation -f json
|
||||
```
|
||||
|
||||
## Categories
|
||||
|
||||
- `search` - fd, ripgrep, ast-grep
|
||||
- `json` - jq
|
||||
- `viewers` - bat, eza, delta
|
||||
- `navigation` - zoxide, fzf
|
||||
- `http` - httpie
|
||||
|
||||
## Output Formats
|
||||
|
||||
### Text (default)
|
||||
|
||||
```
|
||||
◆ Available Tools
|
||||
|
||||
search
|
||||
✓ fd 10.2.0 — Fast file finder (replaces find)
|
||||
✓ rg 14.1.0 — Fast code search (replaces grep)
|
||||
✗ sg — AST-aware code search and refactoring
|
||||
→ brew install ast-grep
|
||||
|
||||
◇ Summary: 2/3 tools available
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"search": [
|
||||
{
|
||||
"name": "fd",
|
||||
"command": "fd",
|
||||
"category": "search",
|
||||
"available": true,
|
||||
"version": "fd 10.2.0",
|
||||
"replaces": "find",
|
||||
"description": "Fast file finder",
|
||||
"install": {
|
||||
"brew": "brew install fd",
|
||||
"cargo": "cargo install fd-find",
|
||||
"apt": "apt install fd-find",
|
||||
"url": "https://github.com/sharkdp/fd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
scripts/
|
||||
├── index.ts # Entry point - CLI arg parsing and orchestration
|
||||
├── types.ts # Shared TypeScript types
|
||||
├── utils.ts # Tool detection utilities
|
||||
└── checkers/
|
||||
├── search.ts # fd, rg, sg
|
||||
├── json.ts # jq
|
||||
├── viewers.ts # bat, eza, delta
|
||||
├── navigation.ts # z, fzf
|
||||
└── http.ts # http (httpie)
|
||||
```
|
||||
|
||||
Each checker module exports a function that returns `Promise<ToolCheckResult[]>`.
|
||||
|
||||
## Adding New Tools
|
||||
|
||||
1. Add tool definition to appropriate checker module:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: "tool-name",
|
||||
command: "actual-command",
|
||||
category: "category",
|
||||
replaces: "legacy-tool", // optional
|
||||
description: "One-line description",
|
||||
install: {
|
||||
brew: "brew install tool-name",
|
||||
cargo: "cargo install tool-name", // optional
|
||||
apt: "apt install tool-name", // optional
|
||||
url: "https://github.com/org/repo",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
2. Tool is automatically checked and included in results.
|
||||
|
||||
## Adding New Categories
|
||||
|
||||
1. Add category to `types.ts`:
|
||||
|
||||
```typescript
|
||||
export type Category = "search" | "json" | "viewers" | "navigation" | "http" | "new-category";
|
||||
```
|
||||
|
||||
2. Create checker module `checkers/new-category.ts`:
|
||||
|
||||
```typescript
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
export async function checkNewCategoryTools(): Promise<ToolCheckResult[]> {
|
||||
// ... implementation
|
||||
}
|
||||
```
|
||||
|
||||
3. Import and register in `index.ts`:
|
||||
|
||||
```typescript
|
||||
import { checkNewCategoryTools } from "./checkers/new-category.ts";
|
||||
|
||||
const CHECKERS: Record<Category, CheckerFunction> = {
|
||||
// ...
|
||||
"new-category": checkNewCategoryTools,
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Checks availability of HTTP client tools (httpie).
|
||||
* @returns Array of tool check results for HTTP category
|
||||
*/
|
||||
export async function checkHttpTools(): Promise<ToolCheckResult[]> {
|
||||
const tools = [
|
||||
{
|
||||
name: "httpie",
|
||||
command: "http",
|
||||
category: "http",
|
||||
replaces: "curl",
|
||||
description: "Human-friendly HTTP client for testing APIs",
|
||||
install: {
|
||||
brew: "brew install httpie",
|
||||
apt: "apt install httpie",
|
||||
url: "https://httpie.io/",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
const results = await Promise.all(
|
||||
tools.map(async (tool) => {
|
||||
const { available, version } = await checkTool(tool.command);
|
||||
return {
|
||||
...tool,
|
||||
available,
|
||||
version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Checks availability of JSON processing tools (jq).
|
||||
* @returns Array of tool check results for JSON category
|
||||
*/
|
||||
export async function checkJsonTools(): Promise<ToolCheckResult[]> {
|
||||
const tools = [
|
||||
{
|
||||
name: "jq",
|
||||
command: "jq",
|
||||
category: "json",
|
||||
description: "JSON processor and query language",
|
||||
install: {
|
||||
brew: "brew install jq",
|
||||
apt: "apt install jq",
|
||||
url: "https://jqlang.github.io/jq/",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
const results = await Promise.all(
|
||||
tools.map(async (tool) => {
|
||||
const { available, version } = await checkTool(tool.command);
|
||||
return {
|
||||
...tool,
|
||||
available,
|
||||
version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Checks availability of navigation tools (zoxide, fzf).
|
||||
* @returns Array of tool check results for navigation category
|
||||
*/
|
||||
export async function checkNavigationTools(): Promise<ToolCheckResult[]> {
|
||||
const tools = [
|
||||
{
|
||||
name: "zoxide",
|
||||
command: "z",
|
||||
category: "navigation",
|
||||
replaces: "cd",
|
||||
description: "Smart directory jumper that learns your habits",
|
||||
install: {
|
||||
brew: "brew install zoxide",
|
||||
cargo: "cargo install zoxide",
|
||||
apt: "apt install zoxide",
|
||||
url: "https://github.com/ajeetdsouza/zoxide",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fzf",
|
||||
command: "fzf",
|
||||
category: "navigation",
|
||||
description: "Fuzzy finder for files, commands, and more",
|
||||
install: {
|
||||
brew: "brew install fzf",
|
||||
apt: "apt install fzf",
|
||||
url: "https://github.com/junegunn/fzf",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
const results = await Promise.all(
|
||||
tools.map(async (tool) => {
|
||||
const { available, version } = await checkTool(tool.command);
|
||||
return {
|
||||
...tool,
|
||||
available,
|
||||
version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Checks availability of search-related CLI tools (fd, ripgrep, ast-grep).
|
||||
* @returns Array of tool check results for search category
|
||||
*/
|
||||
export async function checkSearchTools(): Promise<ToolCheckResult[]> {
|
||||
const tools = [
|
||||
{
|
||||
name: "fd",
|
||||
command: "fd",
|
||||
category: "search",
|
||||
replaces: "find",
|
||||
description: "Fast file finder",
|
||||
install: {
|
||||
brew: "brew install fd",
|
||||
cargo: "cargo install fd-find",
|
||||
apt: "apt install fd-find",
|
||||
url: "https://github.com/sharkdp/fd",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ripgrep",
|
||||
command: "rg",
|
||||
category: "search",
|
||||
replaces: "grep",
|
||||
description: "Fast code search",
|
||||
install: {
|
||||
brew: "brew install ripgrep",
|
||||
cargo: "cargo install ripgrep",
|
||||
apt: "apt install ripgrep",
|
||||
url: "https://github.com/BurntSushi/ripgrep",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ast-grep",
|
||||
command: "sg",
|
||||
category: "search",
|
||||
description: "AST-aware code search and refactoring",
|
||||
install: {
|
||||
brew: "brew install ast-grep",
|
||||
cargo: "cargo install ast-grep",
|
||||
apt: "npm install -g @ast-grep/cli",
|
||||
url: "https://github.com/ast-grep/ast-grep",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
const results = await Promise.all(
|
||||
tools.map(async (tool) => {
|
||||
const { available, version } = await checkTool(tool.command);
|
||||
return {
|
||||
...tool,
|
||||
available,
|
||||
version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { ToolCheckResult } from "../types.ts";
|
||||
import { checkTool } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Checks availability of file viewer tools (bat, eza, delta).
|
||||
* @returns Array of tool check results for viewers category
|
||||
*/
|
||||
export async function checkViewerTools(): Promise<ToolCheckResult[]> {
|
||||
const tools = [
|
||||
{
|
||||
name: "bat",
|
||||
command: "bat",
|
||||
category: "viewers",
|
||||
replaces: "cat",
|
||||
description: "cat with syntax highlighting and git integration",
|
||||
install: {
|
||||
brew: "brew install bat",
|
||||
cargo: "cargo install bat",
|
||||
apt: "apt install bat",
|
||||
url: "https://github.com/sharkdp/bat",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "eza",
|
||||
command: "eza",
|
||||
category: "viewers",
|
||||
replaces: "ls",
|
||||
description: "Modern ls replacement with colors and icons",
|
||||
install: {
|
||||
brew: "brew install eza",
|
||||
cargo: "cargo install eza",
|
||||
apt: "apt install eza",
|
||||
url: "https://github.com/eza-community/eza",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delta",
|
||||
command: "delta",
|
||||
category: "viewers",
|
||||
replaces: "diff",
|
||||
description: "Better git diff pager with syntax highlighting",
|
||||
install: {
|
||||
brew: "brew install git-delta",
|
||||
cargo: "cargo install git-delta",
|
||||
apt: "apt install git-delta",
|
||||
url: "https://github.com/dandavison/delta",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
const results = await Promise.all(
|
||||
tools.map(async (tool) => {
|
||||
const { available, version } = await checkTool(tool.command);
|
||||
return {
|
||||
...tool,
|
||||
available,
|
||||
version,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { parseArgs } from "node:util";
|
||||
import type { Category, OutputFormat, ToolCheckResult } from "./types.ts";
|
||||
import { checkSearchTools } from "./checkers/search.ts";
|
||||
import { checkJsonTools } from "./checkers/json.ts";
|
||||
import { checkViewerTools } from "./checkers/viewers.ts";
|
||||
import { checkNavigationTools } from "./checkers/navigation.ts";
|
||||
import { checkHttpTools } from "./checkers/http.ts";
|
||||
|
||||
/**
|
||||
* Function signature for tool category checkers.
|
||||
*/
|
||||
interface CheckerFunction {
|
||||
(): Promise<ToolCheckResult[]>;
|
||||
}
|
||||
|
||||
const CHECKERS: Record<Category, CheckerFunction> = {
|
||||
search: checkSearchTools,
|
||||
json: checkJsonTools,
|
||||
viewers: checkViewerTools,
|
||||
navigation: checkNavigationTools,
|
||||
http: checkHttpTools,
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse command-line arguments
|
||||
*/
|
||||
function parseCliArgs() {
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
category: {
|
||||
type: "string",
|
||||
short: "c",
|
||||
},
|
||||
format: {
|
||||
type: "string",
|
||||
short: "f",
|
||||
default: "text",
|
||||
},
|
||||
},
|
||||
strict: true,
|
||||
allowPositionals: false,
|
||||
});
|
||||
|
||||
const category = values.category as Category | undefined;
|
||||
const format = (values.format || "text") as OutputFormat;
|
||||
|
||||
// Validate category if provided
|
||||
if (category && !Object.keys(CHECKERS).includes(category)) {
|
||||
console.error(
|
||||
`Invalid category: ${category}. Valid categories: ${Object.keys(CHECKERS).join(", ")}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Validate format
|
||||
if (format !== "json" && format !== "text") {
|
||||
console.error(`Invalid format: ${format}. Valid formats: json, text`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return { category, format };
|
||||
}
|
||||
|
||||
/**
|
||||
* Run checkers based on category filter
|
||||
*/
|
||||
async function runCheckers(
|
||||
category?: Category,
|
||||
): Promise<Map<Category, ToolCheckResult[]>> {
|
||||
const categoriesToRun = category
|
||||
? [category]
|
||||
: (Object.keys(CHECKERS) as Category[]);
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
categoriesToRun.map(async (cat) => {
|
||||
const checker = CHECKERS[cat];
|
||||
const tools = await checker();
|
||||
return { category: cat, tools };
|
||||
}),
|
||||
);
|
||||
|
||||
const toolsByCategory = new Map<Category, ToolCheckResult[]>();
|
||||
|
||||
for (const result of results) {
|
||||
if (result.status === "fulfilled") {
|
||||
toolsByCategory.set(result.value.category, result.value.tools);
|
||||
} else {
|
||||
console.error(`Error checking tools: ${result.reason}`);
|
||||
}
|
||||
}
|
||||
|
||||
return toolsByCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format results as JSON
|
||||
*/
|
||||
function formatJson(toolsByCategory: Map<Category, ToolCheckResult[]>): string {
|
||||
const output: Record<string, ToolCheckResult[]> = {};
|
||||
|
||||
for (const [category, tools] of toolsByCategory) {
|
||||
output[category] = tools;
|
||||
}
|
||||
|
||||
return JSON.stringify(output, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format results as human-readable text
|
||||
*/
|
||||
function formatText(toolsByCategory: Map<Category, ToolCheckResult[]>): string {
|
||||
const lines: string[] = ["◆ Available Tools", ""];
|
||||
|
||||
let totalTools = 0;
|
||||
let availableTools = 0;
|
||||
|
||||
for (const [category, tools] of toolsByCategory) {
|
||||
lines.push(` ${category}`);
|
||||
|
||||
for (const tool of tools) {
|
||||
totalTools++;
|
||||
if (tool.available) {
|
||||
availableTools++;
|
||||
const versionStr = tool.version ? ` ${tool.version}` : "";
|
||||
const replacesStr = tool.replaces ? ` (replaces ${tool.replaces})` : "";
|
||||
lines.push(
|
||||
` ✓ ${tool.name}${versionStr} — ${tool.description}${replacesStr}`,
|
||||
);
|
||||
} else {
|
||||
lines.push(` ✗ ${tool.name} — ${tool.description}`);
|
||||
// Show installation hint (prefer brew, then cargo, then apt)
|
||||
const installCmd =
|
||||
tool.install.brew || tool.install.cargo || tool.install.apt;
|
||||
if (installCmd) {
|
||||
lines.push(` → ${installCmd}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
lines.push(`◇ Summary: ${availableTools}/${totalTools} tools available`);
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point
|
||||
*/
|
||||
async function main() {
|
||||
const { category, format } = parseCliArgs();
|
||||
|
||||
const toolsByCategory = await runCheckers(category);
|
||||
|
||||
const output =
|
||||
format === "json"
|
||||
? formatJson(toolsByCategory)
|
||||
: formatText(toolsByCategory);
|
||||
|
||||
console.log(output);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Result of checking a CLI tool's availability and version.
|
||||
*/
|
||||
export interface ToolCheckResult {
|
||||
/** Tool display name */
|
||||
name: string;
|
||||
/** Command used to invoke the tool */
|
||||
command: string;
|
||||
/** Tool category for grouping */
|
||||
category: string;
|
||||
/** Whether the tool is available in PATH */
|
||||
available: boolean;
|
||||
/** Version string if available */
|
||||
version?: string;
|
||||
/** Standard tool this replaces (e.g., fd replaces find) */
|
||||
replaces?: string;
|
||||
/** Human-readable description */
|
||||
description: string;
|
||||
/** Installation instructions by package manager */
|
||||
install: {
|
||||
brew?: string;
|
||||
cargo?: string;
|
||||
apt?: string;
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool categories for grouping related tools.
|
||||
*/
|
||||
export type Category = "search" | "json" | "viewers" | "navigation" | "http";
|
||||
|
||||
/**
|
||||
* Output format for tool check results.
|
||||
*/
|
||||
export type OutputFormat = "json" | "text";
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Checks if a command-line tool is available and gets its version.
|
||||
* @param cmd - Command name to check in PATH
|
||||
* @returns Object with availability status and optional version string
|
||||
*/
|
||||
export async function checkTool(
|
||||
cmd: string,
|
||||
): Promise<{ available: boolean; version?: string }> {
|
||||
try {
|
||||
// Check if command exists
|
||||
const whichProc = Bun.spawn(["which", cmd], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const exitCode = await whichProc.exited;
|
||||
|
||||
if (exitCode !== 0) {
|
||||
return { available: false };
|
||||
}
|
||||
|
||||
// Try to get version
|
||||
try {
|
||||
const versionProc = Bun.spawn([cmd, "--version"], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const versionOut = await new Response(versionProc.stdout).text();
|
||||
await versionProc.exited;
|
||||
|
||||
// Extract first line and trim
|
||||
const version = versionOut.split("\n")[0]?.trim();
|
||||
return { available: true, version };
|
||||
} catch {
|
||||
// Tool exists but --version failed, still mark as available
|
||||
return { available: true };
|
||||
}
|
||||
} catch {
|
||||
return { available: false };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user