@lamemind/loom-deck 0.15.0 → 0.16.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 +13 -3
- package/dist/task-edit.js +5 -5
- package/dist/tasks.js +17 -5
- package/dist/view.js +9 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,12 +4,16 @@ Deck TUI (Ink) **per-progetto** della famiglia [loom](https://github.com/lamemin
|
|
|
4
4
|
|
|
5
5
|
Legge il `tasks.md` del progetto e, con un tasto (poi un click), **spawna** una tab
|
|
6
6
|
[Ptyxis](https://gitlab.gnome.org/chergert/ptyxis) che avvia una sessione Claude Code
|
|
7
|
-
già bound alla task via `LOOM_TASK`,
|
|
7
|
+
già bound alla task via `LOOM_TASK`, con un prompt di recap sullo stato della task.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
↑↓ scegli la task → ⏎ → tab CC di fianco →
|
|
10
|
+
↑↓ scegli la task → ⏎ → tab CC di fianco → LOOM_TASK bound + recap stato task
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
Entrambi i prefissi del contratto loom entrano in lista: **`T`** (code task) e
|
|
14
|
+
**`D`** (doc task). Nessuna differenza di trattamento — il prompt iniziale è un
|
|
15
|
+
recap, non l'invocazione di una skill, quindi vale identico sulle due famiglie.
|
|
16
|
+
|
|
13
17
|
## Ruolo nella famiglia loom
|
|
14
18
|
|
|
15
19
|
`loom-deck` è un **client** con runtime proprio (TUI Ink) che **consuma** il contratto
|
|
@@ -193,6 +197,11 @@ p priorità s stato i id
|
|
|
193
197
|
|
|
194
198
|
Partendo da catena vuota, digitare `ppi` produce `[pri ↓, id ↑]`. Il ciclo parte
|
|
195
199
|
sempre **dallo stato corrente**, che il modale mostra dal vivo mentre digiti.
|
|
200
|
+
|
|
201
|
+
Sull'**id** il confronto è numerico (`T9` prima di `T10`, non lessicografico) e i
|
|
202
|
+
due prefissi sono blocchi distinti — le `D` in coda alle `T`: i counter `T` e `D`
|
|
203
|
+
sono separati nel contratto loom, quindi `T01` e `D01` non sono confrontabili
|
|
204
|
+
come numeri soli.
|
|
196
205
|
A parità su tutte le chiavi decide sempre l'`id` (confronto **numerico**: `T9`
|
|
197
206
|
prima di `T10`) → l'ordine è deterministico, mai instabile fra un refresh e l'altro.
|
|
198
207
|
|
|
@@ -234,7 +243,8 @@ npx @lamemind/loom-deck
|
|
|
234
243
|
scripts/deck-run T18
|
|
235
244
|
```
|
|
236
245
|
|
|
237
|
-
Apre una tab Ptyxis nella window attiva con `LOOM_TASK=T18 claude '
|
|
246
|
+
Apre una tab Ptyxis nella window attiva con `LOOM_TASK=T18 claude 'recap stato task T18'`
|
|
247
|
+
(prompt override-abile via `LOOM_DECK_ENTER_PROMPT`, placeholder `{TASK}`).
|
|
238
248
|
|
|
239
249
|
## Sviluppo (TUI Ink)
|
|
240
250
|
|
package/dist/task-edit.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// isolata in `writeTaskEdit`, così la grammatica delle sostituzioni è testabile
|
|
4
4
|
// senza toccare il filesystem.
|
|
5
5
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
-
import { findTaskFile } from './tasks.js';
|
|
6
|
+
import { TASK_ID_RE, findTaskFile } from './tasks.js';
|
|
7
7
|
// Il task file scrive la priorità per NOME (`- **Priority**: Med`), tasks.md la
|
|
8
8
|
// scrive come GLIFO (colonna Pri). Due rappresentazioni dello stesso valore →
|
|
9
9
|
// due mappe, entrambe keyed sul nome canonico di view.ts (unica fonte del rango).
|
|
@@ -81,11 +81,11 @@ export function initialDetail(current, prog, date = today()) {
|
|
|
81
81
|
* restano i token grezzi originali: nessun re-flow della tabella, il diff resta
|
|
82
82
|
* di una riga. `ok:false` = id assente → il chiamante non scrive nulla.
|
|
83
83
|
*
|
|
84
|
-
* L'id deve rispettare
|
|
85
|
-
*
|
|
86
|
-
* di separatore, riscrivendone le
|
|
84
|
+
* L'id deve rispettare `TASK_ID_RE` (importato da tasks.ts — stesso gate di
|
|
85
|
+
* `parseTasks`, non una copia): senza, un id arbitrario matcherebbe la riga di
|
|
86
|
+
* HEADER (`| ID | Pri | K | Prog |`) o quella di separatore, riscrivendone le
|
|
87
|
+
* celle e sfondando la tabella.
|
|
87
88
|
*/
|
|
88
|
-
const TASK_ID_RE = /^T\d+$/;
|
|
89
89
|
export function updateTasksMdRow(content, id, priGlyph, progGlyph) {
|
|
90
90
|
if (!TASK_ID_RE.test(id))
|
|
91
91
|
return { content, ok: false };
|
package/dist/tasks.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { normalizeEmoji } from './viewport.js';
|
|
4
|
+
/**
|
|
5
|
+
* Forma di un ID task: `T` (code) o `D` (doc) + numero. FONTE UNICA del gate —
|
|
6
|
+
* `parseTasks` lo usa per scartare header/separatore della tabella, `task-edit`
|
|
7
|
+
* per non riscrivere per sbaglio quelle stesse righe. Tenerlo in un posto solo
|
|
8
|
+
* evita che i due gate divergano (uno accetterebbe righe che l'altro rifiuta).
|
|
9
|
+
*/
|
|
10
|
+
export const TASK_ID_RE = /^[TD]\d+$/;
|
|
4
11
|
// D1 (preflight T20): default docs/tasks.md, override della docs-root via env
|
|
5
12
|
// LOOM_DECK_DOCS_ROOT (es. questo progetto usa `runtime`). No auto-detect.
|
|
6
13
|
export function resolveTasksPath(cwd = process.cwd()) {
|
|
@@ -13,9 +20,11 @@ export function resolveTasksDir(cwd = process.cwd()) {
|
|
|
13
20
|
return join(dirname(resolveTasksPath(cwd)), 'tasks');
|
|
14
21
|
}
|
|
15
22
|
// Estrae le righe `| Tnn | Pri | K | Prog | Task |` della Tasks Overview.
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
23
|
+
// Due prefissi ammessi (contratto plugin, counter separati): `T` = code task,
|
|
24
|
+
// `D` = doc task. Le D entrano in lista come le T perché `deck-run` NON inietta
|
|
25
|
+
// `run-task`: il prompt iniziale è un recap diretto ("recap stato task <id>"),
|
|
26
|
+
// valido identico su una doc task. Righe header/separatore non matchano `^[TD]\d+$`
|
|
27
|
+
// → scartate.
|
|
19
28
|
export function parseTasks(content) {
|
|
20
29
|
const tasks = [];
|
|
21
30
|
for (const line of content.split('\n')) {
|
|
@@ -25,7 +34,7 @@ export function parseTasks(content) {
|
|
|
25
34
|
const cells = t.split('|').map((c) => c.trim());
|
|
26
35
|
// cells[0] = '' (prima del primo |), cells[last] = '' (dopo l'ultimo |)
|
|
27
36
|
const id = cells[1];
|
|
28
|
-
if (
|
|
37
|
+
if (!TASK_ID_RE.test(id))
|
|
29
38
|
continue;
|
|
30
39
|
// Colonne overview: | ID | Pri | K | Prog | Task | → cells[2]=Pri (icona
|
|
31
40
|
// priorità), cells[4]=Prog. K (Kind, cells[3]) non serve al deck.
|
|
@@ -77,7 +86,10 @@ export function parseTaskDetail(id, content) {
|
|
|
77
86
|
let inDesc = false;
|
|
78
87
|
for (const line of content.split('\n')) {
|
|
79
88
|
if (!title && line.startsWith('# ')) {
|
|
80
|
-
|
|
89
|
+
// I due template scrivono H1 diversi — `# Task: …` (code) e
|
|
90
|
+
// `# Doc Task: …` (doc): entrambi i cappelli vanno via, il titolo mostrato
|
|
91
|
+
// è la descrizione, non la sua categoria (già data dall'id).
|
|
92
|
+
title = line.replace(/^#\s+/, '').replace(/^(?:Doc\s+)?Task:\s*/, '').trim();
|
|
81
93
|
continue;
|
|
82
94
|
}
|
|
83
95
|
if (line.startsWith('## ')) {
|
package/dist/view.js
CHANGED
|
@@ -53,11 +53,16 @@ export function progRank(glyph) {
|
|
|
53
53
|
return PROG_TABLE.find((e) => e.glyph === g)?.rank ?? UNKNOWN_RANK;
|
|
54
54
|
}
|
|
55
55
|
// `T10`.localeCompare(`T9`) mette T10 prima: l'ID va confrontato NUMERICO.
|
|
56
|
-
// parseTasks ammette
|
|
57
|
-
// il
|
|
56
|
+
// parseTasks ammette due prefissi con counter SEPARATI (`T` code, `D` doc) →
|
|
57
|
+
// il solo numero non basta, T01 e D01 collidono. Il prefisso pesa quindi come
|
|
58
|
+
// cifra più significativa (T prima di D, blocco doc in coda), il numero come
|
|
59
|
+
// meno significativa: il rango resta un intero singolo, quindi il comparator
|
|
60
|
+
// della chain non cambia forma. Il gap 1e6 è oltre qualunque counter reale.
|
|
61
|
+
// Un id fuori forma non deve far esplodere il comparator → coda con MAX_SAFE.
|
|
62
|
+
const ID_PREFIX_RANK = { T: 0, D: 1 };
|
|
58
63
|
export function idNum(id) {
|
|
59
|
-
const m = /^
|
|
60
|
-
return m ? Number(m[
|
|
64
|
+
const m = /^([TD])(\d+)$/.exec(id);
|
|
65
|
+
return m ? ID_PREFIX_RANK[m[1]] * 1_000_000 + Number(m[2]) : Number.MAX_SAFE_INTEGER;
|
|
61
66
|
}
|
|
62
67
|
function rankOf(task, key) {
|
|
63
68
|
if (key === 'pri')
|
package/package.json
CHANGED