@lamemind/loom-deck 0.66.1 → 0.67.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/actions.js +16 -2
- package/dist/inbox.js +25 -0
- package/dist/spawn.js +29 -6
- package/dist/wrap-scan.js +25 -0
- package/package.json +1 -1
- package/scripts/deck-run +8 -4
package/dist/actions.js
CHANGED
|
@@ -24,6 +24,8 @@ import { cut, cutMiddle } from './width.js';
|
|
|
24
24
|
import { saveView, viewFilePath } from './view-store.js';
|
|
25
25
|
import { onInTabCommand, runLaunch, spawnClaudeEmpty, spawnBare, spawnDeck, spawnDeckFork, spawnDeckResume, spawnTerminal, fallbackTitle, DECK_RUN, MODEL_DEFAULT, } from './spawn.js';
|
|
26
26
|
import { useTaskOps } from './task-ops.js';
|
|
27
|
+
import { inboxTitle } from './inbox.js';
|
|
28
|
+
import { wrapTitle } from './wrap-scan.js';
|
|
27
29
|
export function useDeckActions({ cwd, tasksPath, tasksDir, columns, model, setNote, }) {
|
|
28
30
|
// La riga di stato di OGNI spawn di sessione Claude: il comando esatto, come
|
|
29
31
|
// lo si scriverebbe in bash, invece di una parafrasi.
|
|
@@ -333,9 +335,18 @@ export function useDeckActions({ cwd, tasksPath, tasksDir, columns, model, setNo
|
|
|
333
335
|
* Sessione NUDA e modello esplicito: il drain lavora sulla doc, non sulla
|
|
334
336
|
* task (D10 preflight), e `MODEL_DEFAULT` passa nell'argv anche essendo il
|
|
335
337
|
* default, come `permissionMode`.
|
|
338
|
+
*
|
|
339
|
+
* Il TITOLO lo deriva `inboxTitle` dal file, e serve un `sessionId` pinnato
|
|
340
|
+
* per scriverlo nel sidecar — senza, la nota non avrebbe una chiave e la riga
|
|
341
|
+
* in lista resterebbe nuda mentre la tab porta un nome. Le due scritture
|
|
342
|
+
* stanno qui accanto per la stessa ragione di `spawnForTask`: il nome che le
|
|
343
|
+
* due superfici mostrano nasce da un valore solo.
|
|
336
344
|
*/
|
|
337
345
|
function drainInbox(file, prompt) {
|
|
338
|
-
const
|
|
346
|
+
const sid = randomUUID();
|
|
347
|
+
const title = inboxTitle(file);
|
|
348
|
+
appendNote(cwd, sid, title);
|
|
349
|
+
const spawned = spawnBare(cwd, prompt, MODEL_DEFAULT, sid, title);
|
|
339
350
|
spawned.child.on('error', () => setNote(`⚠ drain ${file.basename} fallito (${DECK_RUN})`));
|
|
340
351
|
noteSpawn(spawned);
|
|
341
352
|
}
|
|
@@ -349,7 +360,10 @@ export function useDeckActions({ cwd, tasksPath, tasksDir, columns, model, setNo
|
|
|
349
360
|
* decidere se committarlo — non progettare niente.
|
|
350
361
|
*/
|
|
351
362
|
function unwrapPath(path, prompt) {
|
|
352
|
-
const
|
|
363
|
+
const sid = randomUUID();
|
|
364
|
+
const title = wrapTitle(path);
|
|
365
|
+
appendNote(cwd, sid, title);
|
|
366
|
+
const spawned = spawnBare(cwd, prompt, 'sonnet', sid, title);
|
|
353
367
|
spawned.child.on('error', () => setNote(`⚠ srotolamento ${path} fallito (${DECK_RUN})`));
|
|
354
368
|
noteSpawn(spawned);
|
|
355
369
|
}
|
package/dist/inbox.js
CHANGED
|
@@ -192,6 +192,31 @@ export function inboxPrompt(f) {
|
|
|
192
192
|
}
|
|
193
193
|
return `/loom-works:${DRAIN_SKILL[f.natura]} ${f.basename}`;
|
|
194
194
|
}
|
|
195
|
+
/** Prefisso del titolo di una sessione aperta su un file inbox. Astrale come le
|
|
196
|
+
* emoji delle azioni su task (`ACTION_EMOJI` in `src/spawn.ts`) e per la stessa
|
|
197
|
+
* ragione: `sanitize` di `src/width.ts` sostituisce con `·` ogni glifo su cui
|
|
198
|
+
* le due contabilità di larghezza divergono, e le emoji del BMP divergono
|
|
199
|
+
* sempre. Sta anche nella whitelist di `_sane_note` in `deck-run`, o arriverebbe
|
|
200
|
+
* nella tab ridotta a niente mentre la lista del deck la mostra. */
|
|
201
|
+
const INBOX_EMOJI = '🧹';
|
|
202
|
+
/**
|
|
203
|
+
* Il titolo della sessione che il deck apre su un file inbox: `🧹 <basename>`,
|
|
204
|
+
* senza l'estensione.
|
|
205
|
+
*
|
|
206
|
+
* Il `.md` si toglie QUI e non lo si lascia cadere a valle: l'alfabeto di
|
|
207
|
+
* `_sane_note` non ha il punto, quindi il titolo della tab uscirebbe con le due
|
|
208
|
+
* lettere saldate al nome (`…-prioritariamd`) mentre la lista del deck, che
|
|
209
|
+
* legge la nota grezza dal sidecar, mostrerebbe il nome intero — la stessa
|
|
210
|
+
* conversazione con due nomi, senza errore.
|
|
211
|
+
*
|
|
212
|
+
* Uno per ogni natura, `malformato` compreso: quella sessione ripara il marker
|
|
213
|
+
* invece di drenare, ma l'oggetto su cui lavora è lo stesso file, ed è il file
|
|
214
|
+
* che il titolo nomina. L'azione la dice il prompt.
|
|
215
|
+
*/
|
|
216
|
+
export function inboxTitle(f) {
|
|
217
|
+
const name = f.basename.replace(/\.md$/i, '').replace(/\./g, '-');
|
|
218
|
+
return `${INBOX_EMOJI} ${name}`;
|
|
219
|
+
}
|
|
195
220
|
/**
|
|
196
221
|
* Il testo del file inbox, per il detail fullscreen. `null` = illeggibile —
|
|
197
222
|
* il detail lo dice e tiene l'azione attiva, come fa quello della task con un
|
package/dist/spawn.js
CHANGED
|
@@ -205,10 +205,12 @@ export const ACTION_HOTKEYS = Object.fromEntries(DETAIL_ACTIONS.map((a, i) => [a
|
|
|
205
205
|
// (⚙️ ⏱️ 🛠️ …) divergono sempre — string-width le conta 2, VTE ne disegna 1, e
|
|
206
206
|
// il VS16 non sposta nessuno dei due verdetti. Un'emoji aggiunta qui va prima
|
|
207
207
|
// passata in `sanitize` per vedere se sopravvive.
|
|
208
|
-
// Le stesse quattro
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
208
|
+
// Le stesse quattro stanno anche nella whitelist di `_sane_note` in `deck-run`,
|
|
209
|
+
// che è una lista chiusa di code point (P2 preflight): cambiare una voce qui e
|
|
210
|
+
// non là riapre la divergenza fra titolo in lista e titolo in tab. La whitelist
|
|
211
|
+
// è più larga di questa tabella — porta anche le emoji dei titoli che non
|
|
212
|
+
// nascono da un'azione su task (🧹 drain di un file inbox, `src/inbox.ts`; 📏
|
|
213
|
+
// srotolamento di un path, `src/wrap-scan.ts`).
|
|
212
214
|
// Le quadre che delimitavano il prefisso sono cadute: l'emoji da sola si stacca
|
|
213
215
|
// già dallo slug, e con lei sono uscite dalla whitelist di `_sane_note`.
|
|
214
216
|
const ACTION_EMOJI = {
|
|
@@ -399,9 +401,30 @@ export function spawnClaudeEmpty(cwd, model) {
|
|
|
399
401
|
* Il modello è passato SEMPRE, anche sul default, per la stessa ragione di
|
|
400
402
|
* `permissionMode`: lo spawn resta deterministico e leggibile nel process tree
|
|
401
403
|
* invece di dipendere da un default che può cambiare fra versioni.
|
|
404
|
+
*
|
|
405
|
+
* `sessionId` e `title` viaggiano INSIEME o non viaggiano: il titolo lo mostrano
|
|
406
|
+
* due superfici — la tab Ptyxis (via `--title-note`) e la riga della lista
|
|
407
|
+
* sessioni (via la nota nel sidecar, che è keyed sul sessionId) — e senza un id
|
|
408
|
+
* pinnato la seconda non ha una chiave su cui scrivere. Pinnarlo è quindi ciò
|
|
409
|
+
* che rende il titolo visibile in entrambe, non un binding task: una sessione
|
|
410
|
+
* `--no-task` non ne ha uno, ma un NOME ce l'ha.
|
|
411
|
+
*
|
|
412
|
+
* Il titolo sta in TESTA all'argv, prima del prompt: la riga di stato del deck
|
|
413
|
+
* taglia il comando al mezzo, e il prompt (specialmente quello cablato dello
|
|
414
|
+
* srotolamento) è l'argomento più lungo — in coda al titolo lo spingerebbe
|
|
415
|
+
* fuori vista. Stessa ragione dell'ordine di `resumeArgs`.
|
|
402
416
|
*/
|
|
403
|
-
export function
|
|
404
|
-
|
|
417
|
+
export function bareArgs(prompt, model, sessionId, title) {
|
|
418
|
+
const args = ['--no-task'];
|
|
419
|
+
if (title)
|
|
420
|
+
args.push('--title-note', title);
|
|
421
|
+
if (sessionId)
|
|
422
|
+
args.push('--session-id', sessionId);
|
|
423
|
+
args.push('--prompt', prompt, '--model', model);
|
|
424
|
+
return args;
|
|
425
|
+
}
|
|
426
|
+
export function spawnBare(cwd, prompt, model, sessionId, title) {
|
|
427
|
+
return launchDeckRun(bareArgs(prompt, model, sessionId, title), cwd);
|
|
405
428
|
}
|
|
406
429
|
// T39/T32: voce `launch` custom del file config, eseguita con cwd = project root.
|
|
407
430
|
// Spawn detached come spawnDeck: il deck lancia ma non possiede il processo.
|
package/dist/wrap-scan.js
CHANGED
|
@@ -102,6 +102,31 @@ export function wrapPrompt(path) {
|
|
|
102
102
|
'verifica risultato e fai rapporto. se le modifiche sono tutte chiaramente safe, ' +
|
|
103
103
|
'puoi committare direttamente');
|
|
104
104
|
}
|
|
105
|
+
/** Prefisso del titolo di una sessione di srotolamento. Astrale, come le altre
|
|
106
|
+
* emoji che finiscono in un titolo: le BMP `sanitize` (`src/width.ts`) le rende
|
|
107
|
+
* `·`. Va tenuta allineata alla whitelist di `_sane_note` in `deck-run`. */
|
|
108
|
+
const WRAP_EMOJI = '📏';
|
|
109
|
+
/**
|
|
110
|
+
* Il titolo della sessione che srotola un path: `📏 <path a parole>`.
|
|
111
|
+
*
|
|
112
|
+
* Le barre diventano spazi e l'estensione cade PRIMA di `_sane_note`, il cui
|
|
113
|
+
* alfabeto non ha né `/` né `.`: lasciandoceli cadere le parole si salderebbero
|
|
114
|
+
* (`runtimereferencedoc-systemmd`), e il titolo della tab divergerebbe da quello
|
|
115
|
+
* che la lista del deck legge dal sidecar.
|
|
116
|
+
*
|
|
117
|
+
* Il path di default (`.`, la project root intera) non ha parole da mostrare e
|
|
118
|
+
* diventa una frase: un titolo `📏 ` sarebbe il solo prefisso.
|
|
119
|
+
*/
|
|
120
|
+
export function wrapTitle(path) {
|
|
121
|
+
const target = path.trim() || WRAP_DEFAULT_PATH;
|
|
122
|
+
if (target === WRAP_DEFAULT_PATH)
|
|
123
|
+
return `${WRAP_EMOJI} tutto il progetto`;
|
|
124
|
+
const words = target
|
|
125
|
+
.replace(/\.md$/i, '')
|
|
126
|
+
.replace(/[./]+/g, ' ')
|
|
127
|
+
.trim();
|
|
128
|
+
return `${WRAP_EMOJI} ${words}`;
|
|
129
|
+
}
|
|
105
130
|
/**
|
|
106
131
|
* Cartella della cache, una per utente e con `mode 0700` — stessa ragione del
|
|
107
132
|
* project status: `/tmp` è condivisa, e un file a nome prevedibile può essere
|
package/package.json
CHANGED
package/scripts/deck-run
CHANGED
|
@@ -269,16 +269,20 @@ WORKDIR="${LOOM_DECK_WORKDIR:-$PWD}"
|
|
|
269
269
|
# titolo finisce dentro apici singoli in `bash -lc "claude --name '…'"`, e la
|
|
270
270
|
# nota è testo digitato a mano — enumerare i caratteri pericolosi significa
|
|
271
271
|
# sbagliarne uno, enumerare quelli ammessi no. Restano lettere, cifre, accentate
|
|
272
|
-
# italiane, spazio, trattino, underscore e
|
|
272
|
+
# italiane, spazio, trattino, underscore e sei emoji (sotto); tutto il resto
|
|
273
273
|
# (apici, backslash, `$`, backtick, `;`, quadre, ogni altra emoji…) cade.
|
|
274
274
|
#
|
|
275
|
-
# T156 — le
|
|
276
|
-
#
|
|
275
|
+
# T156 — le emoji entrano per il PREFISSO del titolo di fallback che il deck
|
|
276
|
+
# genera quando il campo nota è vuoto (`🚀 slug`, `ACTION_EMOJI` in
|
|
277
277
|
# `src/spawn.ts`). Senza di loro il prefisso spariva qui e restava nella lista
|
|
278
278
|
# del deck, che riceve la nota grezza dal sidecar `session-tasks.jsonl`: la
|
|
279
279
|
# stessa conversazione portava due titoli diversi, senza errore. Le quadre che
|
|
280
280
|
# delimitavano il prefisso sono cadute col prefisso stesso: l'emoji da sola si
|
|
281
281
|
# stacca già dallo slug.
|
|
282
|
+
# Quattro nominano un'azione su una task (📐🚀📊🏁); le altre due nominano
|
|
283
|
+
# l'oggetto di una sessione che una task non ce l'ha — 🧹 il file inbox che si
|
|
284
|
+
# drena (`inboxTitle`, `src/inbox.ts`), 📏 il path che si srotola (`wrapTitle`,
|
|
285
|
+
# `src/wrap-scan.ts`).
|
|
282
286
|
# La whitelist le nomina una per una e NON apre un intervallo di code point. Due
|
|
283
287
|
# ragioni: un'emoji arriva nella tab solo se il deck la mostra identica in lista,
|
|
284
288
|
# cioè se `sanitize` (`src/width.ts`) la lascia passare invece di renderla `·`, e
|
|
@@ -305,7 +309,7 @@ _sane_note() { # <raw> → nota ridotta, spazi collassati, cap 60 char
|
|
|
305
309
|
# `tr` non ne ha bisogno: squeeza uno spazio ASCII, e 0x20 non compare mai
|
|
306
310
|
# come byte di continuazione di una sequenza UTF-8.
|
|
307
311
|
s="$(printf '%s' "$1" \
|
|
308
|
-
| LC_ALL=C.UTF-8 sed 's/[^A-Za-z0-9 _
|
|
312
|
+
| LC_ALL=C.UTF-8 sed 's/[^A-Za-z0-9 _àèéìòùÀÈÉÌÒÙ📐🚀📊🏁🧹📏-]//g' \
|
|
309
313
|
| tr -s ' ')"
|
|
310
314
|
s="${s# }"; s="${s% }"
|
|
311
315
|
printf '%s' "${s:0:60}"
|