diff --git a/.agents/skills/address-review/SKILL.md b/.agents/skills/address-review/SKILL.md new file mode 100644 index 000000000..4779985ae --- /dev/null +++ b/.agents/skills/address-review/SKILL.md @@ -0,0 +1,154 @@ +--- +name: address-review +description: Address review comments on a GitHub pull request +--- + +Address review comments on a GitHub pull request. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +The argument is a PR number (e.g. `/address-review 98`). If no number is given, ask the user. + +## Phase 1: Fetch and display comments + +1. Save the current branch name: `git rev-parse --abbrev-ref HEAD` +2. Checkout the PR branch: `gh pr checkout --detach` +3. Fetch review data (pick one): + + **REST (simple):** Lists inline review comments; it does **not** include per-thread `resolved` status, so you cannot rely on it alone to skip resolved threads. + +``` +gh api repos/{owner}/{repo}/pulls//comments --paginate +``` + +**GraphQL (for filtering resolved threads):** Returns `reviewThreads` with `isResolved`, `isOutdated`, paths/lines, and nested comments—use this when step 5 must honor “already resolved.” Paginate with `cursor` (use `null` or omit for the first page; then pass `reviewThreads.pageInfo.endCursor` while `hasNextPage` is true). + +``` +gh api graphql -f query=' +query($owner:String!, $repo:String!, $number:Int!, $cursor:String) { + repository(owner:$owner, name:$repo) { + pullRequest(number:$number) { + reviewThreads(first:100, after:$cursor) { + pageInfo { hasNextPage endCursor } + nodes { + isResolved + isOutdated + path + line + originalLine + comments(first:100) { + nodes { + databaseId + body + author { login } + createdAt + url + } + } + } + } + } + } +}' -F owner={owner} -F repo={repo} -F number= -F cursor= +``` + +First page: pass JSON `null` for `cursor` (see `gh help api` / your shell for how `gh` expects null). Later pages: set `cursor` to the previous response’s `reviewThreads.pageInfo.endCursor` until `hasNextPage` is false. + +4. Group comments by file. For each comment, extract: `id`, `path`, `line` (or `original_line`), `body`, `user.login`, `in_reply_to_id` (to detect threads). If you used GraphQL, map `databaseId` to `id` and thread fields as needed. +5. Filter out threads that are already resolved (when using GraphQL method). +6. Present a numbered summary to the programmer: + +``` +PR # unaddressed review comments + + 1. : — @: + 2. : — @: + ... +``` + +If there are no unaddressed comments, say so and return to the original branch. + +## Phase 2: Walk through each comment + +For each comment in order, show: + +- The full comment body +- The file path and line number +- The relevant code context (read ~10 lines around the commented line from the actual file) +- The author who left the comment + +Then ask the programmer what to do using AskUserQuestion with these options: + +- **Fix** — Make a code change to address the comment. After the programmer picks this, analyze the comment and the surrounding code, propose a fix, and apply it with Edit. Show the programmer what changed and confirm before moving to the next comment. +- **Reply** — Draft a reply to the comment. Write a concise, human-sounding response (see tone rules below). Show the exact text to the programmer. They can modify it. Queue the reply for posting in Phase 3. +- **Skip** — Move to the next comment without action. + +If the programmer chooses Fix, apply the change and move on. If they choose Reply, queue it. Track all code changes and queued replies separately. + +## Phase 3: Commit, push, and post replies + +After walking through all comments: + +**Code changes:** +If any code fixes were made, run the /commit skill to create a commit with the changes, then push: + +``` +git push +``` + +**Replies:** +If there are queued replies, show all of them in a final summary: + +``` +Queued replies: + +1. : — reply to @: + "" + +2. : — reply to @: + "" +``` + +Ask the programmer for final confirmation before posting. They can modify any reply text at this point. + +Post each reply as a separate API call: + +``` +gh api repos/{owner}/{repo}/pulls//comments//replies \ + --method POST \ + -f body="" +``` + +After all replies are posted, return to the original branch: + +``` +git checkout +``` + +## Reply tone and style + +Follow these rules strictly when drafting replies: + +- Write as a human developer, not an AI. Never mention AI, automation, or tools. +- No markdown headings (#, ##, ###) in replies. Plain text and inline formatting only. +- Keep replies concise — 1-3 sentences. Get to the point. +- Conversational, collaborative tone: "good catch, fixed in the latest push", "I think this is fine because...", "yeah, updated this to use X instead" +- Be direct but not dismissive. If you disagree with a comment, explain briefly why. +- No bullet lists. Keep it flowing as natural text. +- No emojis. +- The programmer gets final say on every reply before it is posted. + +## Rules + +- Never post anything to GitHub without explicit programmer confirmation +- Never modify code without programmer approval +- Show exact reply text before queuing it +- The programmer can modify any reply at any point +- Always checkout with `--detach` to avoid creating local branches +- Never push while in detached state. Before any push, explicitly checkout the target branch after programmer confirmation +- After everything is done, return to the original branch +- No emojis in any output +- No markdown headings in any output or posted content +- If an API call fails, show the error and ask the programmer how to proceed + +$ARGUMENTS diff --git a/.agents/skills/commit/SKILL.md b/.agents/skills/commit/SKILL.md new file mode 100644 index 000000000..bf46ff384 --- /dev/null +++ b/.agents/skills/commit/SKILL.md @@ -0,0 +1,65 @@ +--- +name: commit +description: Create a conventional commit for the current staged/unstaged changes +--- + +Create a git commit for the current staged/unstaged changes using Conventional Commits. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +Rules: + +1. Run `git status` and `git diff` to understand what changed +2. Stage relevant files (prefer specific files over `git add -A`) +3. Write a commit message following Conventional Commits format +4. Use `--signoff` to sign off using the committer's git config (do NOT hardcode any name/email) +5. Do NOT add Co-Authored-By or any other trailers beyond Signed-off-by +6. If there are no changes, say so and stop + +Commit format: + +``` +[optional scope]: +``` + +Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore + +Scope (optional): a noun describing the affected area in parentheses. +Common scopes: config, api, proxy, db, e2e, docker, ci. + +Subject line rules: + +- Imperative mood ("add", not "added" or "adds") +- Do NOT capitalize the first letter after the type prefix +- Do NOT end with a period +- Keep under 72 characters total + +For breaking changes, add `!` after the type/scope: + +``` +feat(config)!: rename queue config key +``` + +Examples of good messages: + +- "feat(api): add support for rejection reason" +- "fix(config): handle invalid regex on commitConfig" +- "refactor(ts): remove any/as usages in push-actions" +- "test(proxy): add tests for Git hosts other than GitHub" +- "docs: update architecture diagram in Architecture.md" +- "build: bump express to 5.2.1" +- "chore: remove unused import in services/push.ts" + +Commit command: + +``` +git commit --signoff -m "[scope]: " +``` + +Do NOT: + +- Use long multi-line messages for simple changes +- Add Co-Authored-By trailers +- Use past tense ("added", "fixed") +- Hardcode any author name or email +- Omit the type prefix diff --git a/.agents/skills/docs/SKILL.md b/.agents/skills/docs/SKILL.md new file mode 100644 index 000000000..ccbb3473c --- /dev/null +++ b/.agents/skills/docs/SKILL.md @@ -0,0 +1,47 @@ +--- +name: docs +description: Update existing project documentation to reflect the current state of the branch +--- + +Update existing project documentation to reflect the current state of the branch. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +Steps: + +1. Run `git diff main...HEAD --stat` and `git log main..HEAD --oneline` to understand what changed on this branch +2. Read the changed files to understand what was added, removed, or modified +3. Read the current documentation files: `README.md`, `docs/Architecture.md`, `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md` +4. Identify documentation that is now outdated or missing based on the branch changes +5. Apply minimal, targeted edits to bring docs in line with the code + +What to update: + +- **Architecture.md** — new push actions, configuration parameters, plugins, build steps, authentiction methods +- **README.md** — project description, features list, usage examples +- **AGENTS.md** — canonical project architecture, invariants, testing commands, and AI-agent workflow guidance +- **CLAUDE.md** — Claude-specific entry point that should remain aligned with `AGENTS.md` +- **.github/copilot-instructions.md** — short Copilot-compatible summary of the highest-priority repo rules + +Principles: + +- **Minimal changes only.** Do not rewrite sections that are already accurate. Edit the smallest possible region. +- **Capture important information.** New commands, config keys, classes, architecture changes, and breaking changes must be documented. +- **Keep it concise.** Prefer inline descriptions over new subsections. Use tables and bullet points. Avoid verbose prose. +- **Match existing style.** Follow the formatting, tone, and structure already in each file. Do not add headings, sections, or patterns that don't already exist. +- **One edit per concern.** If a section needs updating, make one focused edit rather than rewriting the whole section. +- **Skip trivial changes.** Internal refactors, renames, or implementation details that don't affect the public interface do not need doc updates. + +After editing, report what was updated: + +- List each file edited and a one-line description of the change +- If no docs needed updating, say "Documentation is up to date" and stop + +Do NOT: + +- Create new documentation files +- Add sections or headings that don't already exist +- Rewrite large blocks of text when a small edit suffices +- Document internal implementation details +- Add emojis, badges, or decorative elements +- Update docs for changes that don't affect user-facing behavior diff --git a/.agents/skills/implement/SKILL.md b/.agents/skills/implement/SKILL.md new file mode 100644 index 000000000..5cf96e0f9 --- /dev/null +++ b/.agents/skills/implement/SKILL.md @@ -0,0 +1,113 @@ +--- +name: implement +description: Implement a GitHub issue end-to-end: fetch, plan, code with approval, commit incrementally, and generate a PR summary +--- + +Implement a GitHub issue end-to-end: fetch the issue, plan, code with approval, commit incrementally, and generate a PR summary. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +The argument is an issue number (e.g. `/implement 42`) or a full GitHub URL (e.g. `/implement https://github.com/finos/git-proxy/issues/42`). If no argument is given, ask the user. + +## Phase 1: Fetch issue + +1. Parse the argument: + - If it is a full URL like `https://github.com/{owner}/{repo}/issues/{number}`, extract `{owner}/{repo}` and `{number}`. + - If it is just a number, use the current repo (run `gh repo view --json nameWithOwner -q .nameWithOwner` to get it). +2. Fetch issue details: + ``` + gh issue view --repo --json title,body,labels,assignees,comments + ``` +3. Display a summary to the programmer: + - Issue number and title + - Labels (if any) + - Body (truncated to ~40 lines if longer) + - Number of comments and any noteworthy discussion points +4. Ask the programmer to confirm this is the right issue before proceeding. Use AskUserQuestion with options: "Proceed", "Show full issue body", "Cancel". + +## Phase 2: Branch setup + +1. List all configured remotes: `git remote -v` +2. If there is more than one remote, ask the programmer which remote to use as upstream using AskUserQuestion (list the remote names as options). + If there is only one remote, use it automatically. +3. Fetch and update main from the chosen remote: + ``` + git fetch + git checkout main + git pull main + ``` +4. Create a new branch from main. The branch name must follow this convention: + - Format: `/` where `` is a Conventional Commits type (`feat`, `fix`, `refactor`, `docs`, `chore`, etc.) and `` is a kebab-case summary derived from the issue title (3-5 words max). + - Examples: `feat/dynamic-allocation-support`, `fix/event-watcher-reconnect`, `refactor/pod-spec-converter` + - Pick the type based on the issue labels and description (e.g. a bug report maps to `fix/`, a feature request to `feat/`). + ``` + git checkout -b / + ``` +5. Confirm to the programmer: "Created branch `` from `/main`." + +## Phase 3: Plan + +1. Based on the issue description, explore the codebase to understand the relevant code paths. Use subagents to search for files, classes, and patterns referenced in or implied by the issue. +2. Create an implementation plan with numbered steps. Each step should be a logical, committable unit of work. The plan must include: + - A 1-2 sentence summary of the issue + - Numbered steps, where each step has a title, a description of what to change, and the affected file paths +3. Save the plan to `plans/implement-.md` using this format: + + ``` + ## Issue #: + + <1-2 sentence summary of what the issue asks for> + + ## Steps + + 1. <step title> + - <what to change and where> + - Files: <path/to/file.ts> + + 2. <step title> + - <what to change and where> + - Files: <path/to/file.ts, path/to/other.ts> + ``` + +4. Show the full plan to the programmer for approval. +5. Ask the programmer using AskUserQuestion with options: "Approve plan", "Modify plan", "Cancel". +6. If the programmer wants modifications, iterate on the plan until approved. + +## Phase 4: Execute step by step + +For each step in the approved plan: + +1. Announce the step: "Step <n>/<total>: <step title>" +2. Make the code changes for that step +3. If the step involves logic changes, run tests (`npm run test`) and show the result +4. Show the programmer a brief summary of what changed (key files and the nature of the change) +5. Ask the programmer using AskUserQuestion with options: "Commit this step", "Revise changes", "Skip this step", "Stop here". +6. If the programmer picks "Commit this step", run the /commit skill to commit the changes +7. If the programmer picks "Revise changes", iterate until they are satisfied +8. If the programmer picks "Skip this step", move to the next step without committing +9. If the programmer picks "Stop here", skip all remaining steps and jump to Phase 5 + +After each commit, briefly confirm the commit was made and move to the next step. + +## Phase 5: Summary + +1. After all steps are complete (or the programmer stops early): + - Run the /summary skill to generate a PR description based on all commits made during this session + - Show the summary to the programmer +2. Do NOT create a PR or push. Just present the summary for the programmer to use when they are ready. +3. If no commits were made (e.g. the programmer cancelled early), skip the summary and say so. + +## Rules + +- Never make code changes without programmer approval +- Always show what changed after each step before committing +- The programmer can modify, skip, reorder, or stop steps at any point +- Save the plan file before starting execution so the programmer has a reference +- Each commit should be a logical unit (one step = one commit, unless the step is trivial) +- If the issue is from a different repo (not the current one), fetch it via the full URL but make changes in the current repo +- No emojis in any output +- Do not create a PR or push to remote — only local commits and a summary +- If a step requires running tests, run them and show results before committing +- Use subagents for codebase exploration and code changes; keep the main flow focused on coordination and programmer interaction + +$ARGUMENTS diff --git a/.agents/skills/issue/SKILL.md b/.agents/skills/issue/SKILL.md new file mode 100644 index 000000000..5d8814674 --- /dev/null +++ b/.agents/skills/issue/SKILL.md @@ -0,0 +1,38 @@ +--- +name: issue +description: Create a GitHub issue markdown file from a bug report, feature request, or discussion context +--- + +Create a GitHub issue markdown file from the details provided by the user. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +The user will describe a bug, feature request, or discussion context (e.g. Slack conversations, error logs, reproduction steps). Your job is to turn that into a well-structured issue and save it to `plans/`. + +Steps: + +1. Read the user's input to understand the problem, who reported it, and any logs or reproduction steps provided +2. Investigate the codebase to identify relevant code paths, pinpoint where the issue likely originates, and gather context that would help a contributor understand the problem +3. Write a concise GitHub issue markdown file and save it to `plans/issue-<short-slug>.md` + +Issue format: + +- Start with `## <title>` as the first line (this becomes the GitHub issue title) +- `### Problem` — 2-3 sentences explaining the issue and its impact +- `### Steps to Reproduce` — numbered list (if applicable) +- `### Expected Behavior` — 1-2 sentences +- `### Actual Behavior` — include relevant log snippets in code blocks, keep them short (trim stack traces to the key lines) +- `### Potential Root Cause` — based on your codebase investigation, explain where the issue likely lives. Link to source code using upstream GitHub URLs: `https://github.com/finos/git-proxy/blob/main/...#L<start>-L<end>`. +- `### Affected Files` — table with file links and one-line role descriptions +- `### Additional Context` — bullet points for version info, related code paths, or anything else useful + +Style rules: + +- Keep it concise — the whole issue should be under 80 lines +- Use code blocks sparingly — only for key log lines and small code snippets +- Do not propose fixes or implementation approaches +- Do not add Labels, Description, or Title as separate sections — the `##` heading is the title +- Write for a contributor who knows the project but hasn't seen this specific bug +- No emojis + +$ARGUMENTS diff --git a/.agents/skills/lint/SKILL.md b/.agents/skills/lint/SKILL.md new file mode 100644 index 000000000..25f042f66 --- /dev/null +++ b/.agents/skills/lint/SKILL.md @@ -0,0 +1,21 @@ +--- +name: lint +description: Check and fix code formatting using ESLint and Prettier +--- + +Check and fix code formatting using ESLint and Prettier. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +Steps: + +1. Run `npm run format:check` to see if there are formatting violations +2. If violations are found, run `npm run format` to auto-fix them +3. Run `npm run lint` to see if there are linting errors +4. If errors are found, run `npm run lint:fix` to auto-fix them +5. After applying, run `git diff --stat` to show what files were reformatted +6. Summarize the changes (which files, what kind of formatting was fixed) + +If no violations are found, say so and stop. + +Do NOT commit the formatting changes — just apply and report. diff --git a/.agents/skills/summary/SKILL.md b/.agents/skills/summary/SKILL.md new file mode 100644 index 000000000..979c4b778 --- /dev/null +++ b/.agents/skills/summary/SKILL.md @@ -0,0 +1,51 @@ +--- +name: summary +description: Generate a concise implementation summary for a PR description +--- + +Generate a concise implementation summary for a PR description. + +Read `AGENTS.md` first. It is the canonical project guide for this repository. + +Steps: + +1. First, run the `/docs` skill to ensure documentation is up to date with the branch changes +2. Run `git diff main...HEAD --stat` and `git log main..HEAD --oneline` to understand all changes on this branch +3. Read the changed files to understand what was implemented and why +4. Get the current branch name: `git rev-parse --abbrev-ref HEAD` +5. Write a summary suitable for a GitHub PR description +6. Save the raw markdown summary (without the wrapping code fence) to `plans/<branch-name>-summary.md`, replacing any `/` in the branch name with `-` + +Summary format rules: + +- The summary must be GitHub-flavored markdown that renders nicely in a PR description +- Do NOT use any headings (`#`, `##`, `###`, etc.) — structure the summary with bold labels and line breaks instead +- Use `**bold**` for section labels (e.g., `**What:**`, `**Why:**`, `**Changes:**`) +- Use backtick-wrapped inline code for file names, config keys, commands, and identifiers +- Use markdown bullet points (`-`) for listing changes +- Use numbered lists for verification steps +- Use `[text](url)` for hyperlinks when referencing issues or external resources +- Start with `**What:**` — a one-line statement explaining the change +- Follow with `**Why:**` — 2-3 sentences max explaining the motivation +- Include `**Changes:**` — key changes as bullet points (no nested bullets) +- If there are new tests, add `**Tests:**` with one line describing coverage +- End with `**How to verify:**` — concrete steps as a numbered list +- Keep the total summary under 30 lines +- Do not repeat file paths or class names unnecessarily +- Focus on behavior changes, not implementation details +- Write in present tense, active voice + +Output rules: + +- Do NOT print the summary to the conversation +- Only save it to the file and tell the user where it was saved: "Summary saved to `plans/<branch-name>-summary.md`" + +Do NOT: + +- Use headings (`#`, `##`, `###`) anywhere in the summary +- Use tables or badges +- List every single file changed +- Include generic boilerplate like "This PR adds..." +- Add emojis +- Over-explain things that are obvious from the diff +- Print the summary content in the conversation output diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..185046c22 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,15 @@ +# GitProxy Copilot Instructions + +Read `AGENTS.md` for the full project guide. Use the rules below as the highest-priority summary for Copilot surfaces that truncate long instruction files. + +- Preserve the GitProxy flow: Git HTTP proxy -> ordered action chain -> service API -> audit trail -> approval gate. +- A push must never reach the real remote unless it is explicitly approved or auto-approved by policy. +- Do not bypass `blockForAuth`, waiting-authorization checks, or audit logging. +- Keep proxy, service, config, and UI responsibilities separate. Do not place policy logic ad hoc in request handlers. +- The action chain is the core abstraction. Put reusable enforcement logic in processors, organization-specific logic in plugins, and UI/auth state changes in the service layer. +- Processor order matters. If logic depends on cloned repositories, diffs, or earlier artifacts, place it accordingly. +- Processors must be idempotent, use the `Action` object instead of shared mutable state, and preserve audit traceability. +- Configuration changes must update schema validation, keep backward-compatible defaults, and avoid weakening security by default. +- Authentication strategies apply to UI and approval workflows. Git push identity is derived from commit metadata, not interactive auth. +- When changing proxy/processors, test success paths, rejection paths, and audit logging. When changing config, test invalid values and defaults. +- Use the standard repo commands: `npm run build`, `npm run test`, `npm run test:e2e`, `npm run lint`, `npm run format:check`. diff --git a/.gitignore b/.gitignore index c6076f1af..f38f3b36a 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ web_modules/ # dotenv environment variables file .env .env.test +.claude/worktrees/ # parcel-bundler cache (https://parceljs.org/) .cache @@ -275,3 +276,6 @@ website/.docusaurus # Generated from testing /test/fixtures/test-package/package-lock.json + +# Agentic AI generated files +/plans diff --git a/.opencode/commands/address-review.md b/.opencode/commands/address-review.md new file mode 100644 index 000000000..272b42b93 --- /dev/null +++ b/.opencode/commands/address-review.md @@ -0,0 +1,5 @@ +--- +description: Address review comments on a GitHub PR +--- + +Follow @.agents/skills/address-review/SKILL.md. diff --git a/.opencode/commands/commit.md b/.opencode/commands/commit.md new file mode 100644 index 000000000..3b6431c2c --- /dev/null +++ b/.opencode/commands/commit.md @@ -0,0 +1,5 @@ +--- +description: Create a Conventional Commit with signoff +--- + +Follow @.agents/skills/commit/SKILL.md. diff --git a/.opencode/commands/docs.md b/.opencode/commands/docs.md new file mode 100644 index 000000000..09bdec8fa --- /dev/null +++ b/.opencode/commands/docs.md @@ -0,0 +1,5 @@ +--- +description: Update project docs per repo guidelines +--- + +Follow @.agents/skills/docs/SKILL.md. diff --git a/.opencode/commands/implement.md b/.opencode/commands/implement.md new file mode 100644 index 000000000..347b1cbdb --- /dev/null +++ b/.opencode/commands/implement.md @@ -0,0 +1,5 @@ +--- +description: Implement a change following repo workflow +--- + +Follow @.agents/skills/implement/SKILL.md. diff --git a/.opencode/commands/issue.md b/.opencode/commands/issue.md new file mode 100644 index 000000000..d341c58bb --- /dev/null +++ b/.opencode/commands/issue.md @@ -0,0 +1,5 @@ +--- +description: Create a GitHub issue markdown file +--- + +Follow @.agents/skills/issue/SKILL.md. diff --git a/.opencode/commands/lint.md b/.opencode/commands/lint.md new file mode 100644 index 000000000..0652b71d2 --- /dev/null +++ b/.opencode/commands/lint.md @@ -0,0 +1,5 @@ +--- +description: Run repo format + lint workflow +--- + +Follow @.agents/skills/lint/SKILL.md. diff --git a/.opencode/commands/summary.md b/.opencode/commands/summary.md new file mode 100644 index 000000000..9f4eedfa1 --- /dev/null +++ b/.opencode/commands/summary.md @@ -0,0 +1,5 @@ +--- +description: Generate a concise PR implementation summary +--- + +Follow @.agents/skills/summary/SKILL.md. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..d86970c2a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,391 @@ +# AGENTS.md — GitProxy + +## Project Overview + +GitProxy is a Git HTTP proxy that intercepts Git operations (primarily `git push`) and enforces organizational policies before allowing changes to reach the actual Git host. + +It acts as: + +- A **policy enforcement engine** (via processors and plugins) +- A **review/approval gate** (manual or automated) +- A **proxy server** for Git operations +- A **UI + API layer** for reviewing, approving, and auditing pushes + +The core design principle is a **chain-of-processors architecture** where each Git action flows through ordered processing steps. + +--- + +## Build & Run + +``` +# Build +npm run build + +# Run unit tests +npm run test + +# Run e2e tests +npm run test:e2e + +# Lint check/fix +npm run lint +npm run lint:fix + +# Format check/fix +npm run format:check +npm run format +``` + +**Stack: ** + +--- + +## High-Level Architecture + +GitProxy consists of four main components: + +``` +Contributor (git push) + ↓ +HTTP Proxy (/src/proxy) + ↓ +Action Chain (Processors + Plugins) + ↓ +Service API (/src/service) + ↓ +Database (audit, users, repos, approvals) + ↓ +Web UI (/src/ui) +``` + +### 1. Proxy Server (`/src/proxy`) + +Express-based HTTP proxy that: + +- Intercepts Git operations +- Parses requests into an `Action` +- Executes the appropriate **Action Chain** +- Blocks, rejects, or queues for approval + +Core concepts: + +- **Action** — Represents a Git operation (push/pull/default) +- **Chain** — Ordered list of processors +- **Processor (Step)** — A single policy enforcement unit +- **Plugin** — Custom processor injected externally + +--- + +### 2. Service API (`/src/service`) + +Express application responsible for: + +- UI communication +- Authentication (Passport strategies) +- Database access +- Approval/rejection workflows + +Default port: `8080` + +Authentication strategies supported: + +- Local +- ActiveDirectory +- OpenID Connect + +--- + +### 3. Configuration (`/src/config`) + +Loads and validates `proxy.config.json`. + +Controls: + +- Authentication methods +- Repository allowlist +- Commit message policies +- Database configuration +- Feature flags + +Schema reference: +[https://git-proxy.finos.org/docs/configuration/reference/](https://git-proxy.finos.org/docs/configuration/reference/) + +--- + +### 4. Web UI (`/src/ui`) + +React-based UI used to: + +- View pending pushes +- Review diffs +- Approve/reject pushes +- Manage repositories/users (depending on role) + +--- + +## Core Architectural Model + +### Action Lifecycle (Push) + +1. `parseAction` classifies request +2. `pushActionChain` executes processors in strict order +3. If blocked → rejected +4. If valid → queued for approval +5. Approver reviews in UI +6. If approved → user re-pushes to actual remote + +--- + +### Action Chains + +#### Push Action Chain + +``` +parsePush +checkEmptyBranch +checkRepoInAuthorisedList +checkCommitMessages +checkAuthorEmails +checkUserPushPermission +pullRemote +writePack +checkHiddenCommits +checkIfWaitingAuth +preReceive +getDiff +gitleaks +scanDiff +blockForAuth +``` + +**Order matters.** Some processors depend on artifacts created by previous ones (e.g., cloned repo, computed diff). + +--- + +#### Pull Action Chain + +``` +checkRepoInAuthorisedList +``` + +--- + +#### Default Action Chain + +``` +checkRepoInAuthorisedList +``` + +--- + +### Processor Rules + +When modifying or adding processors: + +- They must be **idempotent** +- They must clearly define: + - Required inputs + - Side effects + - Failure mode (reject vs throw vs auto-approve) + +- They must not mutate shared state outside the `Action` +- They must preserve audit traceability + +If a processor requires data not available at the end of the chain, it must be inserted earlier. + +--- + +### Plugin System + +Plugins: + +- Extend push/pull chains +- Are externally defined processors +- Should not modify core system invariants +- Must respect chain ordering semantics + +If logic needs access to internal chain data before plugins execute, implement a **custom processor**, not a plugin. + +--- + +## Authentication Model + +Authentication applies to: + +- UI access +- Approval workflow +- User management + +It does NOT authenticate Git pushes via the proxy itself — Git identity is derived from commit metadata (`user.email`). + +Supported methods: + +- Local (default) +- ActiveDirectory +- OpenID Connect + +New strategies must: + +1. Extend `/src/service/passport` +2. Provide a `configure()` function +3. Match config `type` +4. Be added to `authStrategies` in `index.ts` + +--- + +## Audit Model + +After chain execution: + +- `audit` stores: + - Action metadata + - Processor results + - Approval state + +If repository clone occurred: + +- `clearBareClone` must clean up disk artifacts + +Never introduce processor changes that bypass audit logging. + +Audit integrity is critical. + +--- + +## Development Guidelines for Agents + +### 1. Respect the Chain Architecture + +The action chain is the core abstraction. + +When implementing new functionality: + +- Decide whether it belongs in: + - Existing processor + - New processor + - Plugin + - Service layer + - UI + +- Do NOT insert logic randomly in the proxy request handler. + +--- + +### 2. Separation of Concerns + +- Proxy handles Git interception + chain execution +- Service handles authentication + state +- Config handles validation and schema +- UI handles display + approval user flow + +Do not mix responsibilities across modules. + +--- + +### 3. Approval Semantics + +Important rule: + +> A push must never reach the real Git remote unless explicitly approved or auto-approved by policy. + +Changes must not bypass: + +- `blockForAuth` +- Approval state checks +- Waiting authorization checks + +--- + +### 4. Configuration Safety + +When introducing new config options: + +- Add schema validation +- Provide sensible defaults +- Ensure backward compatibility +- Document in schema reference + +Never silently change default security behavior. + +--- + +### 5. Adding New Policies + +#### If simple and configurable: + +Add to existing processor (if cohesive). + +#### If complex and reusable: + +Create a new processor. + +#### If organization-specific: + +Implement as plugin. + +Ask: + +- Does this require diff access? +- Does this require cloned repo? +- Does this require user database? +- Does this need to run before approval gating? + +--- + +### Testing Expectations + +When modifying: + +#### Proxy / Processors + +- Must test: + - Success path + - Rejection path + - Audit logging (`step.error`, `step.log`) + +#### Config + +- Must test: + - Invalid values + - Default values + +--- + +## Common Pitfalls + +- Breaking processor order dependencies +- Mixing UI and service logic +- Introducing security regressions in approval flow +- Mutating global/shared state outside `Action` + +--- + +## License Header + +All source files must include the Apache 2.0 license header (see any existing file). + +--- + +## Agent Workflow + +**The main agent must act as an orchestrator.** Never do work inline that can be delegated to a subagent. + +- **Delegate everything:** Use the Task tool with specialized subagents for all research, code exploration, code writing, testing, and analysis. The main agent should plan, coordinate, and summarize — not do the work itself. +- **Maximize parallelism:** Launch multiple subagents concurrently whenever their tasks are independent. For example, when exploring code patterns AND analyzing tests AND checking dependencies, spawn all three agents in a single message rather than sequentially. Always send independent Task calls in a **single message** with multiple tool-use blocks. +<!-- - **Use the right agent type:** Pick `Explore` for codebase search/understanding, `Plan` for architecture decisions, `Bash` for commands, and specialized agents (e.g., `code-reviewer`, `test-automator`, `debugger`) when they match the task. +- **Keep the main context clean:** Offload large file reads, multi-file searches, and deep analysis to subagents so the main conversation stays focused on coordination and user communication. +- **Hooks run automatically — use subagents to respond:** When a hook (Spotless, build verification, code review, or simplification) reports an issue, delegate the fix to a subagent rather than doing it inline. If multiple hooks fail simultaneously, spawn parallel subagents to address each issue concurrently. --> + +--- + +## Summary + +GitProxy is: + +- A deterministic policy pipeline +- Wrapped in a Git HTTP proxy +- With an approval gate +- Backed by a service API +- Audited end-to-end + +All changes must respect that flow. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..78926b3c6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,11 @@ +# Claude Instructions + +Read and follow `AGENTS.md` first. It is the canonical project guide for this repository. + +Additional Claude-specific notes: + +- Apply `AGENTS.md` as the source of truth. If this file and `AGENTS.md` ever differ, `AGENTS.md` wins. +- Keep changes aligned with the chain-of-processors architecture. Do not bypass approval gating, `blockForAuth`, waiting-authorization checks, or audit logging. +- Prefer the existing build, test, lint, and format commands listed in `AGENTS.md`. +- Project slash commands are defined as skills in `.agents/skills/`. +- Treat `.claude/worktrees/` as local machine state. Do not rely on it or commit changes from it. diff --git a/README.md b/README.md index eabcaeda0..ad46000c6 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ customize for your environment, see the [project's documentation](https://git-pr ## Contributing -Your contributions are at the core of making this a truly open source project. Any contributions you make are **greatly appreciated**. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for more information. +Your contributions are at the core of making this a truly open source project. Any contributions you make are **greatly appreciated**. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for more information. For AI-assisted development, start with [`AGENTS.md`](AGENTS.md). Tool-specific entry points are provided in [`CLAUDE.md`](CLAUDE.md), [`.cursor/rules/00-core.mdc`](.cursor/rules/00-core.mdc), and [`.github/copilot-instructions.md`](.github/copilot-instructions.md). Shared slash-command prompts live in [`.agents/commands`](.agents/commands), with tool-native entry points in [`.claude/commands`](.claude/commands) and [`.cursor/commands`](.cursor/commands). ## Security