@lamemind/loom-deck 0.44.0 → 0.46.0
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 +20 -10
- package/dist/cli.js +44 -88
- package/dist/fields.js +108 -0
- package/dist/layout.js +0 -9
- package/dist/model.js +14 -0
- package/dist/overlays/sheet.js +84 -72
- package/dist/prompt-catalog.js +55 -0
- package/dist/spawn.js +26 -4
- package/dist/ui/detail-screen.js +63 -78
- package/dist/ui/fields.js +23 -0
- package/dist/ui/modals.js +9 -16
- package/dist/viewport.js +14 -15
- package/package.json +1 -1
- package/scripts/deck-run +103 -26
- package/scripts/prompt-catalog +21 -0
package/dist/viewport.js
CHANGED
|
@@ -223,23 +223,22 @@ export function readerCapacity(rows) {
|
|
|
223
223
|
// 1 riga hint
|
|
224
224
|
// 1 marginTop del box testo
|
|
225
225
|
// 2 bordi del box testo
|
|
226
|
-
// 1 marginTop
|
|
227
|
-
// 1 riga
|
|
228
|
-
// 1 riga
|
|
229
|
-
// 1 riga
|
|
226
|
+
// 1 marginTop dell'area di compilazione
|
|
227
|
+
// 1 riga azione (T66)
|
|
228
|
+
// 1 riga prompt (T117)
|
|
229
|
+
// 1 riga modello (T108)
|
|
230
|
+
// 1 riga titolo (T111)
|
|
230
231
|
//
|
|
231
|
-
// Le ultime
|
|
232
|
-
// READER_CHROME:
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
// TASKS_PANE_CHROME).
|
|
232
|
+
// Le ultime cinque sono il motivo per cui il detail non può riusare
|
|
233
|
+
// READER_CHROME: le quattro righe dell'area di compilazione sono righe FISSE in
|
|
234
|
+
// più dentro l'overlay, e ogni riga fissa aggiunta va scalata dalla capienza del
|
|
235
|
+
// contenuto o il frame sfonda `rows` (stessa invariante di TASKS_PANE_CHROME).
|
|
236
236
|
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
const DETAIL_CHROME = 12;
|
|
237
|
+
// Sono fisse e non `extra` condizionali di `detailCapacity` (T111 · D1): due
|
|
238
|
+
// condizionali mutuamente esclusivi obbligherebbero il parametro a dire QUALE è
|
|
239
|
+
// a schermo — un solo booleano per entrambi sottostima quando quello aperto è il
|
|
240
|
+
// più alto, e sommarli toglie righe al testo per un campo che non c'è.
|
|
241
|
+
const DETAIL_CHROME = 13;
|
|
243
242
|
// T91 — la ricerca dentro il detail: marginTop + riga del campo.
|
|
244
243
|
//
|
|
245
244
|
// `MODAL_HEIGHT` dice che il detail costa 0 ai due pane (li sostituisce), e da
|
package/package.json
CHANGED
package/scripts/deck-run
CHANGED
|
@@ -4,21 +4,25 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Apre una tab Ptyxis nella window ATTIVA (quella col focus = il deck) e vi
|
|
6
6
|
# avvia una sessione Claude Code già bound alla task via LOOM_TASK, dritta sul
|
|
7
|
-
# prompt iniziale scelto con --prompt-kind (default: `recap`,
|
|
8
|
-
#
|
|
7
|
+
# prompt iniziale scelto con --prompt-kind (default: `recap`, cioè la skill
|
|
8
|
+
# `/loom-works:recap-status` sulla task).
|
|
9
9
|
# Riusabile identico da TUI (Ink) e da web.
|
|
10
10
|
#
|
|
11
11
|
# Quattro assi ORTOGONALI, da non confondere fra loro:
|
|
12
12
|
# binding task <TaskID> ..... --no-task
|
|
13
13
|
# continuità nuova ........ --resume [--fork]
|
|
14
|
-
# prompt --prompt-kind none|recap|preflight|run|checkpoint
|
|
14
|
+
# prompt --prompt-kind none|recap|preflight|run|checkpoint | --prompt <testo>
|
|
15
15
|
# modello --model fable|opus|sonnet|haiku
|
|
16
16
|
# Il terzo asse (T56) prima non esisteva: il prompt era una CONSEGUENZA degli
|
|
17
17
|
# altri due (bound ⇒ recap, --no-task ⇒ niente, --resume ⇒ niente), quindi
|
|
18
18
|
# "task-bound SENZA prompt" era inesprimibile e l'unico modo di non avere un
|
|
19
|
-
# prompt era perdere la task. Il
|
|
20
|
-
#
|
|
21
|
-
#
|
|
19
|
+
# prompt era perdere la task. Il kind è un SIMBOLO e il suo testo sta nel
|
|
20
|
+
# catalogo condiviso `prompt-catalog` (sibling di questo script).
|
|
21
|
+
# T117 — accanto al kind c'è ora `--prompt <testo>`, che passa il prompt
|
|
22
|
+
# LETTERALE. I due sono mutuamente esclusivi: sono due modi di dire la stessa
|
|
23
|
+
# cosa, e accettarli insieme costringerebbe a stabilire quale vince. Serve a chi
|
|
24
|
+
# mostra il prompt all'utente prima dello spawn e glielo lascia modificare —
|
|
25
|
+
# dopo una modifica nessun kind descrive più quel testo.
|
|
22
26
|
# Il quarto asse (T108) è indipendente dagli altri tre: vale sul ramo bound come
|
|
23
27
|
# su --no-task, su una sessione nuova come su una ripresa.
|
|
24
28
|
#
|
|
@@ -56,6 +60,12 @@
|
|
|
56
60
|
#
|
|
57
61
|
set -euo pipefail
|
|
58
62
|
|
|
63
|
+
# Il catalogo dei prompt è un file DATI sibling di questo script, non un `case`
|
|
64
|
+
# qui dentro: lo legge anche il deck TUI, che con la stessa risoluzione sibling
|
|
65
|
+
# arriva allo stesso file della stessa installazione.
|
|
66
|
+
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
67
|
+
PROMPT_CATALOG="${LOOM_DECK_PROMPT_CATALOG:-${_SCRIPT_DIR}/prompt-catalog}"
|
|
68
|
+
|
|
59
69
|
TASK=""
|
|
60
70
|
SESSION_ID=""
|
|
61
71
|
RESUME_ID=""
|
|
@@ -66,6 +76,11 @@ TITLE_NOTE=""
|
|
|
66
76
|
# "kind implicito" da "kind chiesto", perché con --no-task il primo è legittimo
|
|
67
77
|
# (nessun prompt, come sempre) e il secondo è un errore d'uso.
|
|
68
78
|
PROMPT_KIND=""
|
|
79
|
+
# T117 — prompt letterale. Vuoto NON basta a dire "non passato": `--prompt ''`
|
|
80
|
+
# è una richiesta legittima (nessun prompt, deciso da chi ha svuotato il campo),
|
|
81
|
+
# quindi la presenza del flag si tiene in un booleano a parte.
|
|
82
|
+
PROMPT_TEXT=""
|
|
83
|
+
PROMPT_GIVEN=0
|
|
69
84
|
# Vuoto = flag non passato: la cascata (env, poi default) si risolve più sotto,
|
|
70
85
|
# insieme alla validazione, così l'ingresso da argomento e quello da env passano
|
|
71
86
|
# per lo stesso enum.
|
|
@@ -117,6 +132,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
117
132
|
PROMPT_KIND="${2:-}"; shift 2 ;;
|
|
118
133
|
--prompt-kind=*)
|
|
119
134
|
PROMPT_KIND="${1#*=}"; shift ;;
|
|
135
|
+
--prompt)
|
|
136
|
+
PROMPT_TEXT="${2:-}"; PROMPT_GIVEN=1; shift 2 ;;
|
|
137
|
+
--prompt=*)
|
|
138
|
+
PROMPT_TEXT="${1#*=}"; PROMPT_GIVEN=1; shift ;;
|
|
120
139
|
--session-id)
|
|
121
140
|
SESSION_ID="${2:-}"; shift 2 ;;
|
|
122
141
|
--session-id=*)
|
|
@@ -145,11 +164,14 @@ USAGE="uso: deck-run <TaskID> [--model <alias>] [--prompt-kind <kind>] [--sessio
|
|
|
145
164
|
|
|
146
165
|
--prompt-kind prompt iniziale della sessione bound (default: recap)
|
|
147
166
|
none nessun prompt — sessione aperta sulla task, a mani nude
|
|
148
|
-
recap recap
|
|
167
|
+
recap /loom-works:recap-status <TaskID>
|
|
149
168
|
preflight /loom-works:preflight-task <TaskID>
|
|
150
169
|
run /loom-works:run-task <TaskID>
|
|
151
170
|
checkpoint /loom-works:checkpoint-task <TaskID>
|
|
152
171
|
|
|
172
|
+
--prompt prompt LETTERALE, per chi lo ha già composto (o fatto
|
|
173
|
+
modificare). Esclusivo con --prompt-kind; vuoto = nessun prompt
|
|
174
|
+
|
|
153
175
|
--model modello della sessione: fable|opus|sonnet|haiku (default: opus)
|
|
154
176
|
valore ignoto → fallback sul default, con avviso su stderr
|
|
155
177
|
|
|
@@ -195,6 +217,18 @@ if [[ $NO_TASK -eq 1 && -n "$PROMPT_KIND" ]]; then
|
|
|
195
217
|
echo "$USAGE" >&2
|
|
196
218
|
exit 2
|
|
197
219
|
fi
|
|
220
|
+
if [[ $NO_TASK -eq 1 && $PROMPT_GIVEN -eq 1 ]]; then
|
|
221
|
+
echo "--prompt richiede una task: una sessione --no-task non ha prompt iniziale" >&2
|
|
222
|
+
echo "$USAGE" >&2
|
|
223
|
+
exit 2
|
|
224
|
+
fi
|
|
225
|
+
# Due modi di dire la stessa cosa: accettarli insieme obbligherebbe a stabilire
|
|
226
|
+
# quale vince, cioè a scrivere una precedenza che nessun chiamante ha chiesto.
|
|
227
|
+
if [[ $PROMPT_GIVEN -eq 1 && -n "$PROMPT_KIND" ]]; then
|
|
228
|
+
echo "--prompt e --prompt-kind sono mutuamente esclusivi (il testo o il simbolo, non entrambi)" >&2
|
|
229
|
+
echo "$USAGE" >&2
|
|
230
|
+
exit 2
|
|
231
|
+
fi
|
|
198
232
|
|
|
199
233
|
MODE="${LOOM_DECK_SPAWN_MODE:-inline}"
|
|
200
234
|
PROFILE_UUID="${LOOM_DECK_PROFILE_UUID:-5a36ae48df1c4d4882f43060e3e59656}"
|
|
@@ -381,38 +415,68 @@ RES_FLAG=""
|
|
|
381
415
|
FORK_FLAG=""
|
|
382
416
|
[[ $FORK -eq 1 ]] && FORK_FLAG="--fork-session "
|
|
383
417
|
|
|
384
|
-
# ── Catalogo dei prompt iniziali (T56)
|
|
418
|
+
# ── Catalogo dei prompt iniziali (T56 · T117) ────────────────────────────────
|
|
385
419
|
# Il prompt iniziale è la scelta di CHI APRE la sessione, non una proprietà della
|
|
386
420
|
# task: lo stesso task file si apre per leggerne lo stato, per un preflight, per
|
|
387
421
|
# eseguirlo o a mani nude. Il kind è quell'intento.
|
|
388
422
|
# none → nessun prompt (≠ stringa vuota: PROMPT_ARG sparisce del tutto,
|
|
389
423
|
# come sul ramo --resume, altrimenti CC riceve un posizionale vuoto)
|
|
390
|
-
# recap →
|
|
424
|
+
# recap → skill di recap sulla task. È un DISPATCHER: `recap-status`
|
|
425
|
+
# risolve la task, classifica (progetto / task / epica) e passa a
|
|
426
|
+
# una sotto-skill. Fino a v0.45 era invece un prompt diretto
|
|
427
|
+
# ("recap stato task <id>"), scelto perché non esisteva una skill
|
|
428
|
+
# di recap tarata sulla singola task e quella di progetto avrebbe
|
|
429
|
+
# risposto largo — vincolo caduto col dispatcher.
|
|
391
430
|
# preflight → skill di preflight sulla task
|
|
392
431
|
# run → skill di esecuzione sulla task
|
|
393
432
|
# checkpoint → skill di checkpoint sulla task
|
|
394
|
-
#
|
|
395
|
-
#
|
|
396
|
-
#
|
|
433
|
+
#
|
|
434
|
+
# T117 — i testi non stanno più qui ma nel file dati `prompt-catalog`, sibling di
|
|
435
|
+
# questo script. Il deck TUI li mostra nel campo prompt del detail prima dello
|
|
436
|
+
# spawn, e per farlo deve leggerli: come `case` bash sarebbe una seconda scrittura
|
|
437
|
+
# delle stesse regole. Il file resta l'unica copia, letta da entrambi i lati.
|
|
438
|
+
# Formato a righe e non JSON perché `jq` è dipendenza OPZIONALE di questo script
|
|
439
|
+
# (con fallback) e leggere il catalogo lo renderebbe dura.
|
|
397
440
|
#
|
|
398
441
|
# Precedenza con l'env: LOOM_DECK_ENTER_PROMPT VINCE sul kind (retro-compat: chi
|
|
399
442
|
# lo usa oggi non si accorge del cambio), tranne su `none` — chiedere
|
|
400
443
|
# esplicitamente nessun prompt non è una richiesta che un default d'ambiente
|
|
401
|
-
# possa scavalcare.
|
|
402
|
-
#
|
|
403
|
-
#
|
|
404
|
-
# Ogni
|
|
444
|
+
# possa scavalcare. Non vince invece su `--prompt`, che è una richiesta esplicita
|
|
445
|
+
# e già risolta: un override d'ambiente che scavalca un testo composto a mano
|
|
446
|
+
# butterebbe via proprio la modifica che il flag esiste per portare.
|
|
447
|
+
# Ogni template nomina il TaskID via `{TASK}`: la lista del deck è monofamiglia
|
|
405
448
|
# (`T`, unico prefisso del contratto loom), quindi nessun prompt discrimina sulla
|
|
406
449
|
# forma dell'id.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
450
|
+
_prompt_template() { # <kind> → template col placeholder {TASK}, o niente
|
|
451
|
+
local want="$1" k t
|
|
452
|
+
[[ -f "$PROMPT_CATALOG" ]] || return 1
|
|
453
|
+
while IFS=$'\t' read -r k t || [[ -n "$k" ]]; do
|
|
454
|
+
[[ -z "$k" || "$k" == \#* ]] && continue
|
|
455
|
+
[[ "$k" == "$want" ]] && { printf '%s' "$t"; return 0; }
|
|
456
|
+
done < "$PROMPT_CATALOG"
|
|
457
|
+
return 1
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if [[ $PROMPT_GIVEN -eq 1 ]]; then
|
|
461
|
+
PROMPT="$PROMPT_TEXT"
|
|
462
|
+
else
|
|
463
|
+
_kind="${PROMPT_KIND:-recap}"
|
|
464
|
+
if [[ "$_kind" == "none" ]]; then
|
|
465
|
+
PROMPT=""
|
|
466
|
+
else
|
|
467
|
+
# Catalogo assente o voce mancante: il kind è già passato dall'enum sopra,
|
|
468
|
+
# quindi qui manca il FILE — un'installazione mutila, non un errore d'uso.
|
|
469
|
+
# Si degrada a nessun prompt dicendolo, invece di aprire una sessione con un
|
|
470
|
+
# prompt inventato al volo (che sarebbe la copia del catalogo).
|
|
471
|
+
if ! PROMPT="$(_prompt_template "$_kind")"; then
|
|
472
|
+
echo "catalogo prompt non leggibile ('${PROMPT_CATALOG}'): sessione senza prompt iniziale" >&2
|
|
473
|
+
PROMPT=""
|
|
474
|
+
fi
|
|
475
|
+
PROMPT="${PROMPT//\{TASK\}/${TASK}}"
|
|
476
|
+
fi
|
|
477
|
+
if [[ "$_kind" != "none" && -n "${LOOM_DECK_ENTER_PROMPT:-}" ]]; then
|
|
478
|
+
PROMPT="${LOOM_DECK_ENTER_PROMPT//\{TASK\}/${TASK}}"
|
|
479
|
+
fi
|
|
416
480
|
fi
|
|
417
481
|
|
|
418
482
|
# Comando eseguito dentro la tab: bind LOOM_TASK per la sessione, avvia CC sul PROMPT.
|
|
@@ -427,8 +491,21 @@ fi
|
|
|
427
491
|
# fork (T28) ricade nello stesso ramo — è una ripresa, con id nuovo.
|
|
428
492
|
# Il kind `none` (T56) atterra sulla stessa assenza per una ragione diversa: lì
|
|
429
493
|
# la sessione è nuova e bound, ma l'utente ha chiesto di entrarci a mani nude.
|
|
494
|
+
#
|
|
495
|
+
# QUOTING DEL PROMPT — il testo entra in `bash -lc "$IN_TAB_CMD"`, cioè in una
|
|
496
|
+
# riga che una shell PARSA. Finché i template erano scritti qui dentro bastava il
|
|
497
|
+
# vincolo "nessun apice singolo nel catalogo"; da T117 il testo può arrivare da
|
|
498
|
+
# `--prompt`, cioè digitato a mano dall'utente nel campo del detail — e lì un
|
|
499
|
+
# apice singolo chiuderebbe la stringa, consegnando alla shell tutto ciò che
|
|
500
|
+
# segue come comando. Si passa quindi per la stessa forma canonica di
|
|
501
|
+
# `shellQuote` in spawn.ts: apice chiuso, apice letterale escapato, apice
|
|
502
|
+
# riaperto (`'\''`). Dentro apici singoli nessun altro carattere resta speciale,
|
|
503
|
+
# quindi non c'è nient'altro da enumerare — newline compresi.
|
|
504
|
+
_shell_quote() { # <testo> → il testo come argomento singolo per una shell POSIX
|
|
505
|
+
printf "'%s'" "${1//\'/\'\\\'\'}"
|
|
506
|
+
}
|
|
430
507
|
PROMPT_ARG=""
|
|
431
|
-
[[ -n "$PROMPT" && -z "$RESUME_ID" ]] && PROMPT_ARG="
|
|
508
|
+
[[ -n "$PROMPT" && -z "$RESUME_ID" ]] && PROMPT_ARG="$(_shell_quote "$PROMPT")"
|
|
432
509
|
# Il SID_FLAG entra anche nel ramo --no-task, che altrimenti lo scarta insieme
|
|
433
510
|
# agli altri elementi task-bound: nel fork di una sessione SPOT il sessionId
|
|
434
511
|
# pinnato non serve a legare una task (non c'è) ma a rendere noto in anticipo
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Catalogo dei prompt iniziali di una sessione Claude aperta dal deck.
|
|
2
|
+
#
|
|
3
|
+
# Formato a righe: <kind><TAB><template>, con `{TASK}` interpolato col TaskID.
|
|
4
|
+
# Righe vuote e righe che iniziano con `#` sono commenti.
|
|
5
|
+
#
|
|
6
|
+
# Perché un file dati e non un `case` dentro deck-run: due lati hanno bisogno
|
|
7
|
+
# dello stesso testo — deck-run per comporre il comando in-tab, il deck TUI per
|
|
8
|
+
# pre-riempire il campo prompt del detail con ciò che sta per essere eseguito.
|
|
9
|
+
# Come codice, il secondo lato dovrebbe riscriverlo, e le due copie divergono
|
|
10
|
+
# alla prima voce aggiunta da una parte sola.
|
|
11
|
+
#
|
|
12
|
+
# `none` non compare: nessun prompt non è un template vuoto, è l'assenza della
|
|
13
|
+
# voce. Chi chiede un kind fuori da questo file riceve un prompt vuoto.
|
|
14
|
+
#
|
|
15
|
+
# Vincolo sui template: nessun apice singolo. Il testo finisce dentro `'...'`
|
|
16
|
+
# nel comando passato a `bash -lc`, e questo file è committato — un apice qui
|
|
17
|
+
# sarebbe un errore di scrittura, non un input da quotare.
|
|
18
|
+
recap /loom-works:recap-status {TASK}
|
|
19
|
+
preflight /loom-works:preflight-task {TASK}
|
|
20
|
+
run /loom-works:run-task {TASK}
|
|
21
|
+
checkpoint /loom-works:checkpoint-task {TASK}
|