Files
wehub-resource-sync d48cda4081
CI / Test (ubuntu-latest, Node 18.x, bun) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, npm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, pnpm) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 18.x, yarn) (push) Failing after 15m1s
CI / Test (ubuntu-latest, Node 20.x, bun) (push) Failing after 17m13s
CI / Test (ubuntu-latest, Node 20.x, npm) (push) Failing after 18m42s
CI / Test (ubuntu-latest, Node 20.x, pnpm) (push) Failing after 15m0s
CI / Test (ubuntu-latest, Node 20.x, yarn) (push) Failing after 49m44s
CI / Test (ubuntu-latest, Node 22.x, bun) (push) Failing after 51m55s
CI / Test (ubuntu-latest, Node 22.x, pnpm) (push) Failing after 21m57s
CI / Test (ubuntu-latest, Node 22.x, npm) (push) Failing after 37m39s
CI / Test (ubuntu-latest, Node 22.x, yarn) (push) Failing after 34m7s
CI / Validate Components (push) Failing after 37m15s
CI / Python Tests (push) Failing after 10m1s
CI / Security Scan (push) Failing after 10m1s
CI / Lint (push) Failing after 17m12s
CI / Coverage (push) Failing after 20m19s
CI / Test (macos-latest, Node 18.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 18.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 20.x, yarn) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, bun) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (macos-latest, Node 22.x, yarn) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, npm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, pnpm) (push) Has been cancelled
CI / Test (windows-latest, Node 22.x, yarn) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:55:55 +08:00

222 lines
6.1 KiB
TypeScript

/**
* ECC Custom Tool: Dependency Analyzer
*
* Analyzes project dependencies for outdated packages, security vulnerabilities,
* and unused dependencies. Supports multiple package managers.
*/
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import * as path from "path"
import * as fs from "fs"
interface DependencyInfo {
name: string
current: string
latest?: string
type: "production" | "development" | "peer"
outdated: boolean
security?: {
vulnerable: boolean
severity?: string
recommendation?: string
}
}
interface AnalysisResult {
success: boolean
packageManager: string
dependencies: DependencyInfo[]
summary: {
total: number
outdated: number
vulnerable: number
unused: number
}
recommendations: string[]
error?: string
}
const dependencyAnalyzerTool: ToolDefinition = tool({
description:
"Analyze project dependencies for outdated packages, security vulnerabilities, and unused dependencies. Supports npm, pnpm, yarn, and bun.",
args: {
type: tool.schema
.enum(["all", "outdated", "security", "unused"])
.optional()
.describe("Type of analysis to run (default: all)"),
fix: tool.schema
.boolean()
.optional()
.describe("Attempt to fix issues automatically (default: false)"),
depth: tool.schema
.number()
.optional()
.describe("Depth of dependency analysis (default: 1)"),
},
async execute(args, context): Promise<string> {
try {
const cwd = context.worktree || context.directory
const analysisType = args.type ?? "all"
const fix = args.fix ?? false
const depth = args.depth ?? 1
// Detect package manager
const packageManager = detectPackageManager(cwd)
// Analyze dependencies
const dependencies = await analyzeDependencies(cwd, packageManager, depth)
// Generate summary
const summary = generateSummary(dependencies)
// Generate recommendations
const recommendations = generateRecommendations(dependencies, summary, analysisType)
return JSON.stringify({
success: true,
packageManager,
dependencies: dependencies.slice(0, 50), // Limit output
summary,
recommendations,
analysisType,
fixMode: fix,
platform: process.platform,
})
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error)
return JSON.stringify({
success: false,
error: `Failed to analyze dependencies: ${errorMessage}`,
type: args.type,
})
}
},
})
export default dependencyAnalyzerTool
function detectPackageManager(cwd: string): string {
if (fs.existsSync(path.join(cwd, "bun.lockb"))) return "bun"
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm"
if (fs.existsSync(path.join(cwd, "yarn.lock"))) return "yarn"
if (fs.existsSync(path.join(cwd, "package-lock.json"))) return "npm"
return "npm"
}
async function analyzeDependencies(
cwd: string,
packageManager: string,
depth: number
): Promise<DependencyInfo[]> {
const dependencies: DependencyInfo[] = []
try {
// Read package.json
const packageJsonPath = path.join(cwd, "package.json")
if (!fs.existsSync(packageJsonPath)) {
throw new Error("package.json not found")
}
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
// Analyze production dependencies
if (packageJson.dependencies) {
for (const [name, version] of Object.entries(packageJson.dependencies)) {
dependencies.push({
name,
current: version as string,
type: "production",
outdated: false, // Would need npm outdated to check
})
}
}
// Analyze development dependencies
if (packageJson.devDependencies) {
for (const [name, version] of Object.entries(packageJson.devDependencies)) {
dependencies.push({
name,
current: version as string,
type: "development",
outdated: false,
})
}
}
// Analyze peer dependencies
if (packageJson.peerDependencies) {
for (const [name, version] of Object.entries(packageJson.peerDependencies)) {
dependencies.push({
name,
current: version as string,
type: "peer",
outdated: false,
})
}
}
} catch (error) {
throw new Error(`Failed to read package.json: ${error}`)
}
return dependencies
}
function generateSummary(dependencies: DependencyInfo[]) {
return {
total: dependencies.length,
outdated: dependencies.filter(d => d.outdated).length,
vulnerable: dependencies.filter(d => d.security?.vulnerable).length,
unused: 0, // Would need additional analysis
}
}
function generateRecommendations(
dependencies: DependencyInfo[],
summary: { total: number; outdated: number; vulnerable: number; unused: number },
analysisType: string
): string[] {
const recommendations: string[] = []
if (summary.outdated > 0) {
recommendations.push(
`${summary.outdated} outdated dependencies found. Consider updating with: npm update`
)
}
if (summary.vulnerable > 0) {
recommendations.push(
`${summary.vulnerable} vulnerable dependencies found. Run: npm audit fix`
)
}
if (summary.total > 100) {
recommendations.push(
"Large number of dependencies detected. Consider removing unused packages."
)
}
// Check for common issues
const hasTypeScript = dependencies.some(d => d.name === "typescript")
const hasEslint = dependencies.some(d => d.name === "eslint")
const hasPrettier = dependencies.some(d => d.name === "prettier")
if (hasTypeScript && !hasEslint) {
recommendations.push(
"TypeScript project without ESLint detected. Consider adding linting."
)
}
if (hasEslint && !hasPrettier) {
recommendations.push(
"ESLint without Prettier detected. Consider adding code formatting."
)
}
if (recommendations.length === 0) {
recommendations.push("No critical dependency issues found.")
}
return recommendations
}