# RepoForge Agent Guide
RepoForge is a library for local Git archive discovery and safe fast-forward
refresh. It does not call GitHub or Bitbucket APIs, list remote repositories,
manage credentials, clone through a dedicated API, write manifests, or provide
a CLI/dry-run mode. The caller owns those operations and policies.
## Targets And Credentials
- `parse_git_remote_slug` recognizes GitHub (`github.com`) and Bitbucket Cloud
(`bitbucket.org`) HTTPS, HTTP, SSH, SCP-like SSH, `git+ssh`, and optional-port
remotes. It returns `(GitProvider, owner/repo)`; Bitbucket's owner is a
workspace. Deep browser paths and lookalike hosts are rejected.
- Use `parse_github_remote_slug` or `parse_bitbucket_remote_slug` when the
provider is already known. Use `split_owner_repo_slug` for an explicit
`owner/repo` target. These functions do not validate remote existence.
- RepoForge reads no token environment variables. A caller's GitHub/Bitbucket
API client must define its own token contract. Git subprocesses inherit the
process environment and normal Git credential/SSH configuration.
- Never put tokens in clone URLs, output, errors, or manifests. Prefer credential
helpers or SSH agents. Do not assume `GITHUB_TOKEN`, `GH_TOKEN`, or a
Bitbucket token is consumed by RepoForge or automatically used by Git.
```rust
use repoforge::{GitProvider, parse_git_remote_slug, split_owner_repo_slug};
assert_eq!(
parse_git_remote_slug("git@bitbucket.org:workspace/repo.git"),
Some((GitProvider::Bitbucket, "workspace/repo".to_string())),
);
assert_eq!(split_owner_repo_slug("octocat/Hello-World"),
Some(("octocat", "Hello-World")));
```
## Autonomous Workflow
1. Parse and validate the requested provider/slug. Reject unsupported hosts or
malformed slugs instead of guessing.
2. List remote repositories with a separately configured provider client when
needed. Bound pagination, retries, concurrency, and result count. RepoForge
has no remote-list API.
3. Discover local repositories with `discover_git_archives` or
`discover_git_archives_for_owner`. A missing root returns an empty vector; a
path that exists but is not a directory returns `OutputNotDirectory`.
4. Build a deterministic plan before mutation: sorted target slug, provider,
destination, and action (`skip`, `clone`, `refresh`, or `error`). A dry run
prints this plan and must not call clone or refresh operations.
5. For clone actions, construct a canonical HTTPS URL with
`git_web_url_for_slug`; never reuse an untrusted raw URL for display. Invoke
Git through `run_git_capture` or the caller's process layer and check
`Output::status`. RepoForge has no clone result type.
6. Refresh discovered candidates with `refresh_git_archives` or
`refresh_one_git_archive`. Refresh checks `git status --porcelain`, refuses
dirty worktrees, then runs `git pull --ff-only`.
7. Verify successful clone/refresh destinations with `is_git_worktree`,
`git_remote_origin_url`, `parse_git_remote_slug`, and `git_head`. These are
structural checks, not content-integrity or signature verification.
8. Write caller-owned manifests only after verification. Record non-secret,
stable data such as provider, slug, canonical web URL, destination, HEAD,
action, and outcome. Use an atomic replace and do not claim failed work as
successful. RepoForge never reads or writes manifests.
## Planning, Existing Paths, And Limits
- Discovery is recursive, sorted, and stops below each worktree. It skips
`.git`, `.thesa`, and `target`; filters match slug or path case-insensitively.
- Implement skip-existing in the caller. If the planned destination is an
existing worktree for the same parsed provider/slug, report `skip` (or plan a
separate refresh when explicitly requested). Never clone over it.
- Treat an existing non-worktree, unreadable remote, or mismatched remote as a
conflict, not as skippable success. Do not delete, reset, or repurpose it.
- `refresh_git_archives(&archives, concurrency)` uses batches of at most
`concurrency.max(1)` threads. Choose a conservative positive bound. Bound
provider listing and clone concurrency separately; RepoForge does not do so.
- Git output is captured, not streamed. Emit concise per-repository outcomes
and aggregate counts; cap displayed failures/output while preserving the full
structured result or manifest privately. Never print credential-bearing URLs.
```rust,no_run
use std::path::Path;
use repoforge::{discover_git_archives, refresh_git_archives};
let root = Path::new("./archives");
let archives = discover_git_archives(root, None)?;
// Dry run: report the sorted `archives` plan and stop here.
let summary = refresh_git_archives(&archives, 4);
println!("updated={} unchanged={} failed={}",
summary.updated, summary.unchanged, summary.failed.len());
# Ok::<(), repoforge::RepoForgeError>(())
```
One possible caller-owned clone step, after planning and destination checks:
```rust,no_run
use std::path::Path;
use repoforge::{GitProvider, git_web_url_for_slug, run_git_capture};
let root = Path::new("./archives");
let destination = "octocat/Hello-World";
let url = git_web_url_for_slug(GitProvider::GitHub, destination)
.expect("validated owner/repo slug");
let output = run_git_capture(root, &["clone", "--", &url, destination])?;
if !output.status.success() {
// Convert captured, redacted stderr/status into the caller's failure type.
}
# Ok::<(), repoforge::RepoForgeError>(())
```
## Errors And Safety
- APIs returning `repoforge::Result<T>` can report `OutputNotDirectory`,
`MissingGit`, `DirtyWorktree`, `GitCommandFailed`, or `Io`.
- `refresh_git_archives` does not return `Result`; inspect every
`GitRefreshSummary`, especially `failed`, `has_failures()`, and `is_success()`.
Use `refresh_one_git_archive` when the typed error is required.
- `git_remote_origin_url` returns `None` when Git cannot read the origin; it
does not distinguish absence from command failure.
- `git_head` returns `Ok(None)` for an unreadable/unborn HEAD.
- RepoForge never deletes, resets, checks out, rebases, merges, stashes, or
force-pulls. Preserve this contract in autonomous callers and require explicit
operator policy for any broader mutation.
The same text is available at runtime as `repoforge::AGENT_GUIDE`.