diff --git a/cli/src/__tests__/company-import-url.test.ts b/cli/src/__tests__/company-import-url.test.ts index abc96f7d..1f1548bd 100644 --- a/cli/src/__tests__/company-import-url.test.ts +++ b/cli/src/__tests__/company-import-url.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { isGithubShorthand, - isGithubUrl, + looksLikeRepoUrl, isHttpUrl, normalizeGithubImportSource, } from "../commands/client/company.js"; @@ -21,17 +21,17 @@ describe("isHttpUrl", () => { }); }); -describe("isGithubUrl", () => { +describe("looksLikeRepoUrl", () => { it("matches GitHub URLs", () => { - expect(isGithubUrl("https://github.com/org/repo")).toBe(true); + expect(looksLikeRepoUrl("https://github.com/org/repo")).toBe(true); }); - it("rejects non-GitHub HTTP URLs", () => { - expect(isGithubUrl("https://example.com/foo")).toBe(false); + it("rejects URLs without owner/repo path", () => { + expect(looksLikeRepoUrl("https://example.com/foo")).toBe(false); }); it("rejects local paths", () => { - expect(isGithubUrl("/tmp/my-company")).toBe(false); + expect(looksLikeRepoUrl("/tmp/my-company")).toBe(false); }); }); diff --git a/cli/src/commands/client/company.ts b/cli/src/commands/client/company.ts index 5d82abd0..571c1c1a 100644 --- a/cli/src/commands/client/company.ts +++ b/cli/src/commands/client/company.ts @@ -765,7 +765,7 @@ export function isHttpUrl(input: string): boolean { return /^https?:\/\//i.test(input.trim()); } -export function isGithubUrl(input: string): boolean { +export function looksLikeRepoUrl(input: string): boolean { try { const url = new URL(input.trim()); if (url.protocol !== "https:") return false; @@ -843,7 +843,7 @@ export function normalizeGithubImportSource(input: string, refOverride?: string) }); } - if (!isGithubUrl(trimmed)) { + if (!looksLikeRepoUrl(trimmed)) { throw new Error("GitHub source must be a GitHub or GitHub Enterprise URL, or owner/repo[/path] shorthand."); } if (!ref) { @@ -1218,10 +1218,10 @@ export function registerCompanyCommands(program: Command): void { | { type: "github"; url: string }; const treatAsLocalPath = !isHttpUrl(from) && await pathExists(from); - const isGithubSource = isGithubUrl(from) || (isGithubShorthand(from) && !treatAsLocalPath); + const isGithubSource = looksLikeRepoUrl(from) || (isGithubShorthand(from) && !treatAsLocalPath); if (isHttpUrl(from) || isGithubSource) { - if (!isGithubUrl(from) && !isGithubShorthand(from)) { + if (!looksLikeRepoUrl(from) && !isGithubShorthand(from)) { throw new Error( "Only GitHub URLs and local paths are supported for import. " + "Generic HTTP URLs are not supported. Use a GitHub or GitHub Enterprise URL (https://github.com/... or https://ghe.example.com/...) or a local directory path.", diff --git a/server/src/services/company-skills.ts b/server/src/services/company-skills.ts index a878a779..fae77e5f 100644 --- a/server/src/services/company-skills.ts +++ b/server/src/services/company-skills.ts @@ -984,7 +984,7 @@ async function readUrlSkillImports( ): Promise<{ skills: ImportedSkill[]; warnings: string[] }> { const url = sourceUrl.trim(); const warnings: string[] = []; - const isGitHubRepoUrl = (() => { try { + const looksLikeRepoUrl = (() => { try { const parsed = new URL(url); if (parsed.protocol !== "https:") return false; const h = parsed.hostname.toLowerCase(); @@ -992,7 +992,7 @@ async function readUrlSkillImports( const segments = parsed.pathname.split("/").filter(Boolean); return segments.length >= 2 && !parsed.pathname.endsWith(".md"); } catch { return false; } })(); - if (isGitHubRepoUrl) { + if (looksLikeRepoUrl) { const parsed = parseGitHubSourceUrl(url); const apiBase = gitHubApiBase(parsed.hostname); const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed); diff --git a/ui/src/components/NewProjectDialog.tsx b/ui/src/components/NewProjectDialog.tsx index b3c58e6a..367384ac 100644 --- a/ui/src/components/NewProjectDialog.tsx +++ b/ui/src/components/NewProjectDialog.tsx @@ -118,7 +118,7 @@ export function NewProjectDialog() { const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value); - const isGitHubRepoUrl = (value: string) => { + const looksLikeRepoUrl = (value: string) => { try { const parsed = new URL(value); if (parsed.protocol !== "https:") return false; @@ -155,7 +155,7 @@ export function NewProjectDialog() { setWorkspaceError("Local folder must be a full absolute path."); return; } - if (repoUrl && !isGitHubRepoUrl(repoUrl)) { + if (repoUrl && !looksLikeRepoUrl(repoUrl)) { setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL."); return; } diff --git a/ui/src/components/ProjectProperties.tsx b/ui/src/components/ProjectProperties.tsx index 87c1431d..0e0ea33e 100644 --- a/ui/src/components/ProjectProperties.tsx +++ b/ui/src/components/ProjectProperties.tsx @@ -343,7 +343,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value); - const isGitHubRepoUrl = (value: string) => { + const looksLikeRepoUrl = (value: string) => { try { const parsed = new URL(value); if (parsed.protocol !== "https:") return false; @@ -431,7 +431,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa persistCodebase({ repoUrl: null }); return; } - if (!isGitHubRepoUrl(repoUrl)) { + if (!looksLikeRepoUrl(repoUrl)) { setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL."); return; }