Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
834a994af9 | ||
|
|
0a12e905c9 |
@@ -288,7 +288,7 @@ Shared engineering skills are available in `~/dev/.skills/`. Load at task start
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ Shared engineering skills are available in `~/dev/.skills/`. Load at task start
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
+1
-1
@@ -291,7 +291,7 @@ Shared engineering skills are available in `~/dev/.skills/`. Load at task start
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ Shared engineering skills are available in `~/dev/.skills/`. Load at task start
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
- **Name**: gitea-mcp
|
- **Name**: gitea-mcp
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal
|
||||||
- **Repo**: https://gitea.d-ma.be/mathias/gitea-mcp
|
- **Repo**: https://git.d-ma.be/mathias/gitea-mcp
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|||||||
+3
-3
@@ -1,13 +1,13 @@
|
|||||||
FROM golang:1.26-alpine AS build
|
FROM golang:1.26-alpine AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Fetch internal gitea-hosted Go modules (e.g. mcp-chassis) without going
|
# Fetch internal git-hosted Go modules (e.g. mcp-chassis) without going
|
||||||
# through proxy.golang.org and without HTTP→HTTPS surprises. Gitea returns
|
# through proxy.golang.org and without HTTP→HTTPS surprises. Gitea returns
|
||||||
# http:// in its go-import meta tag, so rewrite to https here and bypass
|
# http:// in its go-import meta tag, so rewrite to https here and bypass
|
||||||
# the module proxy + sumdb.
|
# the module proxy + sumdb.
|
||||||
RUN apk add --no-cache git && \
|
RUN apk add --no-cache git && \
|
||||||
git config --global url."https://gitea.d-ma.be/".insteadOf "http://gitea.d-ma.be/"
|
git config --global url."https://git.d-ma.be/".insteadOf "http://git.d-ma.be/"
|
||||||
ENV GOPRIVATE=gitea.d-ma.be
|
ENV GOPRIVATE=git.d-ma.be
|
||||||
ENV GOPROXY=direct
|
ENV GOPROXY=direct
|
||||||
ENV GOSUMDB=off
|
ENV GOSUMDB=off
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import (
|
|||||||
"git.d-ma.be/mathias/gitea-mcp/internal/tools"
|
"git.d-ma.be/mathias/gitea-mcp/internal/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// version is the build version, overridable via -ldflags "-X main.version=<tag>".
|
||||||
|
// Defaults to "dev" for local / un-stamped builds (was a hardcoded, drifting
|
||||||
|
// "0.1.0" — it now tells the truth instead of a stale literal).
|
||||||
|
var version = "dev"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||||
// Route the chassis's package-level slog (auth audit logs, gitea-mcp#9) through
|
// Route the chassis's package-level slog (auth audit logs, gitea-mcp#9) through
|
||||||
@@ -69,7 +74,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addr := ":" + cfg.Port
|
addr := ":" + cfg.Port
|
||||||
logger.Info("gitea-mcp starting", "addr", addr, "version", "0.1.0")
|
logger.Info("gitea-mcp starting", "addr", addr, "version", version)
|
||||||
if err := http.ListenAndServe(addr, mux); err != nil {
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
||||||
logger.Error("server stopped", "err", err)
|
logger.Error("server stopped", "err", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func NewCreateProjectFromTemplate(c *gitea.Client, a *allowlist.Allowlist, tmplO
|
|||||||
func (t *CreateProjectFromTemplate) Descriptor() registry.ToolDescriptor {
|
func (t *CreateProjectFromTemplate) Descriptor() registry.ToolDescriptor {
|
||||||
return registry.ToolDescriptor{
|
return registry.ToolDescriptor{
|
||||||
Name: "create_project_from_template",
|
Name: "create_project_from_template",
|
||||||
Description: "Create a new project repo from a template. Best-effort substitution of placeholders (__PROJECT_NAME__, __MODULE_PATH__) in every file's content AND path (e.g. renaming cmd/__PROJECT_NAME__/): it completes only if the generated branch is promptly writable. If gitea's async generate is slow (infra#179) the repo is still created and partial_failure explains how to finalize locally (`hyperguild new-project`). Check files_substituted and partial_failure. Defaults to the server-configured template; pass template_name to override (e.g. template-go-agent). Pass dispatch_allow=true to also inject a .dispatch-allow file so the project is immediately dispatch-eligible (dispatch#3).",
|
Description: "Create a new project repo from a template. Best-effort substitution of placeholders (__PROJECT_NAME__, __MODULE_PATH__) in every file's content AND path (e.g. renaming cmd/__PROJECT_NAME__/): it completes only if the generated branch is promptly writable. If gitea's async generate is slow (infra#179) the repo is still created and partial_failure explains how to finish substituting the placeholders manually. Check files_substituted and partial_failure. Defaults to the server-configured template; pass template_name to override (e.g. template-go-agent). Pass dispatch_allow=true to also inject a .dispatch-allow file so the project is immediately dispatch-eligible (dispatch#3).",
|
||||||
InputSchema: json.RawMessage(`{
|
InputSchema: json.RawMessage(`{
|
||||||
"type":"object",
|
"type":"object",
|
||||||
"properties":{
|
"properties":{
|
||||||
@@ -209,11 +209,7 @@ func (t *CreateProjectFromTemplate) Call(ctx context.Context, raw json.RawMessag
|
|||||||
// is best-effort).
|
// is best-effort).
|
||||||
if strings.Contains(result.PartialFailure, "branch does not exist") ||
|
if strings.Contains(result.PartialFailure, "branch does not exist") ||
|
||||||
strings.Contains(result.PartialFailure, "not found") {
|
strings.Contains(result.PartialFailure, "not found") {
|
||||||
result.PartialFailure = fmt.Sprintf(
|
result.PartialFailure = infra179FinalizeMessage(
|
||||||
"repo created, but its branch (%s) was not writable within %ds — gitea's "+
|
|
||||||
"template-generate is slow-async on this instance (infra#179), so substitution "+
|
|
||||||
"is incomplete (%d file(s) done). Finalize locally with `hyperguild new-project` "+
|
|
||||||
"(clone + substitute, no API race). Underlying: %s",
|
|
||||||
branch, substitutionBudget, len(result.FilesSubstituted), result.PartialFailure)
|
branch, substitutionBudget, len(result.FilesSubstituted), result.PartialFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +223,21 @@ func (t *CreateProjectFromTemplate) Call(ctx context.Context, raw json.RawMessag
|
|||||||
return textOK(result)
|
return textOK(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// infra179FinalizeMessage explains the best-effort outcome when gitea's slow
|
||||||
|
// async template-generate (infra#179) leaves the branch unwritable within the
|
||||||
|
// budget. It names the concrete remaining work — substituting the two
|
||||||
|
// placeholders — rather than pointing at a specific tool, so the guidance stays
|
||||||
|
// correct regardless of scaffolding-CLI state (gitea-mcp#46).
|
||||||
|
func infra179FinalizeMessage(branch string, budget, done int, underlying string) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"repo created, but its branch (%s) was not writable within %ds — gitea's "+
|
||||||
|
"template-generate is slow-async on this instance (infra#179), so substitution "+
|
||||||
|
"is incomplete (%d file(s) done). Finish it by cloning the repo and replacing the "+
|
||||||
|
"remaining __PROJECT_NAME__ / __MODULE_PATH__ placeholders (in file contents and "+
|
||||||
|
"paths), then pushing; or retry create once the branch settles. Underlying: %s",
|
||||||
|
branch, budget, done, underlying)
|
||||||
|
}
|
||||||
|
|
||||||
// substitutionBudget bounds how long we retry the first write while the freshly
|
// substitutionBudget bounds how long we retry the first write while the freshly
|
||||||
// generated branch becomes writable. gitea's /generate returns (and serves reads)
|
// generated branch becomes writable. gitea's /generate returns (and serves reads)
|
||||||
// before the branch ref is committed, so writes 404 "branch does not exist" for a
|
// before the branch ref is committed, so writes 404 "branch does not exist" for a
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #46: the infra#179 finalize guidance must not point at a non-existent command
|
||||||
|
// (`hyperguild new-project` was never built). It should name the real remaining
|
||||||
|
// work — substituting the placeholders — so the caller isn't sent to a dead end.
|
||||||
|
func TestInfra179FinalizeMessage(t *testing.T) {
|
||||||
|
msg := infra179FinalizeMessage("main", 5, 2, "branch does not exist")
|
||||||
|
|
||||||
|
for _, want := range []string{"infra#179", "__PROJECT_NAME__", "__MODULE_PATH__", "branch does not exist"} {
|
||||||
|
if !strings.Contains(msg, want) {
|
||||||
|
t.Errorf("message missing %q\ngot: %s", want, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(msg, "hyperguild new-project") {
|
||||||
|
t.Errorf("message must not reference the defunct `hyperguild new-project` command\ngot: %s", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user