Resolve Face source repos by canonical upstream

This commit is contained in:
Meta Vens 2026-07-13 12:42:58 +01:00
commit cf526a7c78
2 changed files with 45 additions and 5 deletions

View file

@ -244,18 +244,53 @@ function readTrackedRepoNames(archivePath, requestedRepoNames) {
function readAvailableRepos(sourceRepoRoot) {
const lookup = new Map();
const canonicalCandidates = new Map();
for (const entry of readdirSync(sourceRepoRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) {
continue;
}
lookup.set(entry.name.toLowerCase(), resolve(sourceRepoRoot, entry.name));
const repoPath = resolve(sourceRepoRoot, entry.name);
lookup.set(entry.name.toLowerCase(), repoPath);
const canonicalRepoName = readCanonicalGameCultRepoName(repoPath);
if (!canonicalRepoName) {
continue;
}
const key = canonicalRepoName.toLowerCase();
const candidates = canonicalCandidates.get(key) ?? [];
candidates.push({ localRepoName: entry.name, repoPath, canonicalRepoName });
canonicalCandidates.set(key, candidates);
}
for (const [key, candidates] of canonicalCandidates) {
candidates.sort((left, right) => {
const leftExact = left.localRepoName.toLowerCase() === left.canonicalRepoName.toLowerCase() ? 0 : 1;
const rightExact = right.localRepoName.toLowerCase() === right.canonicalRepoName.toLowerCase() ? 0 : 1;
return leftExact - rightExact || left.localRepoName.localeCompare(right.localRepoName);
});
lookup.set(key, candidates[0].repoPath);
}
return lookup;
}
function readCanonicalGameCultRepoName(repoPath) {
try {
const originUrl = execFileSync(
"git",
["-C", repoPath, "remote", "get-url", "origin"],
{ encoding: "utf8", windowsHide: true, stdio: ["ignore", "pipe", "ignore"] },
).trim();
const match = originUrl.match(/github\.com[:/]GameCult\/([^/]+?)(?:\.git)?$/i);
return match?.[1] || null;
} catch {
return null;
}
}
function inspectRepoActivity({ repoName, repoPath, sinceIso, maxCommits, cursorEntry }) {
if (!repoPath) {
return {

View file

@ -4773,13 +4773,18 @@ function renderRepoFaceRepoActivitySurface(
identity: RepoDiscordIdentity,
config: ReturnType<typeof loadConfig>,
): string {
const sourceRepoName = getRepoFaceSourceRepoName(identity);
if (!sourceRepoName) {
return "- This Persona has no source repository configured; no repo activity was requested.";
}
const statePath = resolveRepoFaceStatePath(identity, config.storageRoot);
const result = spawnSync(
process.execPath,
[
resolve("scripts", "export-recent-repo-activity.mjs"),
"--repos",
identity.repoName,
sourceRepoName,
"--state-path",
statePath,
"--read-only",
@ -4800,7 +4805,7 @@ function renderRepoFaceRepoActivitySurface(
if (result.status !== 0) {
const detail = `${result.stdout}\n${result.stderr}`.trim().slice(-600);
return [
`- Recent ${identity.repoName} activity could not be read for this turn.`,
`- Recent ${sourceRepoName} activity could not be read for this turn.`,
detail ? `- Reader error: ${collapseWhitespace(detail, 500)}` : "- Reader error: no diagnostic output.",
"- Do not claim current repo state from stale memory; use source/history tools before making fresh claims.",
].join("\n");
@ -4809,10 +4814,10 @@ function renderRepoFaceRepoActivitySurface(
try {
const parsed = JSON.parse(result.stdout) as { digest?: unknown };
const digest = typeof parsed.digest === "string" ? parsed.digest.trim() : "";
return digest || `- No recent ${identity.repoName} activity was reported.`;
return digest || `- No recent ${sourceRepoName} activity was reported.`;
} catch {
return [
`- Recent ${identity.repoName} activity output was not parseable.`,
`- Recent ${sourceRepoName} activity output was not parseable.`,
`- Raw output: ${collapseWhitespace(result.stdout, 500)}`,
"- Do not claim current repo state from stale memory; use source/history tools before making fresh claims.",
].join("\n");