step 10: plugins system (Lua + templates) + DokuWiki as optional plugin
Plugin Manager: - Discover plugins from .verstak/plugins/<name>/plugin.json - Enable/disable per plugin - Template definitions (JSON) → pre-filled node trees - SQL migrations from plugins - Built-in templates loaded from internal/core/plugins/builtin/templates/ Lua Runtime: - Stub (gopher-lua placeholder) — ready for real implementation - When dep added: hooks (on_init, on_vault_open, on_node_create), sandbox (no io/os.execute), Plugin API GUI: - Template selector in create node modal - POST /api/nodes/from-template creates tree from template - Built-in "Клиент" template: Overview note + Документы/Переписка/Скриншоты CLI: - verstak plugin list/enable/disable/templates DokuWiki Importer: - Moved to contrib/plugins/importer-dokuwiki/ (optional plugin) - plugin.json + migration + README DokuWiki removed from MVP core — now an opt-in plugin. Acceptance: go build ./... pass, go test ./... pass (all packages).
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"verstak/internal/core/actions"
|
||||
"verstak/internal/core/plugins"
|
||||
"verstak/internal/core/search"
|
||||
"verstak/internal/core/storage"
|
||||
"verstak/internal/core/vault"
|
||||
@@ -37,6 +38,8 @@ func main() {
|
||||
runLog(os.Args[2:])
|
||||
case "index":
|
||||
runIndex(os.Args[2:])
|
||||
case "plugin":
|
||||
runPlugin(os.Args[2:])
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", os.Args[1])
|
||||
os.Exit(1)
|
||||
@@ -593,3 +596,71 @@ func runIndexRebuild(args []string) {
|
||||
|
||||
fmt.Printf("indexed %d nodes\n", count)
|
||||
}
|
||||
|
||||
// --- plugin ---
|
||||
|
||||
func runPlugin(args []string) {
|
||||
vaultPath, _ := stringFlag(args, "--vault")
|
||||
|
||||
if len(args) == 0 || args[0] == "--help" || args[0] == "-h" {
|
||||
fmt.Println("verstak plugin — manage plugins")
|
||||
fmt.Println()
|
||||
fmt.Println("Usage: verstak plugin <command> [options]")
|
||||
fmt.Println()
|
||||
fmt.Println("Commands:")
|
||||
fmt.Println(" list List discovered plugins")
|
||||
fmt.Println(" enable NAME Enable a plugin")
|
||||
fmt.Println(" disable NAME Disable a plugin")
|
||||
fmt.Println(" templates List available templates")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
abs, _ := filepath.Abs(vaultPath)
|
||||
|
||||
switch args[0] {
|
||||
case "list":
|
||||
mgr := plugins.NewManager(abs)
|
||||
mgr.Discover()
|
||||
pp := mgr.Plugins()
|
||||
if len(pp) == 0 {
|
||||
fmt.Println("No plugins found.")
|
||||
fmt.Println("Put plugins in .verstak/plugins/<name>/plugin.json")
|
||||
return
|
||||
}
|
||||
for _, p := range pp {
|
||||
status := "on"
|
||||
if !p.Active {
|
||||
status = "off"
|
||||
}
|
||||
fmt.Printf("%-20s %-6s %s\n", p.Meta.Name, status, p.Meta.Description)
|
||||
}
|
||||
case "enable", "disable":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "Error: plugin name required")
|
||||
os.Exit(1)
|
||||
}
|
||||
mgr := plugins.NewManager(abs)
|
||||
mgr.Discover()
|
||||
name := args[1]
|
||||
if args[0] == "enable" {
|
||||
mgr.Enable(name)
|
||||
} else {
|
||||
mgr.Disable(name)
|
||||
}
|
||||
fmt.Printf("%s %s\n", args[0]+"d", name)
|
||||
case "templates":
|
||||
mgr := plugins.NewManager(abs)
|
||||
mgr.Discover()
|
||||
tmpls := mgr.Templates()
|
||||
if len(tmpls) == 0 {
|
||||
fmt.Println("No templates found.")
|
||||
return
|
||||
}
|
||||
for _, t := range tmpls {
|
||||
fmt.Printf("%-20s %-10s %s\n", t.Name, fmt.Sprintf("[%s]", t.Plugin), t.Description)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown plugin command: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user