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

77 lines
2.6 KiB
TypeScript

/**
* ECC Custom Tool: Git Summary
*
* Returns branch/status/log/diff details for the active repository.
*/
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import { execFileSync } from "child_process"
// Conservative subset of git's allowed ref-name characters. Rejects shell
// metacharacters and option-like leading `-` so a model-supplied baseBranch
// cannot inject into the shell command line built below.
const SAFE_GIT_REF = /^[A-Za-z0-9._/-]+$/
function isSafeRef(ref: string): boolean {
if (typeof ref !== "string" || ref.length === 0 || ref.length > 200) return false
if (!SAFE_GIT_REF.test(ref)) return false
if (ref.startsWith("-") || ref.startsWith(".") || ref.startsWith("/")) return false
if (ref.includes("..") || ref.includes("//")) return false
return true
}
function isSafeDepth(value: unknown): value is number {
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 1000
}
const gitSummaryTool: ToolDefinition = tool({
description:
"Generate git summary with branch, status, recent commits, and optional diff stats.",
args: {
depth: tool.schema
.number()
.optional()
.describe("Number of recent commits to include (default: 5)"),
includeDiff: tool.schema
.boolean()
.optional()
.describe("Include diff stats against base branch (default: true)"),
baseBranch: tool.schema
.string()
.optional()
.describe("Base branch for diff comparison (default: main)"),
},
async execute(args, context) {
const cwd = context.worktree || context.directory
const depth = isSafeDepth(args.depth) ? args.depth : 5
const includeDiff = args.includeDiff ?? true
const baseBranch = args.baseBranch ?? "main"
const result: Record<string, string> = {
branch: runArgs(["branch", "--show-current"], cwd) || "unknown",
status: runArgs(["status", "--short"], cwd) || "clean",
log: runArgs(["log", "--oneline", `-${depth}`], cwd) || "no commits found",
}
if (includeDiff) {
result.stagedDiff = runArgs(["diff", "--cached", "--stat"], cwd) || ""
result.branchDiff = isSafeRef(baseBranch)
? runArgs(["diff", `${baseBranch}...HEAD`, "--stat"], cwd) ||
`unable to diff against ${baseBranch}`
: `unable to diff against ${baseBranch} (invalid ref)`
}
return JSON.stringify(result)
},
})
export default gitSummaryTool
function runArgs(args: string[], cwd: string): string {
try {
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim()
} catch {
return ""
}
}