fix: resolve all test failures and Cubic review issues

- Fix unstable-agent-babysitter: add promptAsync to test mock
- Fix claude-code-mcp-loader: isolate tests from user home configs
- Fix npm-dist-tags: encode packageName for scoped packages
- Fix agent-builder: clone source to prevent shared object mutation
- Fix add-plugin-to-opencode-config: handle JSONC with leading comments
- Fix auth-plugins/add-provider-config: error on parse failures
- Fix bun-install: clear timeout on completion
- Fix git-diff-stats: include untracked files in diff summary
This commit is contained in:
YeonGyu-Kim
2026-02-08 15:31:32 +09:00
parent 119e18c810
commit c7122b4127
9 changed files with 91 additions and 71 deletions
+47 -22
View File
@@ -9,15 +9,6 @@ interface GitFileStat {
export function getGitDiffStats(directory: string): GitFileStat[] {
try {
const output = execSync("git diff --numstat HEAD", {
cwd: directory,
encoding: "utf-8",
timeout: 5000,
stdio: ["pipe", "pipe", "pipe"],
}).trim()
if (!output) return []
const statusOutput = execSync("git status --porcelain", {
cwd: directory,
encoding: "utf-8",
@@ -25,12 +16,18 @@ export function getGitDiffStats(directory: string): GitFileStat[] {
stdio: ["pipe", "pipe", "pipe"],
}).trim()
if (!statusOutput) return []
const statusMap = new Map<string, "modified" | "added" | "deleted">()
const untrackedFiles: string[] = []
for (const line of statusOutput.split("\n")) {
if (!line) continue
const status = line.substring(0, 2).trim()
const filePath = line.substring(3)
if (status === "A" || status === "??") {
if (status === "??") {
statusMap.set(filePath, "added")
untrackedFiles.push(filePath)
} else if (status === "A") {
statusMap.set(filePath, "added")
} else if (status === "D") {
statusMap.set(filePath, "deleted")
@@ -39,21 +36,49 @@ export function getGitDiffStats(directory: string): GitFileStat[] {
}
}
const output = execSync("git diff --numstat HEAD", {
cwd: directory,
encoding: "utf-8",
timeout: 5000,
stdio: ["pipe", "pipe", "pipe"],
}).trim()
const stats: GitFileStat[] = []
for (const line of output.split("\n")) {
const parts = line.split("\t")
if (parts.length < 3) continue
const trackedPaths = new Set<string>()
const [addedStr, removedStr, path] = parts
const added = addedStr === "-" ? 0 : parseInt(addedStr, 10)
const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10)
if (output) {
for (const line of output.split("\n")) {
const parts = line.split("\t")
if (parts.length < 3) continue
stats.push({
path,
added,
removed,
status: statusMap.get(path) ?? "modified",
})
const [addedStr, removedStr, path] = parts
const added = addedStr === "-" ? 0 : parseInt(addedStr, 10)
const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10)
trackedPaths.add(path)
stats.push({
path,
added,
removed,
status: statusMap.get(path) ?? "modified",
})
}
}
for (const filePath of untrackedFiles) {
if (trackedPaths.has(filePath)) continue
try {
const content = execSync(`wc -l < "${filePath}"`, {
cwd: directory,
encoding: "utf-8",
timeout: 3000,
stdio: ["pipe", "pipe", "pipe"],
}).trim()
const lineCount = parseInt(content, 10) || 0
stats.push({ path: filePath, added: lineCount, removed: 0, status: "added" })
} catch {
stats.push({ path: filePath, added: 0, removed: 0, status: "added" })
}
}
return stats
@@ -21,6 +21,9 @@ function createMockPluginInput(options: {
prompt: async (input: unknown) => {
promptCalls.push({ input })
},
promptAsync: async (input: unknown) => {
promptCalls.push({ input })
},
},
},
}