199 lines
4.8 KiB
Go
199 lines
4.8 KiB
Go
package plugins
|
|
|
|
import "fmt"
|
|
|
|
// KiroCliPlugin manages the stable v2 custom-agent and early-access v3 hook contracts.
|
|
type KiroCliPlugin struct{}
|
|
|
|
func (p *KiroCliPlugin) Install(config InstallConfig) error {
|
|
if config.AgentID == "" {
|
|
return fmt.Errorf("agent ID is required")
|
|
}
|
|
if config.GuardScriptPath == "" {
|
|
return fmt.Errorf("guard script path is required")
|
|
}
|
|
guardExists, err := regularFileExists(config.GuardScriptPath, "Elydora guard runtime")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !guardExists {
|
|
return fmt.Errorf("Elydora guard runtime is missing: %s", config.GuardScriptPath)
|
|
}
|
|
|
|
v2Path, v3Path, err := kiroConfigPaths()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v2Settings, _, err := readHookJSONObject(v2Path, "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v3Settings, _, err := readHookJSONObject(v3Path, "Kiro CLI v3 hooks config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v2Hooks, err := kiroHooksObject(v2Settings, "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currentV3Hooks, err := kiroV3Hooks(v3Settings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
scriptPath, err := hookScriptPath(config.AgentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if config.HookScript != "" {
|
|
scriptPath = config.HookScript
|
|
}
|
|
nodePath, err := resolveNodeRuntime()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
preToolUse, err := kiroHookEntries(v2Hooks, "preToolUse", "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
postToolUse, err := kiroHookEntries(v2Hooks, "postToolUse", "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v2Hooks["preToolUse"] = append(
|
|
withoutKiroV2Hooks(preToolUse, ""),
|
|
buildKiroV2Hook(nodePath, config.GuardScriptPath),
|
|
)
|
|
v2Hooks["postToolUse"] = append(
|
|
withoutKiroV2Hooks(postToolUse, ""),
|
|
buildKiroV2Hook(nodePath, scriptPath),
|
|
)
|
|
|
|
nextV2 := map[string]any{
|
|
"name": kiroV2AgentName,
|
|
"description": kiroV2Description,
|
|
"tools": []any{"*"},
|
|
"includeMcpJson": true,
|
|
}
|
|
copyKiroObject(nextV2, v2Settings)
|
|
nextV2["hooks"] = v2Hooks
|
|
|
|
nextV3 := cloneKiroObject(v3Settings)
|
|
nextV3["version"] = "v1"
|
|
v3Hooks := make([]any, 0, len(currentV3Hooks)+2)
|
|
for _, hook := range currentV3Hooks {
|
|
if !isManagedKiroV3Hook(hook, "") {
|
|
v3Hooks = append(v3Hooks, hook)
|
|
}
|
|
}
|
|
v3Hooks = append(
|
|
v3Hooks,
|
|
buildKiroV3Hook(
|
|
kiroV3GuardName,
|
|
"Block tool use when the Elydora agent is frozen",
|
|
"PreToolUse",
|
|
nodePath,
|
|
config.GuardScriptPath,
|
|
),
|
|
buildKiroV3Hook(
|
|
kiroV3AuditName,
|
|
"Record tool use in the Elydora audit trail",
|
|
"PostToolUse",
|
|
nodePath,
|
|
scriptPath,
|
|
),
|
|
)
|
|
nextV3["hooks"] = v3Hooks
|
|
|
|
if err := GenerateHookScript(scriptPath, config); err != nil {
|
|
return fmt.Errorf("generate hook script: %w", err)
|
|
}
|
|
if err := writeHookJSONObjectAtomic(v2Path, nextV2); err != nil {
|
|
return err
|
|
}
|
|
if err := writeHookJSONObjectAtomic(v3Path, nextV3); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println(`Kiro CLI v2: start with "kiro-cli --agent elydora-audit".`)
|
|
fmt.Println(`Kiro CLI v3: start with "kiro-cli --v3"; global hooks load automatically.`)
|
|
return nil
|
|
}
|
|
|
|
func (p *KiroCliPlugin) Uninstall(agentID string) error {
|
|
v2Path, v3Path, err := kiroConfigPaths()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v2Settings, v2Exists, err := readHookJSONObject(v2Path, "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v3Settings, v3Exists, err := readHookJSONObject(v3Path, "Kiro CLI v3 hooks config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v2Mutation, err := prepareKiroV2Uninstall(v2Path, v2Settings, v2Exists, agentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v3Mutation, err := prepareKiroV3Uninstall(v3Path, v3Settings, v3Exists, agentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := applyKiroMutation(v2Mutation); err != nil {
|
|
return err
|
|
}
|
|
if err := applyKiroMutation(v3Mutation); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println("Uninstalled Elydora hooks from Kiro CLI.")
|
|
return nil
|
|
}
|
|
|
|
func (p *KiroCliPlugin) Status() (PluginStatus, error) {
|
|
v2Path, v3Path, err := kiroConfigPaths()
|
|
if err != nil {
|
|
return PluginStatus{}, err
|
|
}
|
|
status := PluginStatus{
|
|
AgentName: kiroAgentKey,
|
|
DisplayName: "Kiro CLI",
|
|
ConfigPath: v3Path,
|
|
}
|
|
v2Settings, v2Exists, err := readHookJSONObject(v2Path, "Kiro CLI v2 agent config")
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
v3Settings, v3Exists, err := readHookJSONObject(v3Path, "Kiro CLI v3 hooks config")
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
|
|
contracts, err := configuredKiroContracts(
|
|
v2Path,
|
|
v2Settings,
|
|
v2Exists,
|
|
v3Path,
|
|
v3Settings,
|
|
v3Exists,
|
|
)
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
if len(contracts) == 0 {
|
|
return status, nil
|
|
}
|
|
status.ConfigPath = contracts[len(contracts)-1].configPath
|
|
status.HookConfigured = true
|
|
status.HookScriptExists, err = kiroRuntimeScriptsExist(contracts)
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
status.Installed = status.HookScriptExists
|
|
return status, nil
|
|
}
|