@lamemind/loom-deck 0.43.0 → 0.45.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 +59 -90
- package/dist/fields.js +108 -0
- package/dist/glyphs.js +22 -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/ui/panes.js +23 -9
- package/dist/view.js +31 -0
- package/dist/viewport.js +14 -15
- package/package.json +1 -1
- package/scripts/deck-run +94 -22
- package/scripts/prompt-catalog +21 -0
package/dist/ui/panes.js
CHANGED
|
@@ -5,11 +5,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
5
5
|
import { Box, Text } from 'ink';
|
|
6
6
|
import { cut, cutParts, pad, sanitize, termWidth } from '../width.js';
|
|
7
7
|
import { isDone, paneTextWidth } from '../layout.js';
|
|
8
|
-
import { CARET, CARET_OFF, LIVE_BUSY, LIVE_IDLE, LIVE_NONE, MODEL_W, SESSION_SEP, SID_CHARS, TASK_EMPTY, WARN, displayProg, modelShort, relTime, } from '../glyphs.js';
|
|
8
|
+
import { CARET, CARET_OFF, LIVE_BUSY, LIVE_IDLE, LIVE_NONE, MODEL_W, SESSION_SEP, SID_CHARS, TASK_EMPTY, WARN, displayProg, modelShort, relTime, taskTail, } from '../glyphs.js';
|
|
9
9
|
import { META_ROWS, ROW_ALL, ROW_SPOT } from '../model.js';
|
|
10
10
|
import { rowLabel, sessionTitle } from '../session-list.js';
|
|
11
11
|
import { sessionView, taskView, SESSION_VIEWS, TASK_VIEWS, } from '../pane-views.js';
|
|
12
|
-
import { describeSort, PRI_ENTRIES, PROG_ENTRIES } from '../view.js';
|
|
12
|
+
import { describeSort, padId, PRI_ENTRIES, PROG_ENTRIES } from '../view.js';
|
|
13
13
|
/**
|
|
14
14
|
* Header del pane task, tagliato QUI e non da Ink — stesso motivo del gemello
|
|
15
15
|
* `SessionsHeader`, con una differenza di rischio: qui i segmenti sono tutti
|
|
@@ -49,7 +49,7 @@ export function TasksHeader({ counts, active, above, below, focused, columns, })
|
|
|
49
49
|
const shown = cutParts(segments.map((s) => s.text), paneTextWidth(columns), segments.findIndex((s) => s.active));
|
|
50
50
|
return (_jsx(Text, { bold: true, color: focused ? 'cyan' : undefined, wrap: "truncate-end", children: segments.map((seg, i) => shown[i] ? (_jsx(Text, { color: seg.color, dimColor: seg.dim, inverse: seg.active, children: shown[i] }, i)) : null) }));
|
|
51
51
|
}
|
|
52
|
-
export function TasksPane({ tasks, counts, activeView, paneCount, view, selected, spotCount, allCount, childCount, focused, loadError, windowStart, above, below, columns, dirty, }) {
|
|
52
|
+
export function TasksPane({ tasks, counts, activeView, paneCount, view, selected, spotCount, allCount, childCount, idW, tailW, focused, loadError, windowStart, above, below, columns, dirty, }) {
|
|
53
53
|
const allSelected = selected === ROW_ALL;
|
|
54
54
|
const spotSelected = selected === ROW_SPOT;
|
|
55
55
|
return (_jsxs(Box, { flexDirection: "column", width: "50%", marginRight: 1, borderStyle: "single", borderColor: focused ? 'cyan' : 'gray', paddingX: 1, children: [_jsx(TasksHeader, { counts: counts, active: activeView, above: above, below: below, focused: focused, columns: columns }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cut(sanitize(`sort: ${describeSort(view.sort)}` +
|
|
@@ -71,17 +71,31 @@ export function TasksPane({ tasks, counts, activeView, paneCount, view, selected
|
|
|
71
71
|
// completa, su cui è keyata la selezione. +META_ROWS: le prime due
|
|
72
72
|
// righe sono le meta.
|
|
73
73
|
const sel = windowStart + i + META_ROWS === selected;
|
|
74
|
-
const
|
|
74
|
+
const tail = taskTail(childCount.get(task.id) ?? 0, dirty.has(task.id));
|
|
75
|
+
// T118 — l'id è paddato a `idW`, quindi `head` ha la stessa larghezza
|
|
76
|
+
// su ogni riga e Pri/Prog cadono sempre nella stessa colonna.
|
|
77
|
+
const id = padId(task.id, idW);
|
|
75
78
|
// Invariante ③: la descrizione è l'unico pezzo a lunghezza libera, e
|
|
76
79
|
// si taglia QUI sul budget che resta dopo le colonne fisse. Lasciarlo
|
|
77
80
|
// fare a `truncate-end` significa passare da `cli-truncate`, che
|
|
78
81
|
// restituisce una riga più larga del pane (una colonna per emoji) e
|
|
79
82
|
// quindi scrive sopra il bordo. Le parti fisse si misurano con
|
|
80
|
-
// `termWidth`:
|
|
81
|
-
const head = `${CARET_OFF}${
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
// `termWidth`: i due glifi Pri/Prog valgono 2 ciascuno.
|
|
84
|
+
const head = `${CARET_OFF}${id} ${sanitize(task.pri)} ${displayProg(task.prog)} `;
|
|
85
|
+
// T118 — la colonna della coda si riserva PRIMA di tagliare la
|
|
86
|
+
// descrizione, e per la stessa larghezza su ogni riga: appesa dopo,
|
|
87
|
+
// entrava nel budget solo dove c'era qualcosa da scrivere, e la
|
|
88
|
+
// descrizione si tagliava a una colonna diversa riga per riga.
|
|
89
|
+
//
|
|
90
|
+
// Pavimento `0` su tutte e tre le misure e non un minimo di cortesia:
|
|
91
|
+
// il budget è un TETTO. Un pavimento sopra lo spazio reale fa uscire
|
|
92
|
+
// la riga dal pane e le mangia il bordo. `reserve` si clampa su ciò
|
|
93
|
+
// che avanza, così `head + desc + coda` sta sempre dentro il pane.
|
|
94
|
+
const avail = Math.max(0, paneTextWidth(columns) - termWidth(head));
|
|
95
|
+
const reserve = Math.min(tailW, avail);
|
|
96
|
+
const descW = avail - reserve;
|
|
97
|
+
const desc = cut(task.desc, descW);
|
|
98
|
+
return (_jsxs(Text, { inverse: sel && focused, bold: sel && !focused, dimColor: !sel && isDone(task.prog), wrap: "truncate-end", children: [sel ? CARET : CARET_OFF, id, " ", sanitize(task.pri), " ", displayProg(task.prog), " ", desc, ' '.repeat(Math.max(0, descW - termWidth(desc))), pad(tail, reserve, 'right')] }, task.id));
|
|
85
99
|
}))] }));
|
|
86
100
|
}
|
|
87
101
|
/**
|
package/dist/view.js
CHANGED
|
@@ -59,6 +59,37 @@ export function idNum(id) {
|
|
|
59
59
|
const m = /^T(\d+)$/.exec(id);
|
|
60
60
|
return m ? Number(m[1]) : Number.MAX_SAFE_INTEGER;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* T118 — larghezza della colonna id, DERIVATA dalla popolazione.
|
|
64
|
+
*
|
|
65
|
+
* La popolazione è la vista attiva COMPLETA, non la finestra visibile: una
|
|
66
|
+
* larghezza misurata sulle sole righe renderizzate sposta le colonne mentre si
|
|
67
|
+
* scorre, cioè una tabella corretta a ogni frame e illeggibile nel movimento.
|
|
68
|
+
* Il chiamante deve quindi passare `paneTasks`, non l'array che finisce nel
|
|
69
|
+
* pane (`tasks` in `TaskPane` è già la finestra).
|
|
70
|
+
*
|
|
71
|
+
* Gli id sono `T` + cifre (`TASK_ID_RE`), cioè ASCII: `.length` conta colonne.
|
|
72
|
+
*/
|
|
73
|
+
export function idColumnWidth(tasks) {
|
|
74
|
+
let w = 0;
|
|
75
|
+
for (const t of tasks)
|
|
76
|
+
w = Math.max(w, t.id.length);
|
|
77
|
+
return w;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* T118 — id allineato a destra DENTRO il prefisso: `T102` in lista rende `T90`
|
|
81
|
+
* come `T 90`, non come ` T90`.
|
|
82
|
+
*
|
|
83
|
+
* Il prefisso di tipo è l'ancora con cui l'occhio riconosce la colonna:
|
|
84
|
+
* spostarlo riga per riga toglie proprio ciò che l'allineamento dava. Sono le
|
|
85
|
+
* cifre ad allinearsi fra loro, la `T` resta ferma.
|
|
86
|
+
*/
|
|
87
|
+
export function padId(id, cols) {
|
|
88
|
+
const m = /^([A-Za-z]+)(\d+)$/.exec(id);
|
|
89
|
+
if (!m)
|
|
90
|
+
return id.padEnd(cols);
|
|
91
|
+
return m[1] + ' '.repeat(Math.max(0, cols - id.length)) + m[2];
|
|
92
|
+
}
|
|
62
93
|
function rankOf(task, key) {
|
|
63
94
|
if (key === 'pri')
|
|
64
95
|
return priRank(task.pri);
|
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
|
@@ -11,14 +11,18 @@
|
|
|
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=*)
|
|
@@ -150,6 +169,9 @@ USAGE="uso: deck-run <TaskID> [--model <alias>] [--prompt-kind <kind>] [--sessio
|
|
|
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,7 +415,7 @@ 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.
|
|
@@ -391,28 +425,53 @@ FORK_FLAG=""
|
|
|
391
425
|
# preflight → skill di preflight sulla task
|
|
392
426
|
# run → skill di esecuzione sulla task
|
|
393
427
|
# checkpoint → skill di checkpoint sulla task
|
|
394
|
-
#
|
|
395
|
-
#
|
|
396
|
-
#
|
|
428
|
+
#
|
|
429
|
+
# T117 — i testi non stanno più qui ma nel file dati `prompt-catalog`, sibling di
|
|
430
|
+
# questo script. Il deck TUI li mostra nel campo prompt del detail prima dello
|
|
431
|
+
# spawn, e per farlo deve leggerli: come `case` bash sarebbe una seconda scrittura
|
|
432
|
+
# delle stesse regole. Il file resta l'unica copia, letta da entrambi i lati.
|
|
433
|
+
# Formato a righe e non JSON perché `jq` è dipendenza OPZIONALE di questo script
|
|
434
|
+
# (con fallback) e leggere il catalogo lo renderebbe dura.
|
|
397
435
|
#
|
|
398
436
|
# Precedenza con l'env: LOOM_DECK_ENTER_PROMPT VINCE sul kind (retro-compat: chi
|
|
399
437
|
# lo usa oggi non si accorge del cambio), tranne su `none` — chiedere
|
|
400
438
|
# esplicitamente nessun prompt non è una richiesta che un default d'ambiente
|
|
401
|
-
# possa scavalcare.
|
|
402
|
-
#
|
|
403
|
-
#
|
|
404
|
-
# Ogni
|
|
439
|
+
# possa scavalcare. Non vince invece su `--prompt`, che è una richiesta esplicita
|
|
440
|
+
# e già risolta: un override d'ambiente che scavalca un testo composto a mano
|
|
441
|
+
# butterebbe via proprio la modifica che il flag esiste per portare.
|
|
442
|
+
# Ogni template nomina il TaskID via `{TASK}`: la lista del deck è monofamiglia
|
|
405
443
|
# (`T`, unico prefisso del contratto loom), quindi nessun prompt discrimina sulla
|
|
406
444
|
# forma dell'id.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
445
|
+
_prompt_template() { # <kind> → template col placeholder {TASK}, o niente
|
|
446
|
+
local want="$1" k t
|
|
447
|
+
[[ -f "$PROMPT_CATALOG" ]] || return 1
|
|
448
|
+
while IFS=$'\t' read -r k t || [[ -n "$k" ]]; do
|
|
449
|
+
[[ -z "$k" || "$k" == \#* ]] && continue
|
|
450
|
+
[[ "$k" == "$want" ]] && { printf '%s' "$t"; return 0; }
|
|
451
|
+
done < "$PROMPT_CATALOG"
|
|
452
|
+
return 1
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if [[ $PROMPT_GIVEN -eq 1 ]]; then
|
|
456
|
+
PROMPT="$PROMPT_TEXT"
|
|
457
|
+
else
|
|
458
|
+
_kind="${PROMPT_KIND:-recap}"
|
|
459
|
+
if [[ "$_kind" == "none" ]]; then
|
|
460
|
+
PROMPT=""
|
|
461
|
+
else
|
|
462
|
+
# Catalogo assente o voce mancante: il kind è già passato dall'enum sopra,
|
|
463
|
+
# quindi qui manca il FILE — un'installazione mutila, non un errore d'uso.
|
|
464
|
+
# Si degrada a nessun prompt dicendolo, invece di aprire una sessione con un
|
|
465
|
+
# prompt inventato al volo (che sarebbe la copia del catalogo).
|
|
466
|
+
if ! PROMPT="$(_prompt_template "$_kind")"; then
|
|
467
|
+
echo "catalogo prompt non leggibile ('${PROMPT_CATALOG}'): sessione senza prompt iniziale" >&2
|
|
468
|
+
PROMPT=""
|
|
469
|
+
fi
|
|
470
|
+
PROMPT="${PROMPT//\{TASK\}/${TASK}}"
|
|
471
|
+
fi
|
|
472
|
+
if [[ "$_kind" != "none" && -n "${LOOM_DECK_ENTER_PROMPT:-}" ]]; then
|
|
473
|
+
PROMPT="${LOOM_DECK_ENTER_PROMPT//\{TASK\}/${TASK}}"
|
|
474
|
+
fi
|
|
416
475
|
fi
|
|
417
476
|
|
|
418
477
|
# Comando eseguito dentro la tab: bind LOOM_TASK per la sessione, avvia CC sul PROMPT.
|
|
@@ -427,8 +486,21 @@ fi
|
|
|
427
486
|
# fork (T28) ricade nello stesso ramo — è una ripresa, con id nuovo.
|
|
428
487
|
# Il kind `none` (T56) atterra sulla stessa assenza per una ragione diversa: lì
|
|
429
488
|
# la sessione è nuova e bound, ma l'utente ha chiesto di entrarci a mani nude.
|
|
489
|
+
#
|
|
490
|
+
# QUOTING DEL PROMPT — il testo entra in `bash -lc "$IN_TAB_CMD"`, cioè in una
|
|
491
|
+
# riga che una shell PARSA. Finché i template erano scritti qui dentro bastava il
|
|
492
|
+
# vincolo "nessun apice singolo nel catalogo"; da T117 il testo può arrivare da
|
|
493
|
+
# `--prompt`, cioè digitato a mano dall'utente nel campo del detail — e lì un
|
|
494
|
+
# apice singolo chiuderebbe la stringa, consegnando alla shell tutto ciò che
|
|
495
|
+
# segue come comando. Si passa quindi per la stessa forma canonica di
|
|
496
|
+
# `shellQuote` in spawn.ts: apice chiuso, apice letterale escapato, apice
|
|
497
|
+
# riaperto (`'\''`). Dentro apici singoli nessun altro carattere resta speciale,
|
|
498
|
+
# quindi non c'è nient'altro da enumerare — newline compresi.
|
|
499
|
+
_shell_quote() { # <testo> → il testo come argomento singolo per una shell POSIX
|
|
500
|
+
printf "'%s'" "${1//\'/\'\\\'\'}"
|
|
501
|
+
}
|
|
430
502
|
PROMPT_ARG=""
|
|
431
|
-
[[ -n "$PROMPT" && -z "$RESUME_ID" ]] && PROMPT_ARG="
|
|
503
|
+
[[ -n "$PROMPT" && -z "$RESUME_ID" ]] && PROMPT_ARG="$(_shell_quote "$PROMPT")"
|
|
432
504
|
# Il SID_FLAG entra anche nel ramo --no-task, che altrimenti lo scarta insieme
|
|
433
505
|
# agli altri elementi task-bound: nel fork di una sessione SPOT il sessionId
|
|
434
506
|
# 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 recap stato task {TASK}
|
|
19
|
+
preflight /loom-works:preflight-task {TASK}
|
|
20
|
+
run /loom-works:run-task {TASK}
|
|
21
|
+
checkpoint /loom-works:checkpoint-task {TASK}
|