domain/architecture/system/release/plugin/claude/ops.bend

near-v-framework@0.7.0.0 · 191 lines · 0 definitions

1import Base
2import bend-net-json@0.3.0.0/json.bend as Json
3import near-v-framework@0.7.0.0/domain/architecture/system/release/plugin/json.bend as J
4import near-v-framework@0.7.0.0/domain/architecture/system/release/plugin/claude/type.bend as Pl
5
6# Claude Code's part of a plugin, as the keys it adds to .claude-plugin/plugin.json.
7
8# ---- userConfig ----
9def kind_keys(k: Pl.Input) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
10  match k:
11    case Pl.Text{sensitive, d}:
12      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("type", Json.Str{"string"})],
13        List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, J.when_true("sensitive", sensitive), J.opt("default", J.text(d))))
14    case Pl.Choice{os, d}:
15      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("type", Json.Str{"string"}), ("options", J.texts(os))], J.opt("default", J.text(d)))
16    case Pl.Texts{d}:
17      [("type", Json.Str{"string"}), ("multiple", Json.Flag{True{}}), ("default", J.texts(d))]
18    case Pl.Number{lo, hi, d}:
19      List.concat(&2, Sigma<&2, &2, String, _ => Json.Val>, [[("type", Json.Str{"number"})], J.opt("min", J.number(lo)), J.opt("max", J.number(hi)), J.opt("default", J.number(d))])
20    case Pl.Boolean{d}:
21      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("type", Json.Str{"boolean"})], J.opt("default", J.flag(d)))
22    case Pl.Folder{d}:
23      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("type", Json.Str{"directory"})], J.opt("default", J.text(d)))
24    case Pl.Path{d}:
25      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("type", Json.Str{"file"})], J.opt("default", J.text(d)))
26
27def field(f: Pl.Field) -> Sigma<&2, &2, String, _ => Json.Val>:
28  match f:
29    case Pl.Field{key, title, description, required, k}:
30      (key, J.cat([[("title", Json.Str{title}), ("description", Json.Str{description})], J.when_true("required", required), kind_keys(k)]))
31
32def fields(fs: List<&2, Pl.Field>) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
33  match fs:
34    case []:
35      []
36    case f <> rest:
37      field(f) <> fields(rest)
38
39# ---- hooks ----
40def event_name(e: Pl.HookEvent) -> String:
41  match e:
42    case Pl.SessionStart{}:
43      "SessionStart"
44    case Pl.Setup{}:
45      "Setup"
46    case Pl.UserPromptSubmit{}:
47      "UserPromptSubmit"
48    case Pl.UserPromptExpansion{}:
49      "UserPromptExpansion"
50    case Pl.PreToolUse{}:
51      "PreToolUse"
52    case Pl.PermissionRequest{}:
53      "PermissionRequest"
54    case Pl.PermissionDenied{}:
55      "PermissionDenied"
56    case Pl.PostToolUse{}:
57      "PostToolUse"
58    case Pl.PostToolUseFailure{}:
59      "PostToolUseFailure"
60    case Pl.PostToolBatch{}:
61      "PostToolBatch"
62    case Pl.Notification{}:
63      "Notification"
64    case Pl.MessageDisplay{}:
65      "MessageDisplay"
66    case Pl.SubagentStart{}:
67      "SubagentStart"
68    case Pl.SubagentStop{}:
69      "SubagentStop"
70    case Pl.TaskCreated{}:
71      "TaskCreated"
72    case Pl.TaskCompleted{}:
73      "TaskCompleted"
74    case Pl.Stop{}:
75      "Stop"
76    case Pl.StopFailure{}:
77      "StopFailure"
78    case Pl.TeammateIdle{}:
79      "TeammateIdle"
80    case Pl.InstructionsLoaded{}:
81      "InstructionsLoaded"
82    case Pl.ConfigChange{}:
83      "ConfigChange"
84    case Pl.CwdChanged{}:
85      "CwdChanged"
86    case Pl.DirectoryAdded{}:
87      "DirectoryAdded"
88    case Pl.FileChanged{}:
89      "FileChanged"
90    case Pl.WorktreeCreate{}:
91      "WorktreeCreate"
92    case Pl.WorktreeRemove{}:
93      "WorktreeRemove"
94    case Pl.PreCompact{}:
95      "PreCompact"
96    case Pl.PostCompact{}:
97      "PostCompact"
98    case Pl.PreModelSwitch{}:
99      "PreModelSwitch"
100    case Pl.PostModelSwitch{}:
101      "PostModelSwitch"
102    case Pl.Elicitation{}:
103      "Elicitation"
104    case Pl.ElicitationResult{}:
105      "ElicitationResult"
106    case Pl.SessionEnd{}:
107      "SessionEnd"
108
109def shell_name(m: Maybe<&2, Pl.Shell>) -> Maybe<&2, Json.Val>:
110  match m:
111    case Some{Pl.Bash{}}:
112      Some{Json.Str{"bash"}}
113    case Some{Pl.PowerShell{}}:
114      Some{Json.Str{"powershell"}}
115    case None{}:
116      None{}
117
118def form_keys(f: Pl.Form) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
119  match f:
120    case Pl.Exec{command, args}:
121      [("command", Json.Str{command}), ("args", J.texts(args))]
122    case Pl.ShellForm{command, sh}:
123      List.append(&2, Sigma<&2, &2, String, _ => Json.Val>, [("command", Json.Str{command})], J.opt("shell", shell_name(sh)))
124
125def run_keys(r: Pl.Run) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
126  match r:
127    case Pl.Blocking{}:
128      []
129    case Pl.Async{}:
130      [("async", Json.Flag{True{}})]
131    case Pl.AsyncRewake{}:
132      [("asyncRewake", Json.Flag{True{}})]
133
134def handler(h: Pl.Handler) -> Json.Val:
135  match h:
136    case Pl.Command{f, r, w, t, s}:
137      J.cat([[("type", Json.Str{"command"})], form_keys(f), run_keys(r), J.opt("if", J.text(w)), J.opt("timeout", J.number(t)), J.opt("statusMessage", J.text(s))])
138
139def handlers(hs: List<&2, Pl.Handler>) -> List<&2, Json.Val>:
140  match hs:
141    case []:
142      []
143    case h <> rest:
144      handler(h) <> handlers(rest)
145
146def group(g: Pl.Group) -> Json.Val:
147  match g:
148    case Pl.Group{m, hs}:
149      J.cat([J.opt("matcher", J.text(m)), [("hooks", Json.Arr{handlers(hs)})]])
150
151def groups(gs: List<&2, Pl.Group>) -> List<&2, Json.Val>:
152  match gs:
153    case []:
154      []
155    case g <> rest:
156      group(g) <> groups(rest)
157
158def hooks(hs: List<&2, Pl.Hook>) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
159  match hs:
160    case []:
161      []
162    case Pl.Hook{e, gs} <> rest:
163      (event_name(e), Json.Arr{groups(gs)}) <> hooks(rest)
164
165# ---- Claude Code's part of .claude-plugin/plugin.json ----
166def config(fs: List<&2, Pl.Field>) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
167  match fs:
168    case []:
169      []
170    case f <> rest:
171      [("userConfig", J.obj(fields(f <> rest)))]
172
173def hooks_key(hs: List<&2, Pl.Hook>) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
174  match hs:
175    case []:
176      []
177    case h <> rest:
178      [("hooks", J.obj(hooks(h <> rest)))]
179
180# Enabled is Claude Code's default, so only a plugin that starts disabled says so.
181def enabled_key(b: Bool) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
182  match b:
183    case True{}:
184      []
185    case False{}:
186      [("defaultEnabled", Json.Flag{False{}})]
187
188def keys(c: Pl.Claude) -> List<&2, Sigma<&2, &2, String, _ => Json.Val>>:
189  match c:
190    case Pl.Claude{display, enabled, fs, hs}:
191      List.concat(&2, Sigma<&2, &2, String, _ => Json.Val>, [J.opt("displayName", J.text(display)), enabled_key(enabled), config(fs), hooks_key(hs)])