chore: fork — remove vendor CI, repoint release checks to Neuron Gitea

This commit is contained in:
2026-08-21 13:36:36 -05:00
parent 57fa34f235
commit b8c189b4b7
140 changed files with 734 additions and 16034 deletions
@@ -1,199 +0,0 @@
import { test, expect, describe } from "bun:test"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { extractResponseText, formatPromptTooLargeError } from "../../src/cli/cmd/github"
import type { MessageV2 } from "../../src/session/message-v2"
import { SessionID, MessageID, PartID } from "../../src/session/schema"
// Helper to create minimal valid parts
function createTextPart(text: string): SessionV1.Part {
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "text" as const,
text,
}
}
function createReasoningPart(text: string): SessionV1.Part {
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "reasoning" as const,
text,
time: { start: 0 },
}
}
function createToolPart(tool: string, title: string, status: "completed" | "running" = "completed"): SessionV1.Part {
if (status === "completed") {
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "tool" as const,
callID: "c1",
tool,
state: {
status: "completed",
input: {},
output: "",
title,
metadata: {},
time: { start: 0, end: 1 },
},
}
}
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "tool" as const,
callID: "c1",
tool,
state: {
status: "running",
input: {},
time: { start: 0 },
},
}
}
function createStepStartPart(): SessionV1.Part {
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "step-start" as const,
}
}
function createStepFinishPart(): SessionV1.Part {
return {
id: PartID.ascending(),
sessionID: SessionID.make("ses_test"),
messageID: MessageID.make("msg_test"),
type: "step-finish" as const,
reason: "done",
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
}
}
describe("extractResponseText", () => {
test("returns text from text part", () => {
const parts = [createTextPart("Hello world")]
expect(extractResponseText(parts)).toBe("Hello world")
})
test("returns last text part when multiple exist", () => {
const parts = [createTextPart("First"), createTextPart("Last")]
expect(extractResponseText(parts)).toBe("Last")
})
test("returns text even when tool parts follow", () => {
const parts = [createTextPart("I'll help with that."), createToolPart("todowrite", "3 todos")]
expect(extractResponseText(parts)).toBe("I'll help with that.")
})
test("returns null for reasoning-only response (signals summary needed)", () => {
const parts = [createReasoningPart("Let me think about this...")]
expect(extractResponseText(parts)).toBeNull()
})
test("returns null for tool-only response (signals summary needed)", () => {
// This is the exact scenario from the bug report - todowrite with no text
const parts = [createToolPart("todowrite", "8 todos")]
expect(extractResponseText(parts)).toBeNull()
})
test("returns null for multiple completed tools", () => {
const parts = [
createToolPart("read", "src/file.ts"),
createToolPart("edit", "src/file.ts"),
createToolPart("bash", "bun test"),
]
expect(extractResponseText(parts)).toBeNull()
})
test("returns null for running tool parts (signals summary needed)", () => {
const parts = [createToolPart("bash", "", "running")]
expect(extractResponseText(parts)).toBeNull()
})
test("throws on empty array", () => {
expect(() => extractResponseText([])).toThrow("no parts returned")
})
test("returns null for step-start only", () => {
const parts = [createStepStartPart()]
expect(extractResponseText(parts)).toBeNull()
})
test("returns null for step-finish only", () => {
const parts = [createStepFinishPart()]
expect(extractResponseText(parts)).toBeNull()
})
test("returns null for step-start and step-finish", () => {
const parts = [createStepStartPart(), createStepFinishPart()]
expect(extractResponseText(parts)).toBeNull()
})
test("returns text from multi-step response", () => {
const parts = [
createStepStartPart(),
createToolPart("read", "src/file.ts"),
createTextPart("Done"),
createStepFinishPart(),
]
expect(extractResponseText(parts)).toBe("Done")
})
test("prefers text over reasoning when both present", () => {
const parts = [createReasoningPart("Internal thinking..."), createTextPart("Final answer")]
expect(extractResponseText(parts)).toBe("Final answer")
})
test("prefers text over tools when both present", () => {
const parts = [createToolPart("read", "src/file.ts"), createTextPart("Here's what I found")]
expect(extractResponseText(parts)).toBe("Here's what I found")
})
})
describe("formatPromptTooLargeError", () => {
test("formats error without files", () => {
const result = formatPromptTooLargeError([])
expect(result).toBe("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
})
test("formats error with files (base64 content)", () => {
// Base64 is ~33% larger than original, so we multiply by 0.75 to get original size
// 400 KB base64 = 300 KB original, 200 KB base64 = 150 KB original
const files = [
{ filename: "screenshot.png", content: "a".repeat(400 * 1024) },
{ filename: "diagram.png", content: "b".repeat(200 * 1024) },
]
const result = formatPromptTooLargeError(files)
expect(result).toStartWith("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
expect(result).toInclude("Files in prompt:")
expect(result).toInclude("screenshot.png (300 KB)")
expect(result).toInclude("diagram.png (150 KB)")
})
test("lists all files when multiple present", () => {
// Base64 sizes: 4KB -> 3KB, 8KB -> 6KB, 12KB -> 9KB
const files = [
{ filename: "img1.png", content: "x".repeat(4 * 1024) },
{ filename: "img2.jpg", content: "y".repeat(8 * 1024) },
{ filename: "img3.gif", content: "z".repeat(12 * 1024) },
]
const result = formatPromptTooLargeError(files)
expect(result).toInclude("img1.png (3 KB)")
expect(result).toInclude("img2.jpg (6 KB)")
expect(result).toInclude("img3.gif (9 KB)")
})
})
@@ -1,90 +0,0 @@
import { test, expect } from "bun:test"
import { parseGitHubRemote } from "../../src/cli/cmd/github"
test("parses https URL with .git suffix", () => {
expect(parseGitHubRemote("https://github.com/sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses https URL without .git suffix", () => {
expect(parseGitHubRemote("https://github.com/sst/opencode")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses git@ URL with .git suffix", () => {
expect(parseGitHubRemote("git@github.com:sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses git@ URL without .git suffix", () => {
expect(parseGitHubRemote("git@github.com:sst/opencode")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses ssh:// URL with .git suffix", () => {
expect(parseGitHubRemote("ssh://git@github.com/sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses ssh:// URL without .git suffix", () => {
expect(parseGitHubRemote("ssh://git@github.com/sst/opencode")).toEqual({ owner: "sst", repo: "opencode" })
})
test("parses git protocol URLs from package metadata", () => {
expect(parseGitHubRemote("git://github.com/facebook/react.git")).toEqual({ owner: "facebook", repo: "react" })
expect(parseGitHubRemote("git+https://github.com/facebook/react.git")).toEqual({ owner: "facebook", repo: "react" })
expect(parseGitHubRemote("git+ssh://git@github.com/facebook/react.git")).toEqual({ owner: "facebook", repo: "react" })
})
test("parses npm-style github shorthand", () => {
expect(parseGitHubRemote("github:facebook/react")).toBeNull()
})
test("parses http URL", () => {
expect(parseGitHubRemote("http://github.com/owner/repo")).toEqual({ owner: "owner", repo: "repo" })
})
test("parses URL with hyphenated owner and repo names", () => {
expect(parseGitHubRemote("https://github.com/my-org/my-repo.git")).toEqual({ owner: "my-org", repo: "my-repo" })
})
test("parses URL with underscores in names", () => {
expect(parseGitHubRemote("git@github.com:my_org/my_repo.git")).toEqual({ owner: "my_org", repo: "my_repo" })
})
test("parses URL with numbers in names", () => {
expect(parseGitHubRemote("https://github.com/org123/repo456")).toEqual({ owner: "org123", repo: "repo456" })
})
test("parses repos with dots in the name", () => {
expect(parseGitHubRemote("https://github.com/socketio/socket.io.git")).toEqual({
owner: "socketio",
repo: "socket.io",
})
expect(parseGitHubRemote("https://github.com/vuejs/vue.js")).toEqual({
owner: "vuejs",
repo: "vue.js",
})
expect(parseGitHubRemote("git@github.com:mrdoob/three.js.git")).toEqual({
owner: "mrdoob",
repo: "three.js",
})
expect(parseGitHubRemote("https://github.com/jashkenas/backbone.git")).toEqual({
owner: "jashkenas",
repo: "backbone",
})
})
test("returns null for non-github URLs", () => {
expect(parseGitHubRemote("https://gitlab.com/owner/repo.git")).toBeNull()
expect(parseGitHubRemote("git@gitlab.com:owner/repo.git")).toBeNull()
expect(parseGitHubRemote("https://bitbucket.org/owner/repo")).toBeNull()
})
test("returns null for invalid URLs", () => {
expect(parseGitHubRemote("not-a-url")).toBeNull()
expect(parseGitHubRemote("")).toBeNull()
expect(parseGitHubRemote("github.com")).toBeNull()
expect(parseGitHubRemote("https://github.com/")).toBeNull()
expect(parseGitHubRemote("https://github.com/owner")).toBeNull()
})
test("returns null for URLs with extra path segments", () => {
expect(parseGitHubRemote("https://github.com/owner/repo/tree/main")).toBeNull()
expect(parseGitHubRemote("https://github.com/owner/repo/blob/main/file.ts")).toBeNull()
})
@@ -314,23 +314,6 @@ Options:
--pure run without external plugins [boolean]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode github --help 1`] = `
"opencode github
manage GitHub agent
Commands:
opencode github install install the GitHub agent
opencode github run run the GitHub agent
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode pr --help 1`] = `
"opencode pr <number>
@@ -581,34 +564,6 @@ Options:
--pure run without external plugins [boolean]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode github install --help 1`] = `
"opencode github install
install the GitHub agent
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode github run --help 1`] = `
"opencode github run
run the GitHub agent
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--event GitHub mock event to run the agent for [string]
--token GitHub personal access token (github_pat_********) [string]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode db path --help 1`] = `
"opencode db path
@@ -58,7 +58,6 @@ const TOP_LEVEL = [
"stats",
"export",
"import",
"github",
"pr",
"session",
"plugin",
@@ -80,8 +79,6 @@ const SUBCOMMANDS = [
["agent", "list"],
["session", "list"],
["session", "delete"],
["github", "install"],
["github", "run"],
["db", "path"],
] as const