diff --git a/src/ClaudeDo.Data/Git/GitService.cs b/src/ClaudeDo.Data/Git/GitService.cs
index 789b26ca..bacd9855 100644
--- a/src/ClaudeDo.Data/Git/GitService.cs
+++ b/src/ClaudeDo.Data/Git/GitService.cs
@@ -414,6 +414,13 @@ public sealed class GitService
.ToList();
}
+ /// URL des Remotes , oder null wenn es keins gibt.
+ public async Task GetRemoteUrlAsync(string repoDir, string remote = "origin", CancellationToken ct = default)
+ {
+ var (exitCode, stdout, _) = await RunGitAsync(repoDir, ["remote", "get-url", remote], ct);
+ return exitCode == 0 && !string.IsNullOrWhiteSpace(stdout) ? stdout.Trim() : null;
+ }
+
public async Task IsMidMergeAsync(string repoDir, CancellationToken ct = default)
{
var (exitCode, stdout, _) = await RunGitAsync(repoDir, ["rev-parse", "--git-dir"], ct);
diff --git a/src/ClaudeDo.Worker/Config/WorkerConfig.cs b/src/ClaudeDo.Worker/Config/WorkerConfig.cs
index 9d0cc234..3eee5907 100644
--- a/src/ClaudeDo.Worker/Config/WorkerConfig.cs
+++ b/src/ClaudeDo.Worker/Config/WorkerConfig.cs
@@ -70,6 +70,10 @@ public sealed class WorkerConfig
[JsonPropertyName("ticket_api_base_url")]
public string? TicketApiBaseUrl { get; set; }
+ /// Teilstring der origin-URL, an dem ein Firmen-Repo erkannt wird. Nur per Hand editierbar.
+ [JsonPropertyName("company_remote_pattern")]
+ public string CompanyRemotePattern { get; set; } = "/_git/";
+
public static string DefaultConfigPath =>
Path.Combine(Paths.AppDataRoot(), "worker.config.json");
diff --git a/src/ClaudeDo.Worker/Git/CompanyRepo.cs b/src/ClaudeDo.Worker/Git/CompanyRepo.cs
new file mode 100644
index 00000000..08e0624a
--- /dev/null
+++ b/src/ClaudeDo.Worker/Git/CompanyRepo.cs
@@ -0,0 +1,16 @@
+using ClaudeDo.Data.Git;
+
+namespace ClaudeDo.Worker.Git;
+
+/// Erkennt Firmen-Repos am origin-Remote; das Muster kommt aus WorkerConfig.CompanyRemotePattern.
+public static class CompanyRepo
+{
+ /// True wenn das (nicht-leere) Muster enthält, ohne Groß-/Kleinschreibung.
+ public static bool Matches(string? remoteUrl, string? pattern) =>
+ !string.IsNullOrWhiteSpace(remoteUrl) && !string.IsNullOrWhiteSpace(pattern)
+ && remoteUrl.Contains(pattern, StringComparison.OrdinalIgnoreCase);
+
+ /// Liest origin live (kein Cache — ein Remote kann sich jederzeit ändern).
+ public static async Task IsCompanyAsync(GitService git, string workingDir, string? pattern, CancellationToken ct) =>
+ Matches(await git.GetRemoteUrlAsync(workingDir, "origin", ct), pattern);
+}
diff --git a/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs b/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs
new file mode 100644
index 00000000..32b9a4d7
--- /dev/null
+++ b/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs
@@ -0,0 +1,34 @@
+using ClaudeDo.Data.Git;
+using ClaudeDo.Worker.Git;
+using ClaudeDo.Worker.Tests.Infrastructure;
+
+namespace ClaudeDo.Worker.Tests.Git;
+
+public sealed class CompanyRepoTests : IDisposable
+{
+ private readonly GitRepoFixture _repo = new();
+ public void Dispose() => _repo.Dispose();
+
+ [Theory]
+ [InlineData("https://srv.local/Coll/Proj/_git/Repo", "/_git/", true)]
+ [InlineData("https://mika@srv.local/tfs/Coll/Proj/_git/Mein%20Repo.git", "/_git/", true)]
+ [InlineData("ssh://git@git.kuns.dev:22222/kuns/ClaudeDo.git", "/_git/", false)]
+ [InlineData(null, "/_git/", false)]
+ [InlineData("https://srv.local/Coll/Proj/_git/Repo", "", false)]
+ [InlineData("https://GITHUB.com/acme/repo.git", "github.com/acme", true)]
+ public void Matches(string? url, string pattern, bool expected)
+ => Assert.Equal(expected, CompanyRepo.Matches(url, pattern));
+
+ [Fact]
+ public async Task IsCompanyAsync_reads_origin()
+ {
+ if (!GitRepoFixture.IsGitAvailable()) return;
+ var git = new GitService();
+ Assert.False(await CompanyRepo.IsCompanyAsync(git, _repo.RepoDir, "/_git/", CancellationToken.None));
+
+ GitRepoFixture.RunGit(_repo.RepoDir, "remote", "add", "origin", "https://srv.local/Coll/Proj/_git/Repo");
+
+ Assert.True(await CompanyRepo.IsCompanyAsync(git, _repo.RepoDir, "/_git/", CancellationToken.None));
+ Assert.Equal("https://srv.local/Coll/Proj/_git/Repo", await git.GetRemoteUrlAsync(_repo.RepoDir));
+ }
+}