lampson 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +394 -382
- package/bin/lampson.js +8 -2
- package/chat.syn +1029 -799
- package/lampson.cmd +4 -4
- package/lampson.ps1 +88 -88
- package/lampson.sh +42 -42
- package/lib/agents.syn +471 -471
- package/lib/diff.syn +142 -0
- package/lib/lamps.syn +386 -386
- package/lib/line.syn +365 -0
- package/lib/lsp.syn +503 -503
- package/lib/mcp.syn +403 -403
- package/lib/md.syn +171 -0
- package/lib/permission.syn +189 -154
- package/lib/prompt.syn +75 -75
- package/lib/session.syn +111 -111
- package/lib/skills.syn +179 -179
- package/lib/tools/bash.syn +105 -105
- package/lib/tools/edit.syn +35 -32
- package/lib/tools/memo.syn +148 -148
- package/lib/tools/process.syn +46 -46
- package/lib/tools/write.syn +4 -0
- package/lib/tools.syn +198 -198
- package/lib/tree.syn +59 -59
- package/package.json +2 -2
- package/public/index.html +1268 -1268
- package/skills/lampson/SKILL.md +117 -117
- package/skills/synsema/SKILL.md +15 -2
- package/web.syn +468 -468
package/lib/line.syn
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
-- lib/line.syn — editor de línea para la terminal (Synsema v0.6.11+: term_open/term_recv)
|
|
2
|
+
-- read(prompt, color, ctx) → texto (puede tener "\n"), INTERRUPT si ctx["idle"]() pidió cortar,
|
|
3
|
+
-- nothing en EOF. Sin TTY (pipe/CI) delega en ctx["fallback"](prompt).
|
|
4
|
+
-- choose(question, options, color) → índice elegido, -1 sin TTY (usar approve), nothing en EOF/Esc.
|
|
5
|
+
-- Teclas: ←→ Home End · ↑↓ historial o menú · Tab completa · Alt+Enter salto de línea · Enter envía
|
|
6
|
+
-- Ctrl+A/E inicio/fin · Ctrl+U borra la línea · Ctrl+W borra palabra · Ctrl+O = /out · Esc cierra el menú
|
|
7
|
+
-- Menú: al escribir "/" aparecen los comandos (primero los recientes); con "/cmd " los argumentos que
|
|
8
|
+
-- devuelva ctx["complete"](cmd, prefijo) (archivos, servers, flags…). Máx MENU_ROWS filas.
|
|
9
|
+
-- MIGA: dibujar SIEMPRE con term_write (print queda buffereado). El runtime restaura la terminal al cerrar.
|
|
10
|
+
|
|
11
|
+
let ESC be decode(bytes("1b", "hex"))
|
|
12
|
+
let MENU_ROWS be 8
|
|
13
|
+
export let INTERRUPT be ESC + "interrupt"
|
|
14
|
+
|
|
15
|
+
task sgr(color, code, s)
|
|
16
|
+
when not color or s == ""
|
|
17
|
+
give s
|
|
18
|
+
give ESC + "[" + code + "m" + s + ESC + "[0m"
|
|
19
|
+
|
|
20
|
+
task vis(s)
|
|
21
|
+
give length(strip_ansi(s))
|
|
22
|
+
|
|
23
|
+
task rep(ch, n)
|
|
24
|
+
let out be ""
|
|
25
|
+
while length(out) < n
|
|
26
|
+
set out to out + ch
|
|
27
|
+
give out
|
|
28
|
+
|
|
29
|
+
task starts(s, p)
|
|
30
|
+
give starts_with(s, p)
|
|
31
|
+
|
|
32
|
+
-- ---------- candidatos ----------
|
|
33
|
+
|
|
34
|
+
-- buf = texto del editor. give {"kind": "cmd"|"arg"|"", "items": [{"label","fill","desc"}], "token": prefijo}
|
|
35
|
+
task candidates(buf, ctx)
|
|
36
|
+
when not starts(buf, "/") or contains(buf, "\n")
|
|
37
|
+
give {"kind": "", "items": [], "token": ""}
|
|
38
|
+
let sp be capture(buf, "^(/\S*)\s+(.*)$")
|
|
39
|
+
when sp == nothing
|
|
40
|
+
-- comando a medias: recientes primero cuando es "/" pelado
|
|
41
|
+
let items be []
|
|
42
|
+
let seen be {}
|
|
43
|
+
when buf == "/"
|
|
44
|
+
each r in ctx["recent"]
|
|
45
|
+
each c in ctx["commands"]
|
|
46
|
+
when c[0] == r and not contains(seen, r)
|
|
47
|
+
set seen[r] to true
|
|
48
|
+
set items to append(items, {"label": c[0], "fill": c[0] + (when c[1] != "" then " " otherwise ""), "args": c[1], "desc": c[2], "recent": true})
|
|
49
|
+
each c in ctx["commands"]
|
|
50
|
+
when starts(c[0], buf) and not contains(seen, c[0])
|
|
51
|
+
set seen[c[0]] to true
|
|
52
|
+
set items to append(items, {"label": c[0], "fill": c[0] + (when c[1] != "" then " " otherwise ""), "args": c[1], "desc": c[2], "recent": false})
|
|
53
|
+
give {"kind": "cmd", "items": items, "token": buf}
|
|
54
|
+
let cmd be sp[0]
|
|
55
|
+
let rest be sp[1]
|
|
56
|
+
-- último token (lo que se completa); lo anterior queda fijo
|
|
57
|
+
let toks be split(rest, " ")
|
|
58
|
+
let last be toks[length(toks) - 1]
|
|
59
|
+
let head be slice(rest, 0, length(rest) - length(last))
|
|
60
|
+
let items be []
|
|
61
|
+
let cands be ctx["complete"](cmd, head, last)
|
|
62
|
+
each c in cands
|
|
63
|
+
when starts(c, last)
|
|
64
|
+
set items to append(items, {"label": c, "fill": cmd + " " + head + c + (when ends_with(c, "/") then "" otherwise " "), "args": "", "desc": "", "recent": false})
|
|
65
|
+
give {"kind": "arg", "items": items, "token": last}
|
|
66
|
+
|
|
67
|
+
-- ---------- dibujo ----------
|
|
68
|
+
|
|
69
|
+
-- filas físicas que ocupa un texto de ancho w en una terminal de cols columnas
|
|
70
|
+
task rows_of(w, cols)
|
|
71
|
+
when cols <= 0
|
|
72
|
+
give 1
|
|
73
|
+
give floor(w / cols) + 1
|
|
74
|
+
|
|
75
|
+
-- dibuja prompt + buffer (multilínea) + menú; deja el cursor en su lugar. give filas totales dibujadas
|
|
76
|
+
-- (para poder volver al inicio en el próximo redraw)
|
|
77
|
+
task draw(h, st, color, ctx)
|
|
78
|
+
let size be term_size(h)
|
|
79
|
+
let cols be size["cols"]
|
|
80
|
+
let lines be split(st["buf"], "\n")
|
|
81
|
+
-- posición del cursor: línea y columna dentro del buffer
|
|
82
|
+
let cl be 0
|
|
83
|
+
let cc be st["cur"]
|
|
84
|
+
let i be 0
|
|
85
|
+
let found be false
|
|
86
|
+
while i < length(lines) and not found
|
|
87
|
+
when cc <= length(lines[i])
|
|
88
|
+
set cl to i
|
|
89
|
+
set found to true
|
|
90
|
+
otherwise
|
|
91
|
+
set cc to cc - length(lines[i]) - 1
|
|
92
|
+
set i to i + 1
|
|
93
|
+
let out be ""
|
|
94
|
+
-- volver al inicio de lo dibujado la vez anterior y limpiar hacia abajo
|
|
95
|
+
when st["cursor_row"] > 0
|
|
96
|
+
set out to out + ESC + "[" + text(st["cursor_row"]) + "A"
|
|
97
|
+
set out to out + "\r" + ESC + "[J"
|
|
98
|
+
let cont be sgr(color, "2", " … ")
|
|
99
|
+
let rows_before_cursor be 0
|
|
100
|
+
let total be 0
|
|
101
|
+
let cursor_col be 0
|
|
102
|
+
set i to 0
|
|
103
|
+
each l in lines
|
|
104
|
+
let pre be when i == 0 then st["prompt"] otherwise cont
|
|
105
|
+
set out to out + (when i > 0 then "\r\n" otherwise "") + pre + l
|
|
106
|
+
let r be rows_of(vis(pre) + length(l), cols)
|
|
107
|
+
when i < cl
|
|
108
|
+
set rows_before_cursor to rows_before_cursor + r
|
|
109
|
+
otherwise when i == cl
|
|
110
|
+
set rows_before_cursor to rows_before_cursor + floor((vis(pre) + cc) / cols)
|
|
111
|
+
set cursor_col to (vis(pre) + cc) % cols
|
|
112
|
+
set total to total + r
|
|
113
|
+
set i to i + 1
|
|
114
|
+
-- menú
|
|
115
|
+
let menu_rows be 0
|
|
116
|
+
when st["menu"] and length(st["items"]) > 0
|
|
117
|
+
let items be st["items"]
|
|
118
|
+
let from be 0
|
|
119
|
+
when st["sel"] >= MENU_ROWS
|
|
120
|
+
set from to st["sel"] - MENU_ROWS + 1
|
|
121
|
+
let k be from
|
|
122
|
+
while k < length(items) and k < from + MENU_ROWS
|
|
123
|
+
let it be items[k]
|
|
124
|
+
let row be ""
|
|
125
|
+
when st["kind"] == "cmd"
|
|
126
|
+
let full be it["label"] + (when it["args"] != "" then " " + it["args"] otherwise "")
|
|
127
|
+
let name be when length(full) > 34 then slice(full, 0, 33) + "…" otherwise full
|
|
128
|
+
let padn be name + rep(" ", 34 - length(name))
|
|
129
|
+
let desc be when length(it["desc"]) > cols - 40 then slice(it["desc"], 0, cols - 43) + "…" otherwise it["desc"]
|
|
130
|
+
set row to (when k == st["sel"] then sgr(color, "7", " " + padn + " ") otherwise " " + sgr(color, "1", it["label"]) + sgr(color, "2", slice(padn, length(it["label"]), length(padn)) + " ")) + (when it["recent"] then sgr(color, "36", "↺ ") otherwise " ") + sgr(color, "2", desc)
|
|
131
|
+
otherwise
|
|
132
|
+
set row to when k == st["sel"] then sgr(color, "7", " " + it["label"] + " ") otherwise " " + it["label"]
|
|
133
|
+
set out to out + "\r\n" + " " + row
|
|
134
|
+
set menu_rows to menu_rows + 1
|
|
135
|
+
set k to k + 1
|
|
136
|
+
when length(items) > MENU_ROWS
|
|
137
|
+
set out to out + "\r\n" + sgr(color, "2", " (" + text(length(items)) + " opciones · ↑↓ elegir · Tab completa · Esc cierra)")
|
|
138
|
+
set menu_rows to menu_rows + 1
|
|
139
|
+
-- recolocar el cursor: subir (filas debajo de la línea del cursor + menú), ir a la columna
|
|
140
|
+
let below be total - rows_before_cursor - 1 + menu_rows
|
|
141
|
+
when below > 0
|
|
142
|
+
set out to out + ESC + "[" + text(below) + "A"
|
|
143
|
+
set out to out + "\r"
|
|
144
|
+
when cursor_col > 0
|
|
145
|
+
set out to out + ESC + "[" + text(cursor_col) + "C"
|
|
146
|
+
term_write(h, out)
|
|
147
|
+
set st["cursor_row"] to rows_before_cursor
|
|
148
|
+
give st
|
|
149
|
+
|
|
150
|
+
-- ---------- edición ----------
|
|
151
|
+
|
|
152
|
+
task insert(st, s)
|
|
153
|
+
set st["buf"] to slice(st["buf"], 0, st["cur"]) + s + slice(st["buf"], st["cur"], length(st["buf"]))
|
|
154
|
+
set st["cur"] to st["cur"] + length(s)
|
|
155
|
+
give st
|
|
156
|
+
|
|
157
|
+
task refresh_menu(st, ctx)
|
|
158
|
+
let c be candidates(st["buf"], ctx)
|
|
159
|
+
set st["kind"] to c["kind"]
|
|
160
|
+
set st["items"] to c["items"]
|
|
161
|
+
set st["sel"] to 0
|
|
162
|
+
set st["nav"] to false
|
|
163
|
+
set st["menu"] to c["kind"] != "" and length(c["items"]) > 0 and not st["menu_off"]
|
|
164
|
+
give st
|
|
165
|
+
|
|
166
|
+
task word_start(buf, cur)
|
|
167
|
+
let i be cur
|
|
168
|
+
while i > 0 and slice(buf, i - 1, i) == " "
|
|
169
|
+
set i to i - 1
|
|
170
|
+
while i > 0 and slice(buf, i - 1, i) != " " and slice(buf, i - 1, i) != "\n"
|
|
171
|
+
set i to i - 1
|
|
172
|
+
give i
|
|
173
|
+
|
|
174
|
+
task accept(st)
|
|
175
|
+
let it be st["items"][st["sel"]]
|
|
176
|
+
set st["buf"] to it["fill"]
|
|
177
|
+
set st["cur"] to length(it["fill"])
|
|
178
|
+
set st["menu"] to false
|
|
179
|
+
give st
|
|
180
|
+
|
|
181
|
+
export task read(prompt, color, ctx)
|
|
182
|
+
let h be term_open({"ctrl_c": "exit"})
|
|
183
|
+
when h == nothing
|
|
184
|
+
give ctx["fallback"](prompt)
|
|
185
|
+
let st be {"buf": "", "cur": 0, "prompt": prompt, "cursor_row": 0, "menu": false, "menu_off": false, "kind": "", "items": [], "sel": 0, "nav": false}
|
|
186
|
+
let hist be ctx["history"]
|
|
187
|
+
let hi be length(hist)
|
|
188
|
+
let draft be ""
|
|
189
|
+
let result be nothing
|
|
190
|
+
let done be false
|
|
191
|
+
term_write(h, "\r\n")
|
|
192
|
+
set st to draw(h, st, color, ctx)
|
|
193
|
+
while not done
|
|
194
|
+
let ev be term_recv(h, 1)
|
|
195
|
+
let dirty be true
|
|
196
|
+
when ev == nothing
|
|
197
|
+
set dirty to false
|
|
198
|
+
when ctx["idle"]()
|
|
199
|
+
set result to INTERRUPT
|
|
200
|
+
set done to true
|
|
201
|
+
otherwise when ev["type"] == "focus"
|
|
202
|
+
set dirty to false
|
|
203
|
+
otherwise when ev["type"] == "eof"
|
|
204
|
+
set result to nothing
|
|
205
|
+
set done to true
|
|
206
|
+
otherwise when ev["type"] == "paste"
|
|
207
|
+
set st to insert(st, replace_text(ev["text"], "\r", ""))
|
|
208
|
+
set st to refresh_menu(st, ctx)
|
|
209
|
+
otherwise when ev["type"] == "resize"
|
|
210
|
+
set st to st
|
|
211
|
+
otherwise when ev["type"] == "key"
|
|
212
|
+
let k be ev["key"]
|
|
213
|
+
let changed be true
|
|
214
|
+
when k == "enter" and ev["alt"]
|
|
215
|
+
set st to insert(st, "\n")
|
|
216
|
+
otherwise when k == "enter"
|
|
217
|
+
when st["menu"] and st["kind"] == "cmd" and st["buf"] != st["items"][st["sel"]]["label"] and st["buf"] != st["items"][st["sel"]]["fill"]
|
|
218
|
+
set st to accept(st)
|
|
219
|
+
when st["items"][st["sel"]]["args"] == ""
|
|
220
|
+
set result to trim(st["buf"])
|
|
221
|
+
set done to true
|
|
222
|
+
otherwise when st["menu"] and st["kind"] == "arg" and st["nav"]
|
|
223
|
+
-- Enter solo completa el argumento si el humano navegó el menú; si no, envía tal cual
|
|
224
|
+
set st to accept(st)
|
|
225
|
+
otherwise
|
|
226
|
+
set result to st["buf"]
|
|
227
|
+
set done to true
|
|
228
|
+
otherwise when k == "tab" and st["menu"]
|
|
229
|
+
set st to accept(st)
|
|
230
|
+
otherwise when k == "escape"
|
|
231
|
+
when st["menu"]
|
|
232
|
+
set st["menu"] to false
|
|
233
|
+
set st["menu_off"] to true
|
|
234
|
+
set changed to false
|
|
235
|
+
otherwise when k == "up" and st["menu"]
|
|
236
|
+
set st["sel"] to (when st["sel"] > 0 then st["sel"] - 1 otherwise length(st["items"]) - 1)
|
|
237
|
+
set st["nav"] to true
|
|
238
|
+
set changed to false
|
|
239
|
+
otherwise when k == "down" and st["menu"]
|
|
240
|
+
set st["sel"] to (when st["sel"] < length(st["items"]) - 1 then st["sel"] + 1 otherwise 0)
|
|
241
|
+
set st["nav"] to true
|
|
242
|
+
set changed to false
|
|
243
|
+
otherwise when k == "up"
|
|
244
|
+
when hi > 0
|
|
245
|
+
when hi == length(hist)
|
|
246
|
+
set draft to st["buf"]
|
|
247
|
+
set hi to hi - 1
|
|
248
|
+
set st["buf"] to hist[hi]
|
|
249
|
+
set st["cur"] to length(st["buf"])
|
|
250
|
+
set changed to false
|
|
251
|
+
otherwise when k == "down"
|
|
252
|
+
when hi < length(hist)
|
|
253
|
+
set hi to hi + 1
|
|
254
|
+
set st["buf"] to (when hi == length(hist) then draft otherwise hist[hi])
|
|
255
|
+
set st["cur"] to length(st["buf"])
|
|
256
|
+
set changed to false
|
|
257
|
+
otherwise when k == "left"
|
|
258
|
+
when st["cur"] > 0
|
|
259
|
+
set st["cur"] to st["cur"] - 1
|
|
260
|
+
set changed to false
|
|
261
|
+
otherwise when k == "right"
|
|
262
|
+
when st["cur"] < length(st["buf"])
|
|
263
|
+
set st["cur"] to st["cur"] + 1
|
|
264
|
+
set changed to false
|
|
265
|
+
otherwise when k == "home" or (k == "char" and ev["ctrl"] and ev["text"] == "a")
|
|
266
|
+
set st["cur"] to 0
|
|
267
|
+
set changed to false
|
|
268
|
+
otherwise when k == "end" or (k == "char" and ev["ctrl"] and ev["text"] == "e")
|
|
269
|
+
set st["cur"] to length(st["buf"])
|
|
270
|
+
set changed to false
|
|
271
|
+
otherwise when k == "backspace"
|
|
272
|
+
when st["cur"] > 0
|
|
273
|
+
set st["buf"] to slice(st["buf"], 0, st["cur"] - 1) + slice(st["buf"], st["cur"], length(st["buf"]))
|
|
274
|
+
set st["cur"] to st["cur"] - 1
|
|
275
|
+
otherwise when k == "delete"
|
|
276
|
+
when st["cur"] < length(st["buf"])
|
|
277
|
+
set st["buf"] to slice(st["buf"], 0, st["cur"]) + slice(st["buf"], st["cur"] + 1, length(st["buf"]))
|
|
278
|
+
otherwise when k == "char" and ev["ctrl"] and ev["text"] == "u"
|
|
279
|
+
set st["buf"] to ""
|
|
280
|
+
set st["cur"] to 0
|
|
281
|
+
otherwise when k == "char" and ev["ctrl"] and ev["text"] == "w"
|
|
282
|
+
let ws be word_start(st["buf"], st["cur"])
|
|
283
|
+
set st["buf"] to slice(st["buf"], 0, ws) + slice(st["buf"], st["cur"], length(st["buf"]))
|
|
284
|
+
set st["cur"] to ws
|
|
285
|
+
otherwise when k == "char" and ev["ctrl"] and ev["text"] == "o"
|
|
286
|
+
set result to "/out"
|
|
287
|
+
set done to true
|
|
288
|
+
otherwise when k == "char" and ev["ctrl"] and ev["text"] == "l"
|
|
289
|
+
term_write(h, ESC + "[2J" + ESC + "[H")
|
|
290
|
+
set st["cursor_row"] to 0
|
|
291
|
+
set changed to false
|
|
292
|
+
otherwise when k == "char" and not ev["ctrl"]
|
|
293
|
+
set st to insert(st, ev["text"])
|
|
294
|
+
otherwise
|
|
295
|
+
set changed to false
|
|
296
|
+
when changed
|
|
297
|
+
set st["menu_off"] to false
|
|
298
|
+
set st to refresh_menu(st, ctx)
|
|
299
|
+
when not done and dirty
|
|
300
|
+
set st to draw(h, st, color, ctx)
|
|
301
|
+
-- dejar el prompt limpio (sin menú) en el scrollback y bajar a una línea nueva
|
|
302
|
+
set st["menu"] to false
|
|
303
|
+
set st to draw(h, st, color, ctx)
|
|
304
|
+
let lines be split(st["buf"], "\n")
|
|
305
|
+
let size be term_size(h)
|
|
306
|
+
let tail be 0
|
|
307
|
+
let i be 0
|
|
308
|
+
each l in lines
|
|
309
|
+
let pre be when i == 0 then vis(st["prompt"]) otherwise 4
|
|
310
|
+
when i > 0 or true
|
|
311
|
+
set tail to tail + rows_of(pre + length(l), size["cols"])
|
|
312
|
+
set i to i + 1
|
|
313
|
+
let down be tail - st["cursor_row"] - 1
|
|
314
|
+
term_write(h, (when down > 0 then ESC + "[" + text(down) + "B" otherwise "") + "\r\n")
|
|
315
|
+
term_close(h)
|
|
316
|
+
give result
|
|
317
|
+
|
|
318
|
+
-- menú vertical de opciones (aprobaciones): ↑↓ + Enter, o la letra inicial / número; Esc = nothing
|
|
319
|
+
export task choose(question, options, color)
|
|
320
|
+
let h be term_open({"ctrl_c": "exit"})
|
|
321
|
+
when h == nothing
|
|
322
|
+
give -1
|
|
323
|
+
let sel be 0
|
|
324
|
+
let done be false
|
|
325
|
+
let result be nothing
|
|
326
|
+
let drawn be 0
|
|
327
|
+
while not done
|
|
328
|
+
let out be ""
|
|
329
|
+
when drawn > 0
|
|
330
|
+
set out to out + ESC + "[" + text(drawn) + "A"
|
|
331
|
+
set out to out + "\r" + ESC + "[J" + question
|
|
332
|
+
let i be 0
|
|
333
|
+
each o in options
|
|
334
|
+
set out to out + "\r\n" + " " + (when i == sel then sgr(color, "7", " " + o + " ") otherwise " " + sgr(color, "2", o))
|
|
335
|
+
set i to i + 1
|
|
336
|
+
term_write(h, out)
|
|
337
|
+
set drawn to length(options)
|
|
338
|
+
let ev be term_recv(h, 600)
|
|
339
|
+
when ev == nothing
|
|
340
|
+
set result to nothing
|
|
341
|
+
set done to true
|
|
342
|
+
otherwise when ev["type"] == "eof"
|
|
343
|
+
set done to true
|
|
344
|
+
otherwise when ev["type"] == "key"
|
|
345
|
+
let k be ev["key"]
|
|
346
|
+
when k == "up"
|
|
347
|
+
set sel to (when sel > 0 then sel - 1 otherwise length(options) - 1)
|
|
348
|
+
otherwise when k == "down" or k == "tab"
|
|
349
|
+
set sel to (when sel < length(options) - 1 then sel + 1 otherwise 0)
|
|
350
|
+
otherwise when k == "enter"
|
|
351
|
+
set result to sel
|
|
352
|
+
set done to true
|
|
353
|
+
otherwise when k == "escape"
|
|
354
|
+
set done to true
|
|
355
|
+
otherwise when k == "char" and not ev["ctrl"]
|
|
356
|
+
let t be lower(ev["text"])
|
|
357
|
+
let j be 0
|
|
358
|
+
each o in options
|
|
359
|
+
when t == text(j + 1) or t == lower(slice(o, 0, 1))
|
|
360
|
+
set result to j
|
|
361
|
+
set done to true
|
|
362
|
+
set j to j + 1
|
|
363
|
+
term_write(h, "\r\n")
|
|
364
|
+
term_close(h)
|
|
365
|
+
give result
|