From 121af5af205f742ea3e6127eea7e663953680d65 Mon Sep 17 00:00:00 2001 From: Delqhi Date: Sun, 14 Jun 2026 11:30:37 +0200 Subject: [PATCH] feat(autodev): bridge OpenSIN-Code/autodev-cli v0.4.0 as stdio MCP sibling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cmd/sin-code/internal/autodev/: new stdlib-only bridge package (DefaultBin, IsInstalled, ResolveAutodevBin, ResolveAutodevMCPBin, Version). 11 hermetic unit tests under go test -race. No Python source vendored (M2 + AGENTS.md §2/§3 hard rule 'NOT a place to vendor tool implementations'). - cmd/sin-code/autodev_cmd.go: cobra constructor (NewAutodevCmd) with three subcommands: setup, doctor, version. Pattern matches gh_cmd.go / vane_cmd.go (Bridged-External-Contract, M4 + v3.8.0+). - cmd/sin-code/main.go: NewAutodevCmd wired into the root AddCommand chain next to NewSummaryCmd; help text declares the bridging scope explicitly. - cmd/sin-code/internal/mcpclient/registry.go: adds '{Name: "autodev", Transport: "stdio", Command: "autodev-mcp"}' so sin-code MCP clients (sin-code serve, sin-code chat) spawn autodev-mcp as a Python stdio MCP sibling and merge the 6 autodev_* tools into the aggregate toolset the agent loop sees. - cmd/sin-code/internal/permission_defaults.go: 7 new rules (split M4 policy). Read-only -> allow (autodev__status, autodev__lessons). Mutating -> ask (autodev__init, autodev__run_experiment, autodev__swarm, autodev__session_log). Backstop catch-all autodev__* -> ask (mirrors the sin_bash default). - ECOSYSTEM.md: new row under 'MCP Skill Servers' table with the literal `autodev__*` marker required by the ecosystem-sync CI gate. - requirements-ecosystem.txt: pinned OpenSIN-Code/autodev-cli@v0.4.0 in the 'MCP skill servers' section. Sync enforcement: ecosystem-sync.yml CI gate is green for the autodev entry across {registry.go, permission_defaults.go, ECOSYSTEM.md, requirements-ecosystem.txt}; no drift. Sister-consumer cross-link: OpenSIN-Code/SIN-Code-WebUI-v2@6a92d92 already exposes the same toolset via lib/sin/mcp.ts:getAutodevTools(); Go-server integration is the missing piece completed here. Refs: OpenSIN-Code/autodev-cli@e8c948a (v0.4.0). Closes #73. --- ECOSYSTEM.md | 1 + cmd/sin-code/autodev_cmd.go | 128 +++++++++++ cmd/sin-code/internal/autodev/autodev.go | 121 +++++++++++ cmd/sin-code/internal/autodev/autodev_test.go | 200 ++++++++++++++++++ cmd/sin-code/internal/mcpclient/registry.go | 3 + cmd/sin-code/internal/permission_defaults.go | 13 ++ cmd/sin-code/main.go | 2 +- requirements-ecosystem.txt | 1 + 8 files changed, 468 insertions(+), 1 deletion(-) create mode 100644 cmd/sin-code/autodev_cmd.go create mode 100644 cmd/sin-code/internal/autodev/autodev.go create mode 100644 cmd/sin-code/internal/autodev/autodev_test.go diff --git a/ECOSYSTEM.md b/ECOSYSTEM.md index ca53ec2..b8c7ad1 100644 --- a/ECOSYSTEM.md +++ b/ECOSYSTEM.md @@ -47,6 +47,7 @@ | SIN-Browser-Tools | `browser__*` (106 tools) | ask | ACTIVE | | GitHub CLI (gh) | `gh_query`, `gh_health`, `gh_execute` | allow / allow / ask (M4) | ACTIVE | +| [OpenSIN-Code/autodev-cli](https://github.com/OpenSIN-Code/autodev-cli) | `autodev__*` (e.g. `autodev_status`, `autodev_lessons`, `autodev_init`, `autodev_run_experiment`, `autodev_swarm`, `autodev_session_log`) | allow (read-only) + ask (mutating) — split M4 policy | ## LLM Backends | Repo | Integration | Status | diff --git a/cmd/sin-code/autodev_cmd.go b/cmd/sin-code/autodev_cmd.go new file mode 100644 index 0000000..bd8c199 --- /dev/null +++ b/cmd/sin-code/autodev_cmd.go @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +// Purpose: `sin-code autodev` — Cobra constructor for the +// OpenSIN-Code/autodev-cli (Python, MIT, v0.4.0) bridge. Mirrors +// gh_cmd.go (M4 + v3.8.0+ Bridged-External pattern): own subcommand +// set, transparent stdout/stderr forwarding, no business logic, +// Python never vendored. Setup / doctor / version are pure shell-out +// to autodev-cli and rely on internal/autodev for binary discovery. +// Docs: autodev.doc.md +package main + +import ( + "context" + "fmt" + "os" + "os/exec" + "time" + + "github.com/OpenSIN-Code/SIN-Code/cmd/sin-code/internal/autodev" + "github.com/spf13/cobra" +) + +// NewAutodevCmd builds the `autodev` cobra subcommand. Pattern matches +// NewGhCmd / NewVaneCmd: returns *cobra.Command with setup / doctor / +// version attached. All verbs are one-line shell-out via autodev-cli; +// no reserialization, no interception, no caching. +func NewAutodevCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "autodev", + Short: "Bridge to OpenSIN-Code/autodev-cli (Python autoresearch loop, never vendored)", + Long: `sin-code autodev shells out to the user-installed autodev-cli +(https://github.com/OpenSIN-Code/autodev-cli, MIT, v0.4.0) without ever +vendoring its Python sources. This subcommand is the operator-facing +entry point: setup, doctor, version. Each verb forwards stdout and +stderr transparently — the calling agent loop sees exactly what +autodev-cli printed, with no reserialization or comment injection. + +Binary resolution honors $AUTODEV_BIN (override) and falls back to +"autodev" on $PATH. Install autodev-cli separately (pipx / pip) — +this subcommand refuses to run if it cannot find the binary, with +the exact lookup error surfaced so the operator can fix PATH.`, + } + cmd.AddCommand(newAutodevSetupCmd()) + cmd.AddCommand(newAutodevDoctorCmd()) + cmd.AddCommand(newAutodevVersionCmd()) + return cmd +} + +// ── setup ───────────────────────────────────────────────────────────── + +// newAutodevSetupCmd runs `autodev init --json .` in the operator's +// working directory. Idempotent: autodev-cli's init tolerates a +// pre-initialized project. Exit code propagates through cobra. +func newAutodevSetupCmd() *cobra.Command { + return &cobra.Command{ + Use: "setup", + Short: "Initialize a project for autodev-cli (runs `autodev init --json .`)", + Long: `Idempotent. Forwards stdout/stderr from 'autodev init --json .' +verbatim. Non-zero exit propagates through cobra so CI / agent loops +can detect partial init. Set $AUTODEV_BIN to override the binary.`, + RunE: func(cmd *cobra.Command, _ []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 2*time.Minute) + defer cancel() + return runPassthrough(ctx, autodev.DefaultBin(), "init", "--json", ".") + }, + } +} + +// ── doctor ──────────────────────────────────────────────────────────── + +// newAutodevDoctorCmd runs `autodev status --json`. Output is the +// canonical JSON blob autodev-cli's autodev-mcp tool consumes; echoing +// it byte-for-byte lets downstream MCP tools round-trip. +func newAutodevDoctorCmd() *cobra.Command { + return &cobra.Command{ + Use: "doctor", + Short: "Show current autodev project state (runs `autodev status --json`)", + Long: `Runs 'autodev status --json' and forwards its JSON output +verbatim so MCP consumers receive the same document upstream produced. +Exit code propagates.`, + RunE: func(cmd *cobra.Command, _ []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Second) + defer cancel() + return runPassthrough(ctx, autodev.DefaultBin(), "status", "--json") + }, + } +} + +// ── version ─────────────────────────────────────────────────────────── + +// newAutodevVersionCmd shells out to `autodev --version` and prints +// the trimmed stdout on a single line. Errors surface the wrapped +// upstream message so the operator can tell whether the binary is +// absent (typical CI case) or upstream lacks the flag (transient). +func newAutodevVersionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Report upstream autodev-cli --version", + Long: `Shells out to 'autodev --version' (per upstream contract) and prints the trimmed stdout line. Non-zero exit / stderr / empty stdout surface as a cobra error.`, + RunE: func(cmd *cobra.Command, _ []string) error { + v, err := autodev.Version() + if err != nil { + // Surface upstream's stderr verbatim so the operator + // can see WHY `--version` failed (e.g. "No such + // option --version" until upstream lands the flag). + fmt.Fprintln(cmd.ErrOrStderr(), "✗ autodev version probe failed:", err) + return err + } + fmt.Fprintln(cmd.OutOrStdout(), v) + return nil + }, + } +} + +// ── helpers ─────────────────────────────────────────────────────────── + +// runPassthrough executes bin with the given ctx and pipes +// stdout + stderr bit-for-bit to the parent process. Gates on +// autodev.ResolveAutodevBin() so the operator gets a clean install +// hint instead of a stack trace if autodev-cli is missing. +func runPassthrough(ctx context.Context, bin string, args ...string) error { + if err := autodev.ResolveAutodevBin(); err != nil { + return fmt.Errorf("autodev bridge: %w (set $AUTODEV_BIN or install %s)", err, bin) + } + c := exec.CommandContext(ctx, bin, args...) + c.Stdout = os.Stdout // transparent: no reserialization + c.Stderr = os.Stderr // transparent: no reserialization + return c.Run() +} diff --git a/cmd/sin-code/internal/autodev/autodev.go b/cmd/sin-code/internal/autodev/autodev.go new file mode 100644 index 0000000..c886143 --- /dev/null +++ b/cmd/sin-code/internal/autodev/autodev.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +// Purpose: autodev bridge — stdlib-only binary discovery + version probe +// for the OpenSIN-Code/autodev-cli (Python, MIT, v0.4.0) CLI/MCP. The +// bridge itself is the runtime subprocess boundary; Python sources are +// never vendored (M2: single static Go binary, CGO_ENABLED=0, and +// "NOT a place to vendor tool implementations that live in their own +// repos" — AGENTS.md §2). Stdlib-only imports: os, context, os/exec, +// errors, fmt, strings. No modelcontextprotocol/go-sdk here — that +// dependency belongs to mcpclient/registry.go callers, not the bridge. +// Docs: autodev.doc.md +package autodev + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "strings" +) + +// Env names for binary overrides (highest priority). Set these to point +// the bridge at locally-built / pinned copies without rebuilding sin-code. +const ( + envBin = "AUTODEV_BIN" // overrides DefaultBin() + envMCPBin = "AUTODEV_MCP_BIN" // overrides DefaultMCPBin() +) + +// DefaultBin returns the autodev CLI binary path the bridge will +// invoke, honoring $AUTODEV_BIN when set and non-empty (trimmed). +// Falls back to "autodev" (PATH-resolved at exec time). +func DefaultBin() string { + if v := strings.TrimSpace(os.Getenv(envBin)); v != "" { + return v + } + return "autodev" +} + +// DefaultMCPBin returns the autodev-mcp stdio server binary, honoring +// $AUTODEV_MCP_BIN. Falls back to "autodev-mcp". +func DefaultMCPBin() string { + if v := strings.TrimSpace(os.Getenv(envMCPBin)); v != "" { + return v + } + return "autodev-mcp" +} + +// IsInstalled reports whether bin resolves on PATH (or as an absolute +// path). Safe on empty / whitespace-only input (returns false). +func IsInstalled(bin string) bool { + if strings.TrimSpace(bin) == "" { + return false + } + _, err := exec.LookPath(bin) + return err == nil +} + +// ErrNotInstalled is returned by ResolveAutodevBin / ResolveAutodevMCPBin +// when the resolved binary is missing. Wrapped exec.ErrNotFound so +// callers can errors.Is() either layer. +var ErrNotInstalled = errors.New("autodev: binary not installed") + +// ResolveAutodevBin returns ErrNotInstalled (wrapping exec.ErrNotFound) +// when DefaultBin() does not resolve on PATH or as an absolute path. +func ResolveAutodevBin() error { return resolve(DefaultBin()) } + +// ResolveAutodevMCPBin returns ErrNotInstalled when DefaultMCPBin() is +// absent. Identical semantics to ResolveAutodevBin; split into two +// functions so callers can tick the right install gate independently. +func ResolveAutodevMCPBin() error { return resolve(DefaultMCPBin()) } + +// Version shells out to `autodev --version` (per the upstream bridge +// contract) and returns the trimmed stdout on clean exit. Any non-zero +// exit code, non-empty stderr, or empty stdout becomes an error so the +// caller can branch on upstream presence. Stdlib-only — no JSON, no +// regex, no side imports. +func Version() (string, error) { + return versionWith(context.Background()) +} + +// versionWith is the context-aware test seam. Production code calls +// Version(); tests inject deadlines or background as needed. +func versionWith(ctx context.Context) (string, error) { + bin := DefaultBin() + cmd := exec.CommandContext(ctx, bin, "--version") + var stdout, stderr strings.Builder + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + // Prefer upstream's stderr (rich diagnostics) over the wrapped + // exit error so the operator sees WHY upstream rejected the + // flag (e.g. "No such option --version" until upstream lands + // one — tracked upstream). + msg := strings.TrimSpace(stderr.String()) + if msg == "" { + msg = strings.TrimSpace(stdout.String()) + } + if msg == "" { + msg = err.Error() + } + return "", fmt.Errorf("autodev: %s --version failed: %w: %s", bin, err, msg) + } + out := strings.TrimSpace(stdout.String()) + if out == "" { + return "", fmt.Errorf("autodev: %s --version returned empty stdout", bin) + } + return out, nil +} + +// resolve is the shared body of the two Resolve* funcs. Centralised so +// the error-wrapping rule (ErrNotInstalled wraps exec.ErrNotFound) is +// impossible to forget in a new Resolve path. +func resolve(bin string) error { + if strings.TrimSpace(bin) == "" { + return fmt.Errorf("%w: empty bin name", ErrNotInstalled) + } + if _, err := exec.LookPath(bin); err != nil { + return fmt.Errorf("%w: %s: %w", ErrNotInstalled, bin, err) + } + return nil +} diff --git a/cmd/sin-code/internal/autodev/autodev_test.go b/cmd/sin-code/internal/autodev/autodev_test.go new file mode 100644 index 0000000..5252458 --- /dev/null +++ b/cmd/sin-code/internal/autodev/autodev_test.go @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +// Purpose: tests for the autodev bridge package. All tests are hermetic +// — no real autodev binary is invoked, no real network. Each test that +// would normally shell out to `autodev` rewrites $PATH via t.Setenv to +// point at t.TempDir(), where a POSIX shell-script shim is installed +// (mode 0o755, exec.LookPath-resolvable). Stdlib-only imports per +// package contract. +// Docs: autodev.doc.md +package autodev + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +// ── Test helpers ────────────────────────────────────────────────────── + +// writeFakeBinary installs a POSIX shell script `name` at +// filepath.Join(dir, name) that prints `stdout` then exits `code`. +// Mode 0o755 so exec.LookPath() finds it through PATH. +func writeFakeBinary(t *testing.T, dir, name, stdout string, code int) string { + t.Helper() + body := fmt.Sprintf("#!/bin/sh\necho %q\nexit %d\n", stdout, code) + p := filepath.Join(dir, name) + if err := os.WriteFile(p, []byte(body), 0o755); err != nil { + t.Fatalf("write fake binary %s: %v", p, err) + } + return p +} + +// prependPath returns a new PATH string with dir at the front so the +// fake binary shadows the real one for the duration of this test. +func prependPath(dir string) string { + if cur := os.Getenv("PATH"); cur != "" { + return dir + string(os.PathListSeparator) + cur + } + return dir +} + +// ── DefaultBin / DefaultMCPBin ──────────────────────────────────────── + +func TestDefaultBin_ReturnsDefault(t *testing.T) { + t.Setenv("AUTODEV_BIN", "") + if got := DefaultBin(); got != "autodev" { + t.Errorf("DefaultBin() = %q, want %q", got, "autodev") + } +} + +func TestDefaultBin_EnvOverrideTrimmed(t *testing.T) { + t.Setenv("AUTODEV_BIN", "/opt/local/bin/my-autodev") + if got := DefaultBin(); got != "/opt/local/bin/my-autodev" { + t.Errorf("DefaultBin() = %q, want literal override", got) + } + t.Setenv("AUTODEV_BIN", " /tmp/x ") + if got := DefaultBin(); got != "/tmp/x" { + t.Errorf("DefaultBin() = %q, want trimmed override", got) + } +} + +func TestDefaultMCPBin_EnvAndDefault(t *testing.T) { + t.Setenv("AUTODEV_MCP_BIN", "") + if got := DefaultMCPBin(); got != "autodev-mcp" { + t.Errorf("DefaultMCPBin() = %q, want %q", got, "autodev-mcp") + } + t.Setenv("AUTODEV_MCP_BIN", "/usr/local/bin/autodev-mcp") + if got := DefaultMCPBin(); got != "/usr/local/bin/autodev-mcp" { + t.Errorf("DefaultMCPBin() = %q, want override", got) + } +} + +// ── IsInstalled ─────────────────────────────────────────────────────── + +func TestIsInstalled_HappyPaths(t *testing.T) { + dir := t.TempDir() + p := writeFakeBinary(t, dir, "autodev-mcp-fake", "x", 0) + if !IsInstalled(p) { + t.Errorf("IsInstalled(absolute %q) = false, want true", p) + } + t.Setenv("PATH", prependPath(dir)) + if !IsInstalled("autodev-mcp-fake") { + t.Errorf("IsInstalled(PATH-resident name) = false, want true") + } +} + +func TestIsInstalled_RejectsEmptyAndMissing(t *testing.T) { + if IsInstalled("") { + t.Error("IsInstalled(\"\") = true, want false") + } + if IsInstalled(" ") { + t.Error("IsInstalled(\" \") = true, want false (whitespace)") + } + t.Setenv("PATH", t.TempDir()) + if IsInstalled("this-binary-really-does-not-exist-anywhere-xyzzy") { + t.Error("IsInstalled(missing) = true, want false") + } +} + +// ── ResolveAutodevMCPBin ────────────────────────────────────────────── + +func TestResolveAutodevMCPBin_OK(t *testing.T) { + dir := t.TempDir() + writeFakeBinary(t, dir, "autodev-mcp-fake", "ok", 0) + t.Setenv("AUTODEV_MCP_BIN", "autodev-mcp-fake") + t.Setenv("PATH", prependPath(dir)) + if err := ResolveAutodevMCPBin(); err != nil { + t.Errorf("ResolveAutodevMCPBin() = %v, want nil", err) + } +} + +func TestResolveAutodevMCPBin_NotInstalledWrapsErr(t *testing.T) { + t.Setenv("AUTODEV_MCP_BIN", "definitely-not-installed-xyzzy") + t.Setenv("PATH", t.TempDir()) + err := ResolveAutodevMCPBin() + if err == nil { + t.Fatal("ResolveAutodevMCPBin() = nil err, want non-nil") + } + if !errors.Is(err, ErrNotInstalled) { + t.Errorf("err = %v, does not wrap ErrNotInstalled", err) + } + if !errors.Is(err, exec.ErrNotFound) { + t.Errorf("err = %v, does not wrap exec.ErrNotFound", err) + } +} + +// ── Version (fake binary paths — the bridged subprocess contract) ───── + +func TestVersion_FakeBinary_HappyPath(t *testing.T) { + // The shim writes only stdout, exits 0. versionWith trims and + // returns; no panic, no error wrapping. + dir := t.TempDir() + writeFakeBinary(t, dir, "autodev-fake", "v0.4.0\n", 0) + t.Setenv("AUTODEV_BIN", "autodev-fake") + t.Setenv("PATH", prependPath(dir)) + + got, err := Version() + if err != nil { + t.Fatalf("Version() err = %v, want nil", err) + } + if got != "v0.4.0" { + t.Errorf("Version() = %q, want %q (trimmed)", got, "v0.4.0") + } +} + +func TestVersion_FakeBinary_ExitNonZero_IsErrorPath(t *testing.T) { + // Fake-process error path (runcount #1 in spec): shim exits 1 with + // stderr populated. versionWith must surface a wrapped error + // carrying the stderr payload and an empty return string. + dir := t.TempDir() + writeFakeBinary(t, dir, "autodev-broken", "ignored-on-stdout", 1) + t.Setenv("AUTODEV_BIN", "autodev-broken") + t.Setenv("PATH", prependPath(dir)) + + got, err := Version() + if err == nil { + t.Fatal("Version() err = nil, want non-nil on exit 1") + } + if got != "" { + t.Errorf("Version() = %q, want empty string on error", got) + } + if !strings.Contains(err.Error(), "exit status 1") { + t.Errorf("Version() err %q missing exec.ExitError wrap", err.Error()) + } +} + +func TestVersion_BinaryMissing_IsErrorPath(t *testing.T) { + // Fake-process error path (run #2): bin name unresolvable. Surfaced + // as wrapped exec error, not as a panic. + t.Setenv("AUTODEV_BIN", "autodev-ghost") + t.Setenv("PATH", t.TempDir()) + + got, err := Version() + if err == nil { + t.Fatal("Version() err = nil, want non-nil on missing bin") + } + if got != "" { + t.Errorf("Version() = %q, want empty on missing bin", got) + } +} + +func TestVersion_PropagatesContextDeadline(t *testing.T) { + // Driving versionWith directly: a cancel-already ctx should not + // block — it returns quickly. We do not assert specific error text + // (varies by OS), only that the call returns within a few seconds. + dir := t.TempDir() + writeFakeBinary(t, dir, "autodev-fake", "v0.4.0", 0) + t.Setenv("AUTODEV_BIN", "autodev-fake") + t.Setenv("PATH", prependPath(dir)) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() // already-deadline before the call + if _, err := versionWith(ctx); err == nil { + t.Errorf("versionWith(cancelled ctx) = (value, nil), want err on cancelled ctx") + } +} diff --git a/cmd/sin-code/internal/mcpclient/registry.go b/cmd/sin-code/internal/mcpclient/registry.go index dca3d58..c6cefb6 100644 --- a/cmd/sin-code/internal/mcpclient/registry.go +++ b/cmd/sin-code/internal/mcpclient/registry.go @@ -40,6 +40,9 @@ func DefaultServers() []ServerConfig { py("SIN-Browser-Tools"), py("Simone-MCP"), py("SIN-Code-Symfony-Lens"), + + // External MCP server (Python stdio) — autodev-cli v0.4.0 (Bridged-External, never vendored) + {Name: "autodev", Transport: "stdio", Command: "autodev-mcp"}, } } diff --git a/cmd/sin-code/internal/permission_defaults.go b/cmd/sin-code/internal/permission_defaults.go index c6b0eb7..65e1b98 100644 --- a/cmd/sin-code/internal/permission_defaults.go +++ b/cmd/sin-code/internal/permission_defaults.go @@ -43,6 +43,19 @@ func DefaultPermissionRules() []permission.Rule { {Tool: "sin_bash", Policy: "ask"}, {Tool: "sin_sbom_generate", Policy: "allow"}, {Tool: "sin_security_scan", Policy: "allow"}, + + + // v3.16.0: autodev-cli bridge (Bridged-External + autodev-mcp stdio MCP). + // Qualified name = server-name + "__" + tool-name (registry.go "autodev" + autodev-mcp tools). + // Split mirrors the gh precedent at lines 40-42: read-only -> allow, mutating -> ask (M4). + {Tool: "autodev__status", Policy: "allow"}, + {Tool: "autodev__lessons", Policy: "allow"}, + {Tool: "autodev__init", Policy: "ask"}, + {Tool: "autodev__run_experiment", Policy: "ask"}, + {Tool: "autodev__swarm", Policy: "ask"}, + {Tool: "autodev__session_log", Policy: "ask"}, + // Backstop catch-all (mirrors sin_bash default at line 44 for unmatched prefixes). + {Tool: "autodev__*", Policy: "ask"}, {Tool: "*", Policy: "ask"}, } } diff --git a/cmd/sin-code/main.go b/cmd/sin-code/main.go index c92e028..9908cf9 100644 --- a/cmd/sin-code/main.go +++ b/cmd/sin-code/main.go @@ -79,7 +79,7 @@ func init() { rootCmd.AddCommand(NewChatCmd(), NewSessionsCmd(), NewMCPCmd(), NewGoalCmd(), NewDaemonCmd(), NewSkillCmd(), NewSwarmCmd(), NewSuperpowersCmd(), NewDoxCmd(), NewVaneCmd(), NewStackCmd(), NewGhCmd(), NewHubCmd(), - NewLedgerCmd(), NewSummaryCmd(), // v3.4.0 + v3.5.0 autonomy suite + v3.6.0 swarm + v3.7.0 superpowers + v3.7.0 dox + v3.8.0 vane + v3.8.0 stack + v3.9.0 gh + v3.12.0 hub + v3.13.0 ledger/summary + NewLedgerCmd(), NewSummaryCmd(), NewAutodevCmd(), // v3.4.0 + v3.5.0 + v3.6.0 + v3.7.0 + v3.8.0 + v3.9.0 + v3.12.0 + v3.13.0 + autodev-bridge (Python MIT v0.4.0, stdio MCP via autodev-mcp) ) // Pass build-time version to self-update module. diff --git a/requirements-ecosystem.txt b/requirements-ecosystem.txt index 65a2b88..2b2fe6c 100644 --- a/requirements-ecosystem.txt +++ b/requirements-ecosystem.txt @@ -29,6 +29,7 @@ SIN-Code-Frontend-Design-Skill==main SIN-Code-MCP-Server-Builder-Skill==main SIN-Browser-Tools==main Simone-MCP==main +OpenSIN-Code/autodev-cli @ git+https://github.com/OpenSIN-Code/autodev-cli@v0.4.0 # LLM backends coder-SIN-Qwen==main