feat(agentic): add language detection tools
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
1cd9672179
commit
cacb8977bf
6 changed files with 196 additions and 0 deletions
|
|
@ -25,6 +25,8 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
|||
c.Command("brain/ingest", core.Command{Description: "Bulk ingest memories into OpenBrain", Action: s.cmdBrainIngest})
|
||||
c.Command("brain/seed-memory", core.Command{Description: "Import markdown memories into OpenBrain from a project memory directory", Action: s.cmdBrainSeedMemory})
|
||||
c.Command("brain/list", core.Command{Description: "List memories in OpenBrain", Action: s.cmdBrainList})
|
||||
c.Command("lang/detect", core.Command{Description: "Detect the primary language for a repository or workspace", Action: s.cmdLangDetect})
|
||||
c.Command("lang/list", core.Command{Description: "List supported language identifiers", Action: s.cmdLangList})
|
||||
c.Command("plan-cleanup", core.Command{Description: "Permanently delete archived plans past the retention period", Action: s.cmdPlanCleanup})
|
||||
c.Command("pr-manage", core.Command{Description: "Manage open PRs (merge, close, review)", Action: s.cmdPRManage})
|
||||
c.Command("status", core.Command{Description: "List agent workspace statuses", Action: s.cmdStatus})
|
||||
|
|
@ -32,6 +34,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
|||
c.Command("extract", core.Command{Description: "Extract a workspace template to a directory", Action: s.cmdExtract})
|
||||
s.registerPlanCommands()
|
||||
s.registerTaskCommands()
|
||||
s.registerLanguageCommands()
|
||||
}
|
||||
|
||||
// ctx := s.commandContext()
|
||||
|
|
|
|||
|
|
@ -1022,6 +1022,8 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
|
|||
assert.Contains(t, cmds, "status")
|
||||
assert.Contains(t, cmds, "prompt")
|
||||
assert.Contains(t, cmds, "extract")
|
||||
assert.Contains(t, cmds, "lang/detect")
|
||||
assert.Contains(t, cmds, "lang/list")
|
||||
assert.Contains(t, cmds, "plan")
|
||||
assert.Contains(t, cmds, "plan/create")
|
||||
assert.Contains(t, cmds, "plan/list")
|
||||
|
|
|
|||
107
pkg/agentic/lang.go
Normal file
107
pkg/agentic/lang.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
var supportedLanguages = []string{"go", "php", "ts", "rust", "py", "cpp", "docker"}
|
||||
|
||||
// input := agentic.LanguageDetectInput{Path: "/workspace/pkg/agentic"}
|
||||
type LanguageDetectInput struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// out := agentic.LanguageDetectOutput{Success: true, Path: "/workspace/pkg/agentic", Language: "go"}
|
||||
type LanguageDetectOutput struct {
|
||||
Success bool `json:"success"`
|
||||
Path string `json:"path"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
// out := agentic.LanguageListOutput{Success: true, Count: 7, Languages: []string{"go", "php"}}
|
||||
type LanguageListOutput struct {
|
||||
Success bool `json:"success"`
|
||||
Count int `json:"count"`
|
||||
Languages []string `json:"languages"`
|
||||
}
|
||||
|
||||
type LanguageListInput struct{}
|
||||
|
||||
func (s *PrepSubsystem) registerLanguageCommands() {
|
||||
c := s.Core()
|
||||
c.Command("lang/detect", core.Command{Description: "Detect the primary language for a workspace or repository", Action: s.cmdLangDetect})
|
||||
c.Command("lang/list", core.Command{Description: "List supported language identifiers", Action: s.cmdLangList})
|
||||
}
|
||||
|
||||
// result := c.Command("lang/detect").Run(ctx, core.NewOptions(core.Option{Key: "path", Value: "."}))
|
||||
func (s *PrepSubsystem) cmdLangDetect(options core.Options) core.Result {
|
||||
path := optionStringValue(options, "_arg", "path", "repo")
|
||||
if path == "" {
|
||||
core.Print(nil, "usage: core-agent lang detect <path>")
|
||||
return core.Result{Value: core.E("agentic.cmdLangDetect", "path is required", nil), OK: false}
|
||||
}
|
||||
|
||||
_, output, err := s.langDetect(context.Background(), nil, LanguageDetectInput{Path: path})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "path: %s", output.Path)
|
||||
core.Print(nil, "language: %s", output.Language)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
// result := c.Command("lang/list").Run(ctx, core.NewOptions())
|
||||
func (s *PrepSubsystem) cmdLangList(_ core.Options) core.Result {
|
||||
_, output, err := s.langList(context.Background(), nil, LanguageListInput{})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "%d language(s)", output.Count)
|
||||
for _, language := range output.Languages {
|
||||
core.Print(nil, " %s", language)
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) registerLanguageTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "lang_detect",
|
||||
Description: "Detect the primary language for a workspace or repository path.",
|
||||
}, s.langDetect)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "lang_list",
|
||||
Description: "List supported language identifiers.",
|
||||
}, s.langList)
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) langDetect(_ context.Context, _ *mcp.CallToolRequest, input LanguageDetectInput) (*mcp.CallToolResult, LanguageDetectOutput, error) {
|
||||
path := core.Trim(input.Path)
|
||||
if path == "" {
|
||||
path = "."
|
||||
}
|
||||
language := detectLanguage(path)
|
||||
return nil, LanguageDetectOutput{
|
||||
Success: true,
|
||||
Path: path,
|
||||
Language: language,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) langList(_ context.Context, _ *mcp.CallToolRequest, _ LanguageListInput) (*mcp.CallToolResult, LanguageListOutput, error) {
|
||||
languages := append([]string(nil), supportedLanguages...)
|
||||
return nil, LanguageListOutput{
|
||||
Success: true,
|
||||
Count: len(languages),
|
||||
Languages: languages,
|
||||
}, nil
|
||||
}
|
||||
81
pkg/agentic/lang_test.go
Normal file
81
pkg/agentic/lang_test.go
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLang_CmdLangDetect_Good_Go(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
dir := t.TempDir()
|
||||
require.True(t, fs.Write(core.JoinPath(dir, "go.mod"), "module example").OK)
|
||||
|
||||
r := s.cmdLangDetect(core.NewOptions(core.Option{Key: "_arg", Value: dir}))
|
||||
require.True(t, r.OK)
|
||||
|
||||
output, ok := r.Value.(LanguageDetectOutput)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, dir, output.Path)
|
||||
assert.Equal(t, "go", output.Language)
|
||||
}
|
||||
|
||||
func TestLang_CmdLangDetect_Bad_MissingPath(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
r := s.cmdLangDetect(core.NewOptions())
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestLang_CmdLangDetect_Ugly_PreferenceOrder(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
dir := t.TempDir()
|
||||
require.True(t, fs.Write(core.JoinPath(dir, "go.mod"), "module example").OK)
|
||||
require.True(t, fs.Write(core.JoinPath(dir, "package.json"), "{}").OK)
|
||||
|
||||
r := s.cmdLangDetect(core.NewOptions(core.Option{Key: "_arg", Value: dir}))
|
||||
require.True(t, r.OK)
|
||||
|
||||
output, ok := r.Value.(LanguageDetectOutput)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "go", output.Language)
|
||||
}
|
||||
|
||||
func TestLang_LangDetect_Good_PHP(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
dir := t.TempDir()
|
||||
require.True(t, fs.Write(core.JoinPath(dir, "composer.json"), "{}").OK)
|
||||
|
||||
_, output, err := s.langDetect(context.Background(), (*mcp.CallToolRequest)(nil), LanguageDetectInput{Path: dir})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dir, output.Path)
|
||||
assert.Equal(t, "php", output.Language)
|
||||
}
|
||||
|
||||
func TestLang_LangList_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
|
||||
_, output, err := s.langList(context.Background(), (*mcp.CallToolRequest)(nil), LanguageListInput{})
|
||||
require.NoError(t, err)
|
||||
require.True(t, output.Success)
|
||||
assert.Equal(t, len(supportedLanguages), output.Count)
|
||||
assert.Equal(t, supportedLanguages, output.Languages)
|
||||
}
|
||||
|
||||
func TestLang_LangList_Ugly_CopyIsolation(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
|
||||
_, first, err := s.langList(context.Background(), (*mcp.CallToolRequest)(nil), LanguageListInput{})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, first.Languages)
|
||||
first.Languages[0] = "mutated"
|
||||
|
||||
_, second, err := s.langList(context.Background(), (*mcp.CallToolRequest)(nil), LanguageListInput{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, supportedLanguages[0], second.Languages[0])
|
||||
}
|
||||
|
|
@ -347,6 +347,7 @@ func (s *PrepSubsystem) RegisterTools(server *mcp.Server) {
|
|||
s.registerIssueTools(server)
|
||||
s.registerSprintTools(server)
|
||||
s.registerContentTools(server)
|
||||
s.registerLanguageTools(server)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "agentic_scan",
|
||||
|
|
|
|||
|
|
@ -584,6 +584,8 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
|
|||
assert.Contains(t, c.Commands(), "brain/ingest")
|
||||
assert.Contains(t, c.Commands(), "brain/seed-memory")
|
||||
assert.Contains(t, c.Commands(), "brain/list")
|
||||
assert.Contains(t, c.Commands(), "lang/detect")
|
||||
assert.Contains(t, c.Commands(), "lang/list")
|
||||
assert.Contains(t, c.Commands(), "plan-cleanup")
|
||||
assert.Contains(t, c.Commands(), "task")
|
||||
assert.Contains(t, c.Commands(), "task/create")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue