Add git_repo_info tool (local repo + Forgejo metadata) #29
Labels
No labels
Agent
Ignore
Agent
In Progress
Agent
Planned
Agent
Planning
Agent
Ready
Agent
Triage
Compat
Breaking
Kind
Bug
Kind
Documentation
Kind
Enhancement
Kind
Feature
Kind
Security
Kind
Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
lucas/omp-deck#29
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Forgejo workflows often need the repo's default branch. Today the only code that reads it is the
basefallback inforgejo_create_pr(apps/server/src/forgejo/tools/prs.ts, around lines 339-343). Agents have no single call that returns the basic facts about the current project. Add a read-onlygit_repo_infotool that the agent can call at the start of a session. It always returns local git facts. When the workspace'soriginis on the configured Forgejo instance, it also returns Forgejo repo metadata. Auto-injecting this into Forgejo session prompts is deferred to the broader integration work.Plan
Context
The new git tool is
git_repo_info, which takes no parameters. It returns:The tool also gets a dedicated web renderer, and unattended Forgejo trigger sessions are allowed to call it.
Approach
Protocol type: in
packages/protocol/src/git-tools.ts, add:Re-export these from the protocol package index the same way as
GitStatusResult(check howgit-tools.tsis re-exported and follow that pattern).Expose the Forgejo context: in
apps/server/src/forgejo/tools/index.ts, addcontext: ForgejoToolContext | nulltoForgejoToolsResult:EMPTYsets it tonull.ctxobject.ForgejoToolContexttype from./shared.ts.Extend
ApiRepoinapps/server/src/forgejo/tools/api-types.tswith these fields:html_url: stringclone_url: stringssh_url: stringdescription: stringprivate: booleanfork: booleanarchived: booleanempty: booleanparent?: { full_name: string } | nullKeep
default_branchandfull_name. The existing use in prs.ts only readsdefault_branchand needs no change.Tool: create
apps/server/src/bridge/git-tools/repo-info.tsexportingbuildRepoInfoTool(cwd: string, forgejo: ForgejoToolContext | null): CustomTool<typeof schema>, whereschema = z.object({}). Copy the structure ofstatus.ts:guarded("git_repo_info", …), thenresolveRepo, thennotARepo, thenok(result). Description: "Repository facts for the session directory in one call: work-tree root, current branch, HEAD, upstream and ahead/behind, the default branch, and Forgejo repo metadata (URLs, visibility, fork parent) when origin is on the configured Forgejo instance. Call it at the start of repo work instead of guessing the default branch."The body:
runGit(repo.root, ["status", "--porcelain=v2", "--branch", "-z", "--untracked-files=no"], { readOnly: true, signal }). Feed the output to the existingparseStatusV2(./status-parse.ts) and take onlybranch,detached,oid,upstream,ahead,behind. If git exits non-zero, returnfail(gitErrorText(res, "git status failed")).runGit(repo.root, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], { readOnly: true, signal }). On exit code 0, strip the leadingorigin/from the trimmed output. Otherwise use null. Add this asoriginHeadBranch(root, signal): Promise<string | null>ingit-tools/repo.ts, next tocurrentBranch.forgejois non-null: callforgejo.client.json<ApiRepo>("GET", repoApiPath(forgejo.owner, forgejo.repo), { signal }), importingrepoApiPathfrom wherever prs.ts imports it.ok, map the response toGitRepoInfoForgejo, withdescription ?? ""andparent: data.parent?.full_name ?? null.{ error: failure.status > 0 ?${failure.status} ${failure.message}: failure.message }. Do not fail the tool.Promise.all.defaultBranchis itsdefault_branchanddefaultBranchSourceis"forgejo". Otherwise, if the origin HEAD lookup returned a branch, use that with"origin_head". Otherwise both arenull.Wiring:
buildGitTools(cwd: string)ingit-tools/index.tstobuildGitTools(cwd: string, forgejo: ForgejoToolContext | null), and appendbuildRepoInfoTool(cwd, forgejo)right afterbuildStatusTool(cwd).apps/server/src/bridge/in-process.ts(session create, around line 252/271, and resume, around line 334/348). Destructurecontext: forgejoCtxfrombuildForgejoTools(...)and pass it asbuildGitTools(cwd, forgejoCtx).grep -rn "buildGitTools(" apps/server/srcand make sure no other callers remain.Policy and prompt lists: add
"git_repo_info""git_status"inforgejoConversationPolicy.toolNames(apps/server/src/bridge/session-policy.ts), andGIT_BASH_RULE_MESSAGE(bridge/git-bash-rule.ts) and in the## Gitsection ofapps/server/src/orientation-store.ts.Leave idea mode alone: its blocklist only names mutating tools.
Web renderer: create
apps/web/src/components/tools/git/GitRepoInfoView.tsx, modelled onGitStatusView.tsx.GitShell, parse the result withextractJson<GitRepoInfoResult>, and add anisRepoInfoguard that checkstypeof root === "string"and the presence of theforgejokey. If the guard fails, fall back to<MaybeJsonBlock text={text} />.AheadBehind, or "no upstream"), followed by aShaChipforoidunless it is"(initial)", in which case show "no commits yet".Tagshowing the source (forgejo/origin/HEAD). If it is null, show "default branch unknown" intext-ink-3.rootas monospace muted text.htmlUrlwithfullNameas the label;Tags for private/public, fork (with "of {parent}"), archived, and empty;<details>withSUMMARY_CLASSlabelled "Clone URLs" that listscloneUrlandsshUrl.{ error }, render<Warn>Forgejo: {error}</Warn>. Fornull, render nothing.case "git_repo_info": return <GitRepoInfoView {...view} />;toGitTool.tsx. Inapps/web/src/lib/git-format.ts, addcase "git_repo_info": return "";tosummarizeGitArgs.GIT_FIXTURESinapps/web/src/preview-git.ts, following the existingjson(...)style:"tc-git-repo-info": full Forgejo data;"tc-git-repo-info-local":forgejo: null, sourceorigin_head;"tc-git-repo-info-forgejo-error".Tests: add
apps/server/src/bridge/git-tools/repo-info.test.ts, using real temp git repos the same way asforgejo/repo.test.ts. Cover:refs/remotes/origin/HEAD→origin/main(create it withgit update-ref refs/remotes/origin/main HEADandgit symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main). ExpectdefaultBranch: "main",defaultBranchSource: "origin_head",forgejo: null.jsonreturns{ ok: true, data: { default_branch: "dev", ... } }. Expect"dev"/"forgejo", which shows that Forgejo wins over origin/HEAD, andparentmapped fromparent.full_name.{ ok: false, status: 500, message: "boom" }. Expectforgejo: { error: "500 boom" }and the origin_head fallback.oid: "(initial)",defaultBranch: null,defaultBranchSource: null.Check the exact
ApiResultshape inforgejo/client.tsbefore building the stubs.Critical files & anchors
apps/server/src/bridge/git-tools/status.ts: the pattern for the new tool.apps/server/src/forgejo/tools/index.ts(buildForgejoTools,ForgejoToolsResult): exposescontext.apps/server/src/bridge/in-process.ts(around lines 252-272 and 334-349): the twobuildGitToolscallers.apps/server/src/bridge/session-policy.ts(toolNames): the trigger allowlist.apps/web/src/components/tools/git/GitStatusView.tsx: the renderer pattern (ui.tsx helpers).Verification
cd apps/server && bun test src/bridge/git-tools/repo-info.test.ts: all cases pass.package.json). It must pass, which confirms the protocol type is shared by server and web./workspace/omp-deck(origin is git.sclu1034.dev) and callgit_repo_info.defaultBranchSource: "forgejo",forgejo.fullName: "lucas/omp-deck", and the card showing the branch, default branch, and a Forgejo link.forgejo: null, and the card shows no Forgejo block.tc-git-repo-info*and check all three variants render.Assumptions