Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"version": "2.5.9",
"resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a",
"integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a"
}
}
}
8 changes: 8 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "Yasa",
"image": "oven/bun:1.3",
"mounts": [
"source=opencode-node-modules,target=${containerWorkspaceFolder}/node_modules,type=volume"
],
"postCreateCommand": "bun install"
}
7 changes: 4 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"heap-snapshot-toolkit": "1.1.3",
"openai": "^6.16.0",
"typescript": "catalog:"
},
"repository": {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,22 @@
"@effect/opentelemetry": "catalog:",
"@effect/platform-node": "catalog:",
"@effect/sql-sqlite-bun": "catalog:",
"@lydell/node-pty": "catalog:",
"@ff-labs/fff-bun": "0.9.4",
"@lydell/node-pty": "catalog:",
"@npmcli/arborist": "9.4.0",
"@npmcli/config": "10.8.1",
"@opencode-ai/effect-drizzle-sqlite": "workspace:*",
"@opencode-ai/effect-sqlite-node": "workspace:*",
"@opencode-ai/llm": "workspace:*",
"@opencode-ai/schema": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/schema": "workspace:*",
"@openrouter/ai-sdk-provider": "2.9.0",
"@opentelemetry/api": "1.9.0",
"@opentelemetry/context-async-hooks": "2.6.1",
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
"@opentelemetry/sdk-trace-base": "2.6.1",
"@parcel/watcher": "2.5.1",
"@silvia-odwyer/photon-node": "0.3.4",
"@openrouter/ai-sdk-provider": "2.9.0",
"ai-gateway-provider": "3.1.2",
"bun-pty": "0.4.8",
"cross-spawn": "catalog:",
Expand All @@ -113,8 +113,8 @@
"google-auth-library": "10.5.0",
"gray-matter": "4.0.3",
"htmlparser2": "8.0.2",
"immer": "11.1.4",
"ignore": "7.0.5",
"immer": "11.1.4",
"jsonc-parser": "3.3.1",
"mime-types": "3.0.2",
"minimatch": "10.2.5",
Expand Down
54 changes: 37 additions & 17 deletions packages/opencode/src/util/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,29 +136,38 @@ function buildFileReference(input: { url: URL; remote: string }) {
} satisfies FileReference
}

export function parseRepositoryReference(input: string) {
const cleaned = normalizeRepositoryInput(input)
if (!cleaned) return null

function tryGithubPrefixedReference(cleaned: string): Reference | null | undefined {
const githubPrefixed = cleaned.match(/^github:([^/\s]+)\/([^/\s]+)$/)
if (githubPrefixed) {
return buildRemoteReference({ host: "github.com", segments: [githubPrefixed[1], githubPrefixed[2]] })
}

if (!cleaned.includes("://")) {
const scp = cleaned.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/)
if (scp) return buildRemoteReference({ host: scp[1], segments: parts(scp[2]), remote: cleaned })
if (!githubPrefixed) return undefined
return buildRemoteReference({ host: "github.com", segments: [githubPrefixed[1], githubPrefixed[2]] })
}

const direct = parts(cleaned)
if (direct.length >= 2 && hostLike(direct[0])) {
return buildRemoteReference({ host: direct[0], segments: direct.slice(1) })
}
function tryScpReference(cleaned: string): Reference | null | undefined {
const scp = cleaned.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/)
if (!scp) return undefined
return buildRemoteReference({ host: scp[1], segments: parts(scp[2]), remote: cleaned })
}

if (direct.length === 2) {
return buildRemoteReference({ host: "github.com", segments: direct })
}
function tryDirectReference(cleaned: string): Reference | null | undefined {
const direct = parts(cleaned)
if (direct.length >= 2 && hostLike(direct[0])) {
return buildRemoteReference({ host: direct[0], segments: direct.slice(1) })
}
if (direct.length === 2) {
return buildRemoteReference({ host: "github.com", segments: direct })
}
return undefined
}

function tryShorthandReference(cleaned: string): Reference | null | undefined {
if (cleaned.includes("://")) return undefined
const scpResult = tryScpReference(cleaned)
if (scpResult !== undefined) return scpResult
return tryDirectReference(cleaned)
}

function tryUrlReference(cleaned: string): Reference | null {
try {
const url = new URL(cleaned)
if (url.protocol === "file:") return buildFileReference({ url, remote: cleaned })
Expand All @@ -174,7 +183,18 @@ export function parseRepositoryReference(input: string) {
return null
}
}
export function parseRepositoryReference(input: string): Reference | null {
const cleaned = normalizeRepositoryInput(input)
if (!cleaned) return null

const githubPrefixed = tryGithubPrefixedReference(cleaned)
if (githubPrefixed !== undefined) return githubPrefixed

const shorthand = tryShorthandReference(cleaned)
if (shorthand !== undefined) return shorthand

return tryUrlReference(cleaned)
}
export function isFileRepositoryReference(reference: Reference): reference is FileReference {
return reference.protocol === "file:"
}
Expand Down
36 changes: 35 additions & 1 deletion packages/opencode/test/util/repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
UnsupportedLocalRepositoryError,
isFileRepositoryReference,
isRemoteRepositoryReference,
parseGitHubRemote,
parseRemoteRepositoryReference,
parseRepositoryReference,
repositoryCacheIdentity,
Expand Down Expand Up @@ -90,4 +91,37 @@ describe("util.repository", () => {
expect(() => validateRepositoryBranch("bad branch")).toThrow("Branch must contain only alphanumeric characters")
expect(() => validateRepositoryBranch("bad branch")).toThrow(InvalidRepositoryBranchError)
})
})

test("parses github: prefixed shorthand", () => {
const reference = parseRemoteRepositoryReference("github:owner/repo")

expect(reference).toMatchObject({
host: "github.com",
owner: "owner",
repo: "repo",
label: "owner/repo",
})
})

test("parses github remote shorthand into owner and repo", () => {
expect(parseGitHubRemote("git@github.com:owner/repo.git")).toEqual({ owner: "owner", repo: "repo" })
expect(parseGitHubRemote("https://github.com/owner/repo.git")).toEqual({ owner: "owner", repo: "repo" })

expect(parseGitHubRemote("owner/repo")).toBeNull()
expect(parseGitHubRemote("https://gitlab.com/owner/repo.git")).toBeNull()
expect(parseGitHubRemote("not a valid remote")).toBeNull()
})

test("uses custom github base url override when set", () => {
const original = process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = "https://github.internal.example.com"

try {
const reference = parseRemoteRepositoryReference("owner/repo")
expect(reference.remote).toBe("https://github.internal.example.com/owner/repo.git")
} finally {
if (original === undefined) delete process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
else process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = original
}
})
})
98 changes: 98 additions & 0 deletions tmp/opencode-provider-dynamic-o82f8c/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="en">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>LCOV - lcov.info - ../../../../tmp/opencode-provider-dynamic-o82f8c</title>
<link rel="stylesheet" type="text/css" href="../../../../../../gcov.css">
</head>

<body>

<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr><td class="title">LCOV - code coverage report</td></tr>
<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>

<tr>
<td width="100%">
<table cellpadding=1 border=0 width="100%">
<tr>
<td width="10%" class="headerItem">Current view:</td>
<td width="10%" class="headerValue"><a href="../../../../../../index.html" title="Click to go to top-level">top level</a> - ../../../../tmp/opencode-provider-dynamic-o82f8c</td>
<td width="5%"></td>
<td width="5%"></td>
<td width="5%" class="headerCovTableHead">Coverage</td>
<td width="5%" class="headerCovTableHead" title="Covered + Uncovered code">Total</td>
<td width="5%" class="headerCovTableHead" title="Exercised code only">Hit</td>
</tr>
<tr>
<td class="headerItem">Test:</td>
<td class="headerValue">lcov.info</td>
<td></td>
<td class="headerItem">Lines:</td>
<td class="headerCovTableEntryHi">100.0&nbsp;%</td>
<td class="headerCovTableEntry">1</td>
<td class="headerCovTableEntry">1</td>
</tr>
<tr>
<td class="headerItem">Test Date:</td>
<td class="headerValue">2026-08-28 11:41:16</td>
<td></td>
<td class="headerItem">Functions:</td>
<td class="headerCovTableEntryHi">-</td>
<td class="headerCovTableEntry">0</td>
<td class="headerCovTableEntry">0</td>
</tr>
<tr><td><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
</table>
</td>
</tr>

<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
</table>

<center>
<table width="80%" cellpadding=1 cellspacing=1 border=0>

<tr>
<td width="40%"><br></td>
<td width="15%"></td>
<td width="15%"></td>
<td width="15%"></td>
<td width="15%"></td>
</tr>

<tr>
<td class="tableHead" rowspan=2>File <span title="Click to sort table by file name" class="tableHeadSort"><img src="../../../../../../glass.png" width=10 height=14 alt="Sort by file name" title="Click to sort table by file name" border=0></span></td>
<td class="tableHead" colspan=4>Line Coverage <span title="Click to sort table by line coverage" class="tableHeadSort"><img src="../../../../../../glass.png" width=10 height=14 alt="Sort by line coverage" title="Click to sort table by line coverage" border=0></span></td>
</tr>
<tr>
<td class="tableHead" colspan=2> Rate</td>
<td class="tableHead"> Total</td>
<td class="tableHead"> Hit</td>
</tr>
<tr>
<td class="coverFile"><a href="provider.mjs.gcov.html" title="Click to go to file /workspaces/opencode/packages/core/../../../../tmp/opencode-provider-dynamic-o82f8c/provider.mjs">provider.mjs</a></td>
<td class="coverBar" align="center">
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="../../../../../../emerald.png" width=100 height=10 alt="100.0%"></td></tr></table>
</td>
<td class="coverPerHi">100.0&nbsp;%</td>
<td class="coverNumDflt">1</td>
<td class="coverNumDflt">1</td>
</tr>
<tr>
<td class="footnote" colspan=5>Note: 'Function Coverage' columns elided as function owner is not identified.</td>
</tr>
</table>
</center>
<br>

<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
<tr><td class="versionInfo">Generated by: <a href="https://github.com//linux-test-project/lcov">LCOV version 2.0-1</a></td></tr>
</table>
<br>

</body>
</html>
77 changes: 77 additions & 0 deletions tmp/opencode-provider-dynamic-o82f8c/provider.mjs.gcov.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="en">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>LCOV - lcov.info - ../../../../tmp/opencode-provider-dynamic-o82f8c/provider.mjs</title>
<link rel="stylesheet" type="text/css" href="../../../../../../gcov.css">
</head>

<body>

<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr><td class="title">LCOV - code coverage report</td></tr>
<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>

<tr>
<td width="100%">
<table cellpadding=1 border=0 width="100%">
<tr>
<td width="10%" class="headerItem">Current view:</td>
<td width="10%" class="headerValue"><a href="../../../../../../index.html" title="Click to go to top-level">top level</a> - <a href="index.html" title="Click to go to directory ../../../../tmp/opencode-provider-dynamic-o82f8c">../../../../tmp/opencode-provider-dynamic-o82f8c</a> - provider.mjs</td>
<td width="5%"></td>
<td width="5%"></td>
<td width="5%" class="headerCovTableHead">Coverage</td>
<td width="5%" class="headerCovTableHead" title="Covered + Uncovered code">Total</td>
<td width="5%" class="headerCovTableHead" title="Exercised code only">Hit</td>
</tr>
<tr>
<td class="headerItem">Test:</td>
<td class="headerValue">lcov.info</td>
<td></td>
<td class="headerItem">Lines:</td>
<td class="headerCovTableEntryHi">100.0&nbsp;%</td>
<td class="headerCovTableEntry">1</td>
<td class="headerCovTableEntry">1</td>
</tr>
<tr>
<td class="headerItem">Test Date:</td>
<td class="headerValue">2026-08-28 11:41:16</td>
<td></td>
<td class="headerItem">Functions:</td>
<td class="headerCovTableEntryHi">-</td>
<td class="headerCovTableEntry">0</td>
<td class="headerCovTableEntry">0</td>
</tr>
<tr><td><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
</table>
</td>
</tr>

<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
</table>

<table cellpadding=0 cellspacing=0 border=0>
<tr>
<td><br></td>
</tr>
<tr>
<td>
<pre class="sourceHeading"> Line data Source code</pre>
<pre class="source">
<span id="L1"><span class="lineNum"> 1</span> <span class="tlaGNC"> 39 : /* ../../../../tmp/opencode-provider-dynamic-o82f8c/provider.mjs empty or unreadable */</span></span>
</pre>
</td>
</tr>
</table>
<br>

<table width="100%" border=0 cellspacing=0 cellpadding=0>
<tr><td class="ruler"><img src="../../../../../../glass.png" width=3 height=3 alt=""></td></tr>
<tr><td class="versionInfo">Generated by: <a href="https://github.com//linux-test-project/lcov" target="_parent">LCOV version 2.0-1</a></td></tr>
</table>
<br>

</body>
</html>
Loading