@lamemind/loom-deck 0.56.1 → 0.57.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/dist/viewport.js CHANGED
@@ -29,6 +29,12 @@ const MAX_DETAIL_LINES = 6;
29
29
  // a un pane va scalata qui, o il frame sfonda `rows` e Ink passa a
30
30
  // clearTerminal (frame-fantasma nello scrollback di VTE).
31
31
  const TASKS_PANE_CHROME = 6; // 2 bordi + header "Tasks (n)" + riga sort + 2 righe meta
32
+ // T134 — vale per ENTRAMBI i pane dello slot destro: il pane inbox ha la stessa
33
+ // cornice (2 bordi + header) e uno solo dei due è montato alla volta, quindi
34
+ // `Budget.sessionRows` è la capienza del pane destro qualunque esso sia. Se un
35
+ // giorno l'inbox guadagnasse una riga fissa, questa costante deve sdoppiarsi e
36
+ // il budget guadagnare un ramo: una riga fissa non contata è ciò che fa sfondare
37
+ // `rows` e manda Ink nel ramo `clearTerminal`.
32
38
  const SESSIONS_PANE_CHROME = 3; // 2 bordi + header "Sessions · …"
33
39
  const PREVIEW_CHROME = 3; // marginTop + 2 bordi
34
40
  /** Preview sessione: righe fisse = titolo + riga meta (size · turni · ultima
@@ -36,6 +42,12 @@ const PREVIEW_CHROME = 3; // marginTop + 2 bordi
36
42
  * variabili, ciascuna al più MAX_SESSION_PREVIEW righe. */
37
43
  const SESSION_DETAIL_FIXED = 2;
38
44
  const MAX_SESSION_PREVIEW = 3;
45
+ /** T134 — preview inbox: nome del file + riga meta (natura, marcatori, branch,
46
+ * cappello, cifre, età). Righe TUTTE fisse, nessuna variabile: il corpo del
47
+ * file sta nel detail fullscreen, e mostrarne qui le prime righe costerebbe una
48
+ * lettura di file a ogni movimento di selezione per un'anteprima che il detail
49
+ * dà per intero. */
50
+ const INBOX_DETAIL_FIXED = 2;
39
51
  /** Altezza di ciascuna modale, marginTop incluso. In flusso, non in overlay:
40
52
  * spingono giù i pane, quindi il loro costo va scalato dal budget. */
41
53
  export const MODAL_HEIGHT = {
@@ -73,6 +85,14 @@ export const MODAL_HEIGHT = {
73
85
  // progetto è un documento lungo quanto un task file, quindi vale la stessa
74
86
  // ragione delle altre quattro. La sua altezza la distribuisce `statusCapacity`.
75
87
  status: 0,
88
+ // T134 — il detail di un file inbox: sesta sostitutiva. Un file di nozioni
89
+ // arriva a 28KB, cioè più lungo di ogni task file del progetto: la stessa
90
+ // ragione delle altre cinque. Altezza da `inboxDetailCapacity`.
91
+ inbox: 0,
92
+ // T134 — la lista hard-wrap: settima sostitutiva. Sul cappello lo scan trova
93
+ // 77 `WRAP` e 147 `misto`, cioè una lista che non entra in nessun box sopra i
94
+ // pane. Altezza da `wrapListCapacity`.
95
+ wrap: 0,
76
96
  };
77
97
  /**
78
98
  * Distribuisce le righe disponibili fra lista task, lista sessioni e preview.
@@ -158,6 +178,16 @@ export function layoutBudget(input) {
158
178
  previewCost = fixed + sessionFirstLines + sessionLastLines;
159
179
  }
160
180
  }
181
+ else if (input.preview === 'inbox') {
182
+ // Tutte righe fisse: o il blocco entra intero o non esiste. Non c'è una
183
+ // parte variabile da sacrificare per farlo stare, quindi non c'è nemmeno la
184
+ // domanda di quanto concedergli.
185
+ const fixed = PREVIEW_CHROME + INBOX_DETAIL_FIXED;
186
+ if (avail - floor - fixed >= 0) {
187
+ preview = true;
188
+ previewCost = fixed;
189
+ }
190
+ }
161
191
  // Ciò che resta va a ENTRAMBE le colonne: la preview sta sotto il blocco dei
162
192
  // pane, quindi il suo costo lo pagano insieme una volta sola.
163
193
  const listAvail = avail - previewCost;
@@ -272,6 +302,36 @@ const STATUS_CHROME = 8;
272
302
  export function statusCapacity(rows) {
273
303
  return Math.max(0, (rows || 24) - SLACK - STATUS_CHROME);
274
304
  }
305
+ // T134 — cornice del detail di un file inbox. Gemella di STATUS_CHROME più una
306
+ // riga: il detail porta in fondo la riga dell'azione (quale skill partirà su
307
+ // `⏎`), che è l'unica cosa che l'utente deve leggere prima di premere.
308
+ // 2 bordi del box esterno
309
+ // 1 riga di testata (natura :: nome del file)
310
+ // 1 riga meta (marcatori · cifre · posizione nel testo)
311
+ // 1 riga hint
312
+ // 1 marginTop del box corpo
313
+ // 2 bordi del box corpo
314
+ // 1 marginTop della riga azione
315
+ // 1 riga azione
316
+ const INBOX_CHROME = 10;
317
+ /** Righe di file inbox che entrano nel terminale. */
318
+ export function inboxDetailCapacity(rows) {
319
+ return Math.max(0, (rows || 24) - SLACK - INBOX_CHROME);
320
+ }
321
+ // T134 — cornice della lista hard-wrap.
322
+ // 2 bordi del box esterno
323
+ // 1 riga di testata (conteggi + ora dell'ultimo scan)
324
+ // 1 riga hint
325
+ // 1 marginTop del box lista
326
+ // 2 bordi del box lista
327
+ // 1 marginTop del box path
328
+ // 2 bordi del box path
329
+ // 1 riga del campo path
330
+ const WRAP_CHROME = 12;
331
+ /** Righe di lista che entrano nel terminale. */
332
+ export function wrapListCapacity(rows) {
333
+ return Math.max(0, (rows || 24) - SLACK - WRAP_CHROME);
334
+ }
275
335
  /**
276
336
  * Righe scorse da una pressione di PagGiù/PagSu: METÀ della capienza visibile.
277
337
  *
package/dist/wrap-scan.js CHANGED
@@ -45,7 +45,11 @@ export function parseWrapTsv(stdout) {
45
45
  if (m)
46
46
  fields[m[1]] = m[2];
47
47
  }
48
- const column = Number(fields.col);
48
+ // `Number('')` vale 0, non NaN: senza il controllo sul campo VUOTO una
49
+ // colonna non stimabile (troppe poche righe candidate) si leggerebbe come
50
+ // `col=0`, cioè una misura che non esiste travestita da misura.
51
+ const raw = fields.col ?? '';
52
+ const column = raw === '' ? Number.NaN : Number(raw);
49
53
  out.push({
50
54
  verdict,
51
55
  path: cells[1],
@@ -73,6 +77,31 @@ export function parseWrapTsv(stdout) {
73
77
  export function wrapCount(files) {
74
78
  return files.filter((f) => f.verdict === 'WRAP').length;
75
79
  }
80
+ /** Quanti file `misto`: fuori dal contatore ma in lista, col proprio marker. */
81
+ export function mixedCount(files) {
82
+ return files.filter((f) => f.verdict === 'misto').length;
83
+ }
84
+ /** Radice di default dell'area di srotolamento: la project root intera (D4). */
85
+ export const WRAP_DEFAULT_PATH = '.';
86
+ /**
87
+ * Il prompt della sessione che srotola un path.
88
+ *
89
+ * CABLATO per decisione, non per fretta (D9): parametrizzarlo prima di aver
90
+ * visto come si comporta significherebbe scegliere i parametri a occhio. Le tre
91
+ * cose che dice sono le tre che rendono l'operazione reversibile e verificabile
92
+ * — backup fuori dal repo, rapporto, commit solo se il diff è chiaramente
93
+ * innocuo — e nessuna delle tre è una preferenza da esporre.
94
+ *
95
+ * Il `<path>` è l'unico buco. Arriva dal campo della lista e finisce dentro
96
+ * `--prompt` come argv singolo, quindi non passa da nessuna shell prima di
97
+ * `deck-run`, che lo quota ad apici singoli: non c'è quoting da fare qui.
98
+ */
99
+ export function wrapPrompt(path) {
100
+ const target = path.trim() || WRAP_DEFAULT_PATH;
101
+ return (`lancia md-wrap modo apply su ${target} e backup in cartella tmp dedicata. ` +
102
+ 'verifica risultato e fai rapporto. se le modifiche sono tutte chiaramente safe, ' +
103
+ 'puoi committare direttamente');
104
+ }
76
105
  /**
77
106
  * Cartella della cache, una per utente e con `mode 0700` — stessa ragione del
78
107
  * project status: `/tmp` è condivisa, e un file a nome prevedibile può essere
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamemind/loom-deck",
3
- "version": "0.56.1",
3
+ "version": "0.57.0",
4
4
  "description": "Deck TUI Ink per-progetto della famiglia loom: legge tasks.md e spawna sessioni Claude Code bound via LOOM_TASK",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/deck-run CHANGED
@@ -26,8 +26,12 @@
26
26
  # Il quarto asse (T108) è indipendente dagli altri tre: vale sul ramo bound come
27
27
  # su --no-task, su una sessione nuova come su una ripresa.
28
28
  #
29
- # Con --no-task la sessione è NUDA: niente LOOM_TASK, niente prompt iniziale,
30
- # niente --session-id. Serve al lavoro spot che non appartiene a nessuna task.
29
+ # Con --no-task la sessione è NUDA: niente LOOM_TASK, niente --session-id.
30
+ # Serve al lavoro spot che non appartiene a nessuna task.
31
+ # T134 — un prompt iniziale ce l'ha se glielo si passa LETTERALE (`--prompt`):
32
+ # il lavoro sulla doc — drenare un file inbox, srotolare l'hard-wrap di un path
33
+ # — non appartiene a nessuna task e non deve ereditarne una. I `--prompt-kind`
34
+ # del catalogo restano invece rifiutati con --no-task: nominano tutti un TaskID.
31
35
  #
32
36
  # Con --resume <sid> riprende una conversazione esistente; aggiungendo --fork la
33
37
  # riprende RAMANDOLA (`--fork-session`): id nuovo, transcript copiato, l'origine
@@ -172,7 +176,9 @@ USAGE="uso: deck-run <TaskID> [--model <alias>] [--prompt-kind <kind>] [--sessio
172
176
  checkpoint /loom-works:checkpoint-task <TaskID>
173
177
 
174
178
  --prompt prompt LETTERALE, per chi lo ha già composto (o fatto
175
- modificare). Esclusivo con --prompt-kind; vuoto = nessun prompt
179
+ modificare). Esclusivo con --prompt-kind; vuoto = nessun
180
+ prompt. Ammesso anche con --no-task: un testo già scritto non
181
+ ha una task da nominare, a differenza dei kind del catalogo
176
182
 
177
183
  --model modello della sessione: fable|opus|sonnet|haiku (default: opus)
178
184
  valore ignoto → fallback sul default, con avviso su stderr
@@ -219,11 +225,21 @@ if [[ $NO_TASK -eq 1 && -n "$PROMPT_KIND" ]]; then
219
225
  echo "$USAGE" >&2
220
226
  exit 2
221
227
  fi
222
- if [[ $NO_TASK -eq 1 && $PROMPT_GIVEN -eq 1 ]]; then
223
- echo "--prompt richiede una task: una sessione --no-task non ha prompt iniziale" >&2
224
- echo "$USAGE" >&2
225
- exit 2
226
- fi
228
+ # T134/D9 `--prompt` con `--no-task` è AMMESSO, e il divieto che c'era qui è
229
+ # caduto. Il razionale del divieto valeva per il catalogo, non per il testo
230
+ # letterale: i sette kind nominano tutti il TaskID, quindi senza task non c'è
231
+ # nulla da chiedere loro; un prompt già composto dal chiamante non ha invece
232
+ # nessuna task da nominare — e ce ne sono di legittimi che non ne hanno una.
233
+ # Il caso che ha fatto cadere il divieto: il deck offre il drain di un file
234
+ # inbox e lo srotolamento dei `.md` di un path, due lavori che stanno sulla doc
235
+ # e non su una task; legarli a un cappello esporterebbe `LOOM_TASK`, farebbe
236
+ # iniettare in contesto il task file di un lavoro chiuso e metterebbe `· T<n>`
237
+ # nel titolo della tab (D10 preflight).
238
+ #
239
+ # Il divieto gemello su `--prompt-kind` RESTA. Toglierlo non è simmetrico:
240
+ # ogni template del catalogo interpola `{TASK}`, e senza task produrrebbe un
241
+ # prompt come `/loom-works:run-task ` con l'id vuoto — cioè una sessione che
242
+ # parte su un comando monco, che è il guasto che il divieto esiste per evitare.
227
243
  # Due modi di dire la stessa cosa: accettarli insieme obbligherebbe a stabilire
228
244
  # quale vince, cioè a scrivere una precedenza che nessun chiamante ha chiesto.
229
245
  if [[ $PROMPT_GIVEN -eq 1 && -n "$PROMPT_KIND" ]]; then
@@ -528,7 +544,12 @@ PROMPT_ARG=""
528
544
  # scelta di chi apre la sessione, non una proprietà della task né della ripresa
529
545
  # (T108) — stesso layer di MODE_FLAG, che infatti gli sta accanto.
530
546
  if [[ $NO_TASK -eq 1 ]]; then
531
- _default_intab="${PROFILE_ENV}claude --name '${TITLE}' ${MODE_FLAG}${MODEL_FLAG}${SID_FLAG}${RES_FLAG}${FORK_FLAG}"
547
+ # T134/D9 il PROMPT_ARG entra anche qui: dal 134 una sessione nuda può
548
+ # nascere su un prompt letterale (drain di un file inbox, srotolamento di un
549
+ # path). Resta vuoto su ogni altro percorso `--no-task`, perché senza
550
+ # `--prompt` il ramo del catalogo non gira — `PROMPT_KIND` con `--no-task`
551
+ # è ancora rifiutato a monte.
552
+ _default_intab="${PROFILE_ENV}claude --name '${TITLE}' ${MODE_FLAG}${MODEL_FLAG}${SID_FLAG}${RES_FLAG}${FORK_FLAG}${PROMPT_ARG}"
532
553
  else
533
554
  _default_intab="${PROFILE_ENV}LOOM_TASK=${TASK} claude --name '${TITLE}' ${MODE_FLAG}${MODEL_FLAG}${SID_FLAG}${RES_FLAG}${FORK_FLAG}${PROMPT_ARG}"
534
555
  fi