fix(create_project): drop defunct hyperguild new-project from finalize guidance (#46)
The infra#179 partial_failure message and the tool descriptor told callers to "Finalize locally with `hyperguild new-project`" — but that command does not exist (the hyperguild CLI only has tier/brain/mode; it was specced, never built). It pointed users at a dead end. Extracted the message into a pure infra179FinalizeMessage() and reworded it to name the actual remaining work — cloning the repo and substituting the leftover __PROJECT_NAME__ / __MODULE_PATH__ placeholders, or retrying — with no reference to any scaffolding CLI. Descriptor updated to match. Unit-tested the wording. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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