From 3cccbfb8cbc5f246cfcc874fae650080dc157328 Mon Sep 17 00:00:00 2001 From: Mathias Date: Sun, 17 May 2026 00:02:08 +0200 Subject: [PATCH] chore: re-sync context adapters after rebase Upstream .context/PROJECT.md gained a branch-protection rule + an extra agent instruction. Pure regeneration via scripts/context-sync.sh to make task check pass before force-push. Co-Authored-By: Claude Opus 4.7 (1M context) --- .aider.conventions.md | 124 +++++++++++++++++++++++-------------- .context/system-prompt.txt | 124 +++++++++++++++++++++++-------------- .cursorrules | 124 +++++++++++++++++++++++-------------- AGENTS.md | 124 +++++++++++++++++++++++-------------- CLAUDE.md | 121 ++++++++++++++++++++++-------------- 5 files changed, 384 insertions(+), 233 deletions(-) diff --git a/.aider.conventions.md b/.aider.conventions.md index b1441ab..a48af53 100644 --- a/.aider.conventions.md +++ b/.aider.conventions.md @@ -49,7 +49,6 @@ These rules apply to every task across every project, regardless of harness. | Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — | | Logging | slog (structured) | — | — | | Testing | Table-driven, testify | — | — | -| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — | Exploratory: Rust, Zig — I'll tell you when I want these. @@ -61,7 +60,7 @@ Exploratory: Rust, Zig — I'll tell you when I want these. - **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs - **Git**: conventional commits (`feat:`, `fix:`, `chore:`), one concern per PR, PR describes *why* not *what* - **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config -- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message +- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc are pre-approved; anything else needs justification in the commit message ## Infrastructure @@ -211,6 +210,7 @@ Key skills: - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` - Branch naming: `feat/short-description`, `fix/short-description` - PRs: one concern per PR, description explains *why* not *what* +- **Branch protection:** always work on a feature branch, open a PR, never push directly to main ### Security - No secrets in code, ever — use env vars or SOPS-encrypted files @@ -248,68 +248,98 @@ When acting as a coding agent on this project: 3. If unsure about a convention, check `DECISIONS.md` or ask 4. Never modify files outside the project root without explicit permission 5. When adding a dependency, explain why in the commit message -6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM +6. Always work on a feature branch and open a PR — never push directly to main +7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM -## Current sprint — gitea-mcp v0.2 (2026-05-14) +## Current sprint — gitea-mcp v0.2 patch (2026-05-14) ### Context -This sprint implements new MCP tools needed for `hyperguild new-project` — -the automated project creation flow triggered from claude.ai. See brain knowledge -nodes `adr-new-project-gitea-first-github-mirror` and `roadmap-github-ingestion-pipeline` -for full background. +The main v0.2 batch (repo_create, repo_update, repo_mirror_push, repo_delete, +repo_tree, repo_topics_update, file_read dir-fix, issue_get, release_create, +create_project_from_template) was implemented and pushed directly to main. -### Issues to implement (priority order) +This sprint fixes three remaining gaps found during code review on 2026-05-14. +These are blockers for `hyperguild new-project`. -**Batch 1 — blockers (do first, one PR: `feat/repo-crud`)** +### Issues to fix (all three in one PR: `fix/v02-patch`) -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #13 | `repo_create` | POST /api/v1/user/repos or /api/v1/orgs/{org}/repos | -| #16 | `repo_mirror_push` (add/list/delete) | POST/GET/DELETE /api/v1/repos/{owner}/{repo}/push_mirrors | -| #12 | `repo_update` | PATCH /api/v1/repos/{owner}/{repo} | +#### #12 — repo_update: add `archived` and `template` fields +**File:** `internal/gitea/repos.go` → `UpdateRepoArgs` struct +**File:** `internal/tools/repo_update.go` → input schema + args struct -**Batch 2 — quality of life (second PR: `feat/repo-ux`)** +Add to `UpdateRepoArgs`: +```go +Archived *bool +Template *bool +``` -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #15 | `file_read` dir-path fix | existing endpoint, detect array vs object response | -| #14 | `repo_tree` | GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=true | -| #18 | `repo_topics_update` | PUT /api/v1/repos/{owner}/{repo}/topics | +Add to tool input schema: +```json +"archived": { + "type": "boolean", + "description": "Mark repo as archived (read-only). Requires confirm=." +}, +"template": { + "type": "boolean", + "description": "Toggle template repo flag." +} +``` -**Batch 3 — can wait** +Add confirm-guard for `archived=true` (same pattern as `private=false`): +```go +if args.Archived != nil && *args.Archived { + if args.Confirm != args.Name { + return nil, fmt.Errorf("setting archived=true is irreversible: set confirm=%q to proceed", args.Name) + } +} +``` -| Issue | Tool | Note | -|-------|------|------| -| #11 | `repo_delete` | HIGH risk — needs `confirm` param == repo name | -| #17 | `release_create` | POST /api/v1/repos/{owner}/{repo}/releases | +New test cases to add in `repo_update_test.go`: +- `TestRepoUpdateTool_Archive` — happy path with confirm +- `TestRepoUpdateTool_ArchiveRequiresConfirm` — missing confirm returns error +- `TestRepoUpdateTool_SetTemplate` — no confirm needed -### How to add a tool (pattern) +#### #24 — create_project_from_template: make template selectable +**File:** `internal/tools/create_project_from_template.go` -Every tool = 4 files following `internal/tools/repo_get.go` exactly: +Add optional `template_name` param to input schema: +```json +"template_name": { + "type": "string", + "enum": ["template-go-web", "template-go-agent"], + "description": "Template repo to generate from. Defaults to template-go-web.", + "default": "template-go-web" +} +``` -1. `internal/gitea/.go` — API client method (use PostJSON/PatchJSON/DeleteJSON) -2. `internal/tools/repo_.go` — tool handler with Descriptor() + Call() -3. `internal/tools/repo__test.go` — table-driven tests with httptest.NewServer -4. Registration in main — find where `NewRepoGet` is registered, add new tool same place +The tool should use `args.TemplateName` if set, fall back to the hardcoded default. +Remove the hardcoded template name from `cmd/gitea-mcp/main.go` constructor call — +the tool resolves it internally. -Key rules: -- Always call `t.a.Check(args.Owner)` before any API call (allowlist guard) -- Use `textOK(result)` for success output -- For `repo_mirror_push`: NEVER log or return `remote_password` in any output -- For `repo_update` with `private: false` and `repo_delete`: require `confirm` param == repo name +New test case: `TestCreateProjectFromTemplate_AgentTemplate` -### Token permissions needed +#### #25 — pr_files_diff: fix same diff returned for all files +**File:** `internal/tools/pr_files_diff.go` -New tools require these additional Gitea token scopes: -- `write:repository` — repo_create, repo_update, repo_mirror_push, repo_topics_update, release_create -- `delete_repo` — repo_delete +There is a loop bug where all file entries in the response contain the same diff +(the first file's diff is reused for every subsequent file). Find the loop and +ensure each iteration reads and assigns the correct diff for its own file. -Check current token: `curl -H "Authorization: token $GITEA_TOKEN" https://gitea.d-ma.be/api/v1/user` -If scopes are missing, update token in Gitea settings before running tests. +Reproduce: call `pr_files_diff` on any PR with 3+ files, verify each file has +a distinct diff. ### Definition of done -- `task check` passes (all tools, all batches) -- Each new tool manually callable via `claude mcp call` -- PR #1 (batch 1) merged before starting batch 2 -- Issue #19 (mirror flow e2e test) verified manually after batch 1 is deployed +- [ ] `task check` passes +- [ ] `repo_update` accepts `archived` and `template` params +- [ ] `archived=true` requires `confirm=` +- [ ] `create_project_from_template` accepts `template_name` param, defaults to `template-go-web` +- [ ] `pr_files_diff` returns distinct diff per file +- [ ] All new test cases pass +- [ ] PR `fix/v02-patch` merged to main via PR (not direct push) + +### After this sprint + +Next: `hyperguild new-project` v1 implementation. +See brain node `adr-new-project-gitea-first-github-mirror` for the full flow spec. +Also: verify end-to-end mirror flow (issue #19) once `repo_mirror_push` is confirmed working. diff --git a/.context/system-prompt.txt b/.context/system-prompt.txt index b916a79..23b35d4 100644 --- a/.context/system-prompt.txt +++ b/.context/system-prompt.txt @@ -54,7 +54,6 @@ These rules apply to every task across every project, regardless of harness. | Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — | | Logging | slog (structured) | — | — | | Testing | Table-driven, testify | — | — | -| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — | Exploratory: Rust, Zig — I'll tell you when I want these. @@ -66,7 +65,7 @@ Exploratory: Rust, Zig — I'll tell you when I want these. - **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs - **Git**: conventional commits (`feat:`, `fix:`, `chore:`), one concern per PR, PR describes *why* not *what* - **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config -- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message +- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc are pre-approved; anything else needs justification in the commit message ## Infrastructure @@ -216,6 +215,7 @@ Key skills: - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` - Branch naming: `feat/short-description`, `fix/short-description` - PRs: one concern per PR, description explains *why* not *what* +- **Branch protection:** always work on a feature branch, open a PR, never push directly to main ### Security - No secrets in code, ever — use env vars or SOPS-encrypted files @@ -253,70 +253,100 @@ When acting as a coding agent on this project: 3. If unsure about a convention, check `DECISIONS.md` or ask 4. Never modify files outside the project root without explicit permission 5. When adding a dependency, explain why in the commit message -6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM +6. Always work on a feature branch and open a PR — never push directly to main +7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM -## Current sprint — gitea-mcp v0.2 (2026-05-14) +## Current sprint — gitea-mcp v0.2 patch (2026-05-14) ### Context -This sprint implements new MCP tools needed for `hyperguild new-project` — -the automated project creation flow triggered from claude.ai. See brain knowledge -nodes `adr-new-project-gitea-first-github-mirror` and `roadmap-github-ingestion-pipeline` -for full background. +The main v0.2 batch (repo_create, repo_update, repo_mirror_push, repo_delete, +repo_tree, repo_topics_update, file_read dir-fix, issue_get, release_create, +create_project_from_template) was implemented and pushed directly to main. -### Issues to implement (priority order) +This sprint fixes three remaining gaps found during code review on 2026-05-14. +These are blockers for `hyperguild new-project`. -**Batch 1 — blockers (do first, one PR: `feat/repo-crud`)** +### Issues to fix (all three in one PR: `fix/v02-patch`) -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #13 | `repo_create` | POST /api/v1/user/repos or /api/v1/orgs/{org}/repos | -| #16 | `repo_mirror_push` (add/list/delete) | POST/GET/DELETE /api/v1/repos/{owner}/{repo}/push_mirrors | -| #12 | `repo_update` | PATCH /api/v1/repos/{owner}/{repo} | +#### #12 — repo_update: add `archived` and `template` fields +**File:** `internal/gitea/repos.go` → `UpdateRepoArgs` struct +**File:** `internal/tools/repo_update.go` → input schema + args struct -**Batch 2 — quality of life (second PR: `feat/repo-ux`)** +Add to `UpdateRepoArgs`: +```go +Archived *bool +Template *bool +``` -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #15 | `file_read` dir-path fix | existing endpoint, detect array vs object response | -| #14 | `repo_tree` | GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=true | -| #18 | `repo_topics_update` | PUT /api/v1/repos/{owner}/{repo}/topics | +Add to tool input schema: +```json +"archived": { + "type": "boolean", + "description": "Mark repo as archived (read-only). Requires confirm=." +}, +"template": { + "type": "boolean", + "description": "Toggle template repo flag." +} +``` -**Batch 3 — can wait** +Add confirm-guard for `archived=true` (same pattern as `private=false`): +```go +if args.Archived != nil && *args.Archived { + if args.Confirm != args.Name { + return nil, fmt.Errorf("setting archived=true is irreversible: set confirm=%q to proceed", args.Name) + } +} +``` -| Issue | Tool | Note | -|-------|------|------| -| #11 | `repo_delete` | HIGH risk — needs `confirm` param == repo name | -| #17 | `release_create` | POST /api/v1/repos/{owner}/{repo}/releases | +New test cases to add in `repo_update_test.go`: +- `TestRepoUpdateTool_Archive` — happy path with confirm +- `TestRepoUpdateTool_ArchiveRequiresConfirm` — missing confirm returns error +- `TestRepoUpdateTool_SetTemplate` — no confirm needed -### How to add a tool (pattern) +#### #24 — create_project_from_template: make template selectable +**File:** `internal/tools/create_project_from_template.go` -Every tool = 4 files following `internal/tools/repo_get.go` exactly: +Add optional `template_name` param to input schema: +```json +"template_name": { + "type": "string", + "enum": ["template-go-web", "template-go-agent"], + "description": "Template repo to generate from. Defaults to template-go-web.", + "default": "template-go-web" +} +``` -1. `internal/gitea/.go` — API client method (use PostJSON/PatchJSON/DeleteJSON) -2. `internal/tools/repo_.go` — tool handler with Descriptor() + Call() -3. `internal/tools/repo__test.go` — table-driven tests with httptest.NewServer -4. Registration in main — find where `NewRepoGet` is registered, add new tool same place +The tool should use `args.TemplateName` if set, fall back to the hardcoded default. +Remove the hardcoded template name from `cmd/gitea-mcp/main.go` constructor call — +the tool resolves it internally. -Key rules: -- Always call `t.a.Check(args.Owner)` before any API call (allowlist guard) -- Use `textOK(result)` for success output -- For `repo_mirror_push`: NEVER log or return `remote_password` in any output -- For `repo_update` with `private: false` and `repo_delete`: require `confirm` param == repo name +New test case: `TestCreateProjectFromTemplate_AgentTemplate` -### Token permissions needed +#### #25 — pr_files_diff: fix same diff returned for all files +**File:** `internal/tools/pr_files_diff.go` -New tools require these additional Gitea token scopes: -- `write:repository` — repo_create, repo_update, repo_mirror_push, repo_topics_update, release_create -- `delete_repo` — repo_delete +There is a loop bug where all file entries in the response contain the same diff +(the first file's diff is reused for every subsequent file). Find the loop and +ensure each iteration reads and assigns the correct diff for its own file. -Check current token: `curl -H "Authorization: token $GITEA_TOKEN" https://gitea.d-ma.be/api/v1/user` -If scopes are missing, update token in Gitea settings before running tests. +Reproduce: call `pr_files_diff` on any PR with 3+ files, verify each file has +a distinct diff. ### Definition of done -- `task check` passes (all tools, all batches) -- Each new tool manually callable via `claude mcp call` -- PR #1 (batch 1) merged before starting batch 2 -- Issue #19 (mirror flow e2e test) verified manually after batch 1 is deployed +- [ ] `task check` passes +- [ ] `repo_update` accepts `archived` and `template` params +- [ ] `archived=true` requires `confirm=` +- [ ] `create_project_from_template` accepts `template_name` param, defaults to `template-go-web` +- [ ] `pr_files_diff` returns distinct diff per file +- [ ] All new test cases pass +- [ ] PR `fix/v02-patch` merged to main via PR (not direct push) + +### After this sprint + +Next: `hyperguild new-project` v1 implementation. +See brain node `adr-new-project-gitea-first-github-mirror` for the full flow spec. +Also: verify end-to-end mirror flow (issue #19) once `repo_mirror_push` is confirmed working. --- diff --git a/.cursorrules b/.cursorrules index 0c3be4a..cc7d254 100644 --- a/.cursorrules +++ b/.cursorrules @@ -52,7 +52,6 @@ These rules apply to every task across every project, regardless of harness. | Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — | | Logging | slog (structured) | — | — | | Testing | Table-driven, testify | — | — | -| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — | Exploratory: Rust, Zig — I'll tell you when I want these. @@ -64,7 +63,7 @@ Exploratory: Rust, Zig — I'll tell you when I want these. - **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs - **Git**: conventional commits (`feat:`, `fix:`, `chore:`), one concern per PR, PR describes *why* not *what* - **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config -- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message +- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc are pre-approved; anything else needs justification in the commit message ## Infrastructure @@ -214,6 +213,7 @@ Key skills: - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` - Branch naming: `feat/short-description`, `fix/short-description` - PRs: one concern per PR, description explains *why* not *what* +- **Branch protection:** always work on a feature branch, open a PR, never push directly to main ### Security - No secrets in code, ever — use env vars or SOPS-encrypted files @@ -251,68 +251,98 @@ When acting as a coding agent on this project: 3. If unsure about a convention, check `DECISIONS.md` or ask 4. Never modify files outside the project root without explicit permission 5. When adding a dependency, explain why in the commit message -6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM +6. Always work on a feature branch and open a PR — never push directly to main +7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM -## Current sprint — gitea-mcp v0.2 (2026-05-14) +## Current sprint — gitea-mcp v0.2 patch (2026-05-14) ### Context -This sprint implements new MCP tools needed for `hyperguild new-project` — -the automated project creation flow triggered from claude.ai. See brain knowledge -nodes `adr-new-project-gitea-first-github-mirror` and `roadmap-github-ingestion-pipeline` -for full background. +The main v0.2 batch (repo_create, repo_update, repo_mirror_push, repo_delete, +repo_tree, repo_topics_update, file_read dir-fix, issue_get, release_create, +create_project_from_template) was implemented and pushed directly to main. -### Issues to implement (priority order) +This sprint fixes three remaining gaps found during code review on 2026-05-14. +These are blockers for `hyperguild new-project`. -**Batch 1 — blockers (do first, one PR: `feat/repo-crud`)** +### Issues to fix (all three in one PR: `fix/v02-patch`) -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #13 | `repo_create` | POST /api/v1/user/repos or /api/v1/orgs/{org}/repos | -| #16 | `repo_mirror_push` (add/list/delete) | POST/GET/DELETE /api/v1/repos/{owner}/{repo}/push_mirrors | -| #12 | `repo_update` | PATCH /api/v1/repos/{owner}/{repo} | +#### #12 — repo_update: add `archived` and `template` fields +**File:** `internal/gitea/repos.go` → `UpdateRepoArgs` struct +**File:** `internal/tools/repo_update.go` → input schema + args struct -**Batch 2 — quality of life (second PR: `feat/repo-ux`)** +Add to `UpdateRepoArgs`: +```go +Archived *bool +Template *bool +``` -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #15 | `file_read` dir-path fix | existing endpoint, detect array vs object response | -| #14 | `repo_tree` | GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=true | -| #18 | `repo_topics_update` | PUT /api/v1/repos/{owner}/{repo}/topics | +Add to tool input schema: +```json +"archived": { + "type": "boolean", + "description": "Mark repo as archived (read-only). Requires confirm=." +}, +"template": { + "type": "boolean", + "description": "Toggle template repo flag." +} +``` -**Batch 3 — can wait** +Add confirm-guard for `archived=true` (same pattern as `private=false`): +```go +if args.Archived != nil && *args.Archived { + if args.Confirm != args.Name { + return nil, fmt.Errorf("setting archived=true is irreversible: set confirm=%q to proceed", args.Name) + } +} +``` -| Issue | Tool | Note | -|-------|------|------| -| #11 | `repo_delete` | HIGH risk — needs `confirm` param == repo name | -| #17 | `release_create` | POST /api/v1/repos/{owner}/{repo}/releases | +New test cases to add in `repo_update_test.go`: +- `TestRepoUpdateTool_Archive` — happy path with confirm +- `TestRepoUpdateTool_ArchiveRequiresConfirm` — missing confirm returns error +- `TestRepoUpdateTool_SetTemplate` — no confirm needed -### How to add a tool (pattern) +#### #24 — create_project_from_template: make template selectable +**File:** `internal/tools/create_project_from_template.go` -Every tool = 4 files following `internal/tools/repo_get.go` exactly: +Add optional `template_name` param to input schema: +```json +"template_name": { + "type": "string", + "enum": ["template-go-web", "template-go-agent"], + "description": "Template repo to generate from. Defaults to template-go-web.", + "default": "template-go-web" +} +``` -1. `internal/gitea/.go` — API client method (use PostJSON/PatchJSON/DeleteJSON) -2. `internal/tools/repo_.go` — tool handler with Descriptor() + Call() -3. `internal/tools/repo__test.go` — table-driven tests with httptest.NewServer -4. Registration in main — find where `NewRepoGet` is registered, add new tool same place +The tool should use `args.TemplateName` if set, fall back to the hardcoded default. +Remove the hardcoded template name from `cmd/gitea-mcp/main.go` constructor call — +the tool resolves it internally. -Key rules: -- Always call `t.a.Check(args.Owner)` before any API call (allowlist guard) -- Use `textOK(result)` for success output -- For `repo_mirror_push`: NEVER log or return `remote_password` in any output -- For `repo_update` with `private: false` and `repo_delete`: require `confirm` param == repo name +New test case: `TestCreateProjectFromTemplate_AgentTemplate` -### Token permissions needed +#### #25 — pr_files_diff: fix same diff returned for all files +**File:** `internal/tools/pr_files_diff.go` -New tools require these additional Gitea token scopes: -- `write:repository` — repo_create, repo_update, repo_mirror_push, repo_topics_update, release_create -- `delete_repo` — repo_delete +There is a loop bug where all file entries in the response contain the same diff +(the first file's diff is reused for every subsequent file). Find the loop and +ensure each iteration reads and assigns the correct diff for its own file. -Check current token: `curl -H "Authorization: token $GITEA_TOKEN" https://gitea.d-ma.be/api/v1/user` -If scopes are missing, update token in Gitea settings before running tests. +Reproduce: call `pr_files_diff` on any PR with 3+ files, verify each file has +a distinct diff. ### Definition of done -- `task check` passes (all tools, all batches) -- Each new tool manually callable via `claude mcp call` -- PR #1 (batch 1) merged before starting batch 2 -- Issue #19 (mirror flow e2e test) verified manually after batch 1 is deployed +- [ ] `task check` passes +- [ ] `repo_update` accepts `archived` and `template` params +- [ ] `archived=true` requires `confirm=` +- [ ] `create_project_from_template` accepts `template_name` param, defaults to `template-go-web` +- [ ] `pr_files_diff` returns distinct diff per file +- [ ] All new test cases pass +- [ ] PR `fix/v02-patch` merged to main via PR (not direct push) + +### After this sprint + +Next: `hyperguild new-project` v1 implementation. +See brain node `adr-new-project-gitea-first-github-mirror` for the full flow spec. +Also: verify end-to-end mirror flow (issue #19) once `repo_mirror_push` is confirmed working. diff --git a/AGENTS.md b/AGENTS.md index b1441ab..a48af53 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,7 +49,6 @@ These rules apply to every task across every project, regardless of harness. | Search | pgvector (vector), BM25 | Qdrant (when >1M vectors or hybrid retrieval) | — | | Logging | slog (structured) | — | — | | Testing | Table-driven, testify | — | — | -| Agents (Go) | google.golang.org/adk + pkg/litellm adapter | — | — | Exploratory: Rust, Zig — I'll tell you when I want these. @@ -61,7 +60,7 @@ Exploratory: Rust, Zig — I'll tell you when I want these. - **Architecture**: prefer stdlib over frameworks, constructor injection, env-var config parsed into typed structs - **Git**: conventional commits (`feat:`, `fix:`, `chore:`), one concern per PR, PR describes *why* not *what* - **Security**: no secrets in code, govulncheck before adding deps, SOPS for encrypted config -- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc, google.golang.org/adk (agent projects only) are pre-approved; anything else needs justification in the commit message +- **Dependencies**: prefer stdlib. testify, slog, templ, sqlc are pre-approved; anything else needs justification in the commit message ## Infrastructure @@ -211,6 +210,7 @@ Key skills: - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` - Branch naming: `feat/short-description`, `fix/short-description` - PRs: one concern per PR, description explains *why* not *what* +- **Branch protection:** always work on a feature branch, open a PR, never push directly to main ### Security - No secrets in code, ever — use env vars or SOPS-encrypted files @@ -248,68 +248,98 @@ When acting as a coding agent on this project: 3. If unsure about a convention, check `DECISIONS.md` or ask 4. Never modify files outside the project root without explicit permission 5. When adding a dependency, explain why in the commit message -6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM +6. Always work on a feature branch and open a PR — never push directly to main +7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM -## Current sprint — gitea-mcp v0.2 (2026-05-14) +## Current sprint — gitea-mcp v0.2 patch (2026-05-14) ### Context -This sprint implements new MCP tools needed for `hyperguild new-project` — -the automated project creation flow triggered from claude.ai. See brain knowledge -nodes `adr-new-project-gitea-first-github-mirror` and `roadmap-github-ingestion-pipeline` -for full background. +The main v0.2 batch (repo_create, repo_update, repo_mirror_push, repo_delete, +repo_tree, repo_topics_update, file_read dir-fix, issue_get, release_create, +create_project_from_template) was implemented and pushed directly to main. -### Issues to implement (priority order) +This sprint fixes three remaining gaps found during code review on 2026-05-14. +These are blockers for `hyperguild new-project`. -**Batch 1 — blockers (do first, one PR: `feat/repo-crud`)** +### Issues to fix (all three in one PR: `fix/v02-patch`) -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #13 | `repo_create` | POST /api/v1/user/repos or /api/v1/orgs/{org}/repos | -| #16 | `repo_mirror_push` (add/list/delete) | POST/GET/DELETE /api/v1/repos/{owner}/{repo}/push_mirrors | -| #12 | `repo_update` | PATCH /api/v1/repos/{owner}/{repo} | +#### #12 — repo_update: add `archived` and `template` fields +**File:** `internal/gitea/repos.go` → `UpdateRepoArgs` struct +**File:** `internal/tools/repo_update.go` → input schema + args struct -**Batch 2 — quality of life (second PR: `feat/repo-ux`)** +Add to `UpdateRepoArgs`: +```go +Archived *bool +Template *bool +``` -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #15 | `file_read` dir-path fix | existing endpoint, detect array vs object response | -| #14 | `repo_tree` | GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=true | -| #18 | `repo_topics_update` | PUT /api/v1/repos/{owner}/{repo}/topics | +Add to tool input schema: +```json +"archived": { + "type": "boolean", + "description": "Mark repo as archived (read-only). Requires confirm=." +}, +"template": { + "type": "boolean", + "description": "Toggle template repo flag." +} +``` -**Batch 3 — can wait** +Add confirm-guard for `archived=true` (same pattern as `private=false`): +```go +if args.Archived != nil && *args.Archived { + if args.Confirm != args.Name { + return nil, fmt.Errorf("setting archived=true is irreversible: set confirm=%q to proceed", args.Name) + } +} +``` -| Issue | Tool | Note | -|-------|------|------| -| #11 | `repo_delete` | HIGH risk — needs `confirm` param == repo name | -| #17 | `release_create` | POST /api/v1/repos/{owner}/{repo}/releases | +New test cases to add in `repo_update_test.go`: +- `TestRepoUpdateTool_Archive` — happy path with confirm +- `TestRepoUpdateTool_ArchiveRequiresConfirm` — missing confirm returns error +- `TestRepoUpdateTool_SetTemplate` — no confirm needed -### How to add a tool (pattern) +#### #24 — create_project_from_template: make template selectable +**File:** `internal/tools/create_project_from_template.go` -Every tool = 4 files following `internal/tools/repo_get.go` exactly: +Add optional `template_name` param to input schema: +```json +"template_name": { + "type": "string", + "enum": ["template-go-web", "template-go-agent"], + "description": "Template repo to generate from. Defaults to template-go-web.", + "default": "template-go-web" +} +``` -1. `internal/gitea/.go` — API client method (use PostJSON/PatchJSON/DeleteJSON) -2. `internal/tools/repo_.go` — tool handler with Descriptor() + Call() -3. `internal/tools/repo__test.go` — table-driven tests with httptest.NewServer -4. Registration in main — find where `NewRepoGet` is registered, add new tool same place +The tool should use `args.TemplateName` if set, fall back to the hardcoded default. +Remove the hardcoded template name from `cmd/gitea-mcp/main.go` constructor call — +the tool resolves it internally. -Key rules: -- Always call `t.a.Check(args.Owner)` before any API call (allowlist guard) -- Use `textOK(result)` for success output -- For `repo_mirror_push`: NEVER log or return `remote_password` in any output -- For `repo_update` with `private: false` and `repo_delete`: require `confirm` param == repo name +New test case: `TestCreateProjectFromTemplate_AgentTemplate` -### Token permissions needed +#### #25 — pr_files_diff: fix same diff returned for all files +**File:** `internal/tools/pr_files_diff.go` -New tools require these additional Gitea token scopes: -- `write:repository` — repo_create, repo_update, repo_mirror_push, repo_topics_update, release_create -- `delete_repo` — repo_delete +There is a loop bug where all file entries in the response contain the same diff +(the first file's diff is reused for every subsequent file). Find the loop and +ensure each iteration reads and assigns the correct diff for its own file. -Check current token: `curl -H "Authorization: token $GITEA_TOKEN" https://gitea.d-ma.be/api/v1/user` -If scopes are missing, update token in Gitea settings before running tests. +Reproduce: call `pr_files_diff` on any PR with 3+ files, verify each file has +a distinct diff. ### Definition of done -- `task check` passes (all tools, all batches) -- Each new tool manually callable via `claude mcp call` -- PR #1 (batch 1) merged before starting batch 2 -- Issue #19 (mirror flow e2e test) verified manually after batch 1 is deployed +- [ ] `task check` passes +- [ ] `repo_update` accepts `archived` and `template` params +- [ ] `archived=true` requires `confirm=` +- [ ] `create_project_from_template` accepts `template_name` param, defaults to `template-go-web` +- [ ] `pr_files_diff` returns distinct diff per file +- [ ] All new test cases pass +- [ ] PR `fix/v02-patch` merged to main via PR (not direct push) + +### After this sprint + +Next: `hyperguild new-project` v1 implementation. +See brain node `adr-new-project-gitea-first-github-mirror` for the full flow spec. +Also: verify end-to-end mirror flow (issue #19) once `repo_mirror_push` is confirmed working. diff --git a/CLAUDE.md b/CLAUDE.md index 934bde5..64260d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,6 +39,7 @@ - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:` - Branch naming: `feat/short-description`, `fix/short-description` - PRs: one concern per PR, description explains *why* not *what* +- **Branch protection:** always work on a feature branch, open a PR, never push directly to main ### Security - No secrets in code, ever — use env vars or SOPS-encrypted files @@ -76,68 +77,98 @@ When acting as a coding agent on this project: 3. If unsure about a convention, check `DECISIONS.md` or ask 4. Never modify files outside the project root without explicit permission 5. When adding a dependency, explain why in the commit message -6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM +6. Always work on a feature branch and open a PR — never push directly to main +7. For client projects: never send code or context to cloud APIs — use local models via LiteLLM -## Current sprint — gitea-mcp v0.2 (2026-05-14) +## Current sprint — gitea-mcp v0.2 patch (2026-05-14) ### Context -This sprint implements new MCP tools needed for `hyperguild new-project` — -the automated project creation flow triggered from claude.ai. See brain knowledge -nodes `adr-new-project-gitea-first-github-mirror` and `roadmap-github-ingestion-pipeline` -for full background. +The main v0.2 batch (repo_create, repo_update, repo_mirror_push, repo_delete, +repo_tree, repo_topics_update, file_read dir-fix, issue_get, release_create, +create_project_from_template) was implemented and pushed directly to main. -### Issues to implement (priority order) +This sprint fixes three remaining gaps found during code review on 2026-05-14. +These are blockers for `hyperguild new-project`. -**Batch 1 — blockers (do first, one PR: `feat/repo-crud`)** +### Issues to fix (all three in one PR: `fix/v02-patch`) -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #13 | `repo_create` | POST /api/v1/user/repos or /api/v1/orgs/{org}/repos | -| #16 | `repo_mirror_push` (add/list/delete) | POST/GET/DELETE /api/v1/repos/{owner}/{repo}/push_mirrors | -| #12 | `repo_update` | PATCH /api/v1/repos/{owner}/{repo} | +#### #12 — repo_update: add `archived` and `template` fields +**File:** `internal/gitea/repos.go` → `UpdateRepoArgs` struct +**File:** `internal/tools/repo_update.go` → input schema + args struct -**Batch 2 — quality of life (second PR: `feat/repo-ux`)** +Add to `UpdateRepoArgs`: +```go +Archived *bool +Template *bool +``` -| Issue | Tool | Gitea API | -|-------|------|-----------| -| #15 | `file_read` dir-path fix | existing endpoint, detect array vs object response | -| #14 | `repo_tree` | GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=true | -| #18 | `repo_topics_update` | PUT /api/v1/repos/{owner}/{repo}/topics | +Add to tool input schema: +```json +"archived": { + "type": "boolean", + "description": "Mark repo as archived (read-only). Requires confirm=." +}, +"template": { + "type": "boolean", + "description": "Toggle template repo flag." +} +``` -**Batch 3 — can wait** +Add confirm-guard for `archived=true` (same pattern as `private=false`): +```go +if args.Archived != nil && *args.Archived { + if args.Confirm != args.Name { + return nil, fmt.Errorf("setting archived=true is irreversible: set confirm=%q to proceed", args.Name) + } +} +``` -| Issue | Tool | Note | -|-------|------|------| -| #11 | `repo_delete` | HIGH risk — needs `confirm` param == repo name | -| #17 | `release_create` | POST /api/v1/repos/{owner}/{repo}/releases | +New test cases to add in `repo_update_test.go`: +- `TestRepoUpdateTool_Archive` — happy path with confirm +- `TestRepoUpdateTool_ArchiveRequiresConfirm` — missing confirm returns error +- `TestRepoUpdateTool_SetTemplate` — no confirm needed -### How to add a tool (pattern) +#### #24 — create_project_from_template: make template selectable +**File:** `internal/tools/create_project_from_template.go` -Every tool = 4 files following `internal/tools/repo_get.go` exactly: +Add optional `template_name` param to input schema: +```json +"template_name": { + "type": "string", + "enum": ["template-go-web", "template-go-agent"], + "description": "Template repo to generate from. Defaults to template-go-web.", + "default": "template-go-web" +} +``` -1. `internal/gitea/.go` — API client method (use PostJSON/PatchJSON/DeleteJSON) -2. `internal/tools/repo_.go` — tool handler with Descriptor() + Call() -3. `internal/tools/repo__test.go` — table-driven tests with httptest.NewServer -4. Registration in main — find where `NewRepoGet` is registered, add new tool same place +The tool should use `args.TemplateName` if set, fall back to the hardcoded default. +Remove the hardcoded template name from `cmd/gitea-mcp/main.go` constructor call — +the tool resolves it internally. -Key rules: -- Always call `t.a.Check(args.Owner)` before any API call (allowlist guard) -- Use `textOK(result)` for success output -- For `repo_mirror_push`: NEVER log or return `remote_password` in any output -- For `repo_update` with `private: false` and `repo_delete`: require `confirm` param == repo name +New test case: `TestCreateProjectFromTemplate_AgentTemplate` -### Token permissions needed +#### #25 — pr_files_diff: fix same diff returned for all files +**File:** `internal/tools/pr_files_diff.go` -New tools require these additional Gitea token scopes: -- `write:repository` — repo_create, repo_update, repo_mirror_push, repo_topics_update, release_create -- `delete_repo` — repo_delete +There is a loop bug where all file entries in the response contain the same diff +(the first file's diff is reused for every subsequent file). Find the loop and +ensure each iteration reads and assigns the correct diff for its own file. -Check current token: `curl -H "Authorization: token $GITEA_TOKEN" https://gitea.d-ma.be/api/v1/user` -If scopes are missing, update token in Gitea settings before running tests. +Reproduce: call `pr_files_diff` on any PR with 3+ files, verify each file has +a distinct diff. ### Definition of done -- `task check` passes (all tools, all batches) -- Each new tool manually callable via `claude mcp call` -- PR #1 (batch 1) merged before starting batch 2 -- Issue #19 (mirror flow e2e test) verified manually after batch 1 is deployed +- [ ] `task check` passes +- [ ] `repo_update` accepts `archived` and `template` params +- [ ] `archived=true` requires `confirm=` +- [ ] `create_project_from_template` accepts `template_name` param, defaults to `template-go-web` +- [ ] `pr_files_diff` returns distinct diff per file +- [ ] All new test cases pass +- [ ] PR `fix/v02-patch` merged to main via PR (not direct push) + +### After this sprint + +Next: `hyperguild new-project` v1 implementation. +See brain node `adr-new-project-gitea-first-github-mirror` for the full flow spec. +Also: verify end-to-end mirror flow (issue #19) once `repo_mirror_push` is confirmed working.