@lamemind/loom-deck 0.7.0 → 0.8.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/cli.js CHANGED
@@ -10,7 +10,7 @@ import { dirname, join } from 'node:path';
10
10
  import { resolveTasksPath, resolveTasksDir, loadTasks, loadTaskDetail, } from './tasks.js';
11
11
  import { discoverProjectSessions } from './sessions.js';
12
12
  import { appendTaskBinding, loadTaskBindings } from './task-index.js';
13
- import { loadLaunch } from './config.js';
13
+ import { loadIdentity, loadLaunch } from './config.js';
14
14
  import { applyView, cycleSort, describeSort, priName, progName, toggleHidden, PRI_ENTRIES, PROG_ENTRIES, } from './view.js';
15
15
  import { initialDetail, progressText, writeTaskEdit, PRI_GLYPH, PRI_LABEL, PROG_GLYPH, } from './task-edit.js';
16
16
  import { loadView, saveView, viewFilePath } from './view-store.js';
@@ -67,6 +67,22 @@ function runLaunch(entry, cwd) {
67
67
  child.unref();
68
68
  return child;
69
69
  }
70
+ // T37 — surface STANDARD LAUNCH `terminal`: built-in e universale (nessuna
71
+ // dichiarazione in `launch[]`), ma di natura launch — fire-once, nessuno stato.
72
+ // Il deck gira già DENTRO una tab Ptyxis → `--tab` mette il terminale accanto a
73
+ // sé nella stessa finestra, invece di sparpagliare finestre.
74
+ // Nessun `-- CMD`: l'azione È aprire la shell (differenza dalle launch custom,
75
+ // che eseguono un comando dentro `bash -lic`).
76
+ // `-T <title>` col core `<owner> <name>` tiene la finestra matchabile da compass
77
+ // anche mentre la tab attiva è il terminale; senza identità nel file config si
78
+ // spawna senza titolo (la surface resta funzionante, il progetto risulta assente
79
+ // dal radar finché quella tab è in primo piano).
80
+ function spawnTerminal(cwd, title) {
81
+ const args = title ? ['--tab', '-T', title, '-d', cwd] : ['--tab', '-d', cwd];
82
+ const child = spawn('ptyxis', args, { cwd, detached: true, stdio: 'ignore' });
83
+ child.unref();
84
+ return child;
85
+ }
70
86
  // Comando claude (override per ambienti dove non è su PATH; loom-deck → NPM).
71
87
  const CLAUDE_CMD = process.env.LOOM_DECK_CLAUDE_CMD ?? 'claude';
72
88
  // T30: create-task inline. Spawna CC HEADLESS (`-p`) con `--session-id` pinnato
@@ -286,6 +302,8 @@ function Deck({ cwd, tasksPath, tasksDir }) {
286
302
  const [editRow, setEditRow] = useState(0);
287
303
  // Voci launch del progetto (T32): lette una volta, raggiunte per indice 1..9.
288
304
  const launch = useMemo(() => loadLaunch(cwd), [cwd]);
305
+ // Identità (T37): titolo delle tab terminale spawnate col tasto `t`.
306
+ const identity = useMemo(() => loadIdentity(cwd), [cwd]);
289
307
  // La vista è una trasformazione DERIVATA, applicata a valle del load: il
290
308
  // polling di tasks.md continua a funzionare senza saperne nulla.
291
309
  const { visible: viewTasks, hidden: hiddenTasks } = useMemo(() => applyView(tasks, view), [tasks, view]);
@@ -604,6 +622,12 @@ function Deck({ cwd, tasksPath, tasksDir }) {
604
622
  setNote('');
605
623
  setMode('filter');
606
624
  }
625
+ else if (input === 't') {
626
+ const title = identity ? `🖥️ ${identity.owner} ${identity.name} [term]` : null;
627
+ const child = spawnTerminal(cwd, title);
628
+ child.on('error', () => setNote('⚠ t → ptyxis non lanciabile'));
629
+ setNote(`t → terminale su ${projectName}`);
630
+ }
607
631
  else if (input === 'w') {
608
632
  // Salvataggio ESPLICITO: comporre una vista non tocca il disco, così
609
633
  // sperimentare non sporca lo stato persistito.
@@ -634,7 +658,7 @@ function Deck({ cwd, tasksPath, tasksDir }) {
634
658
  const parentLabel = isSpot ? 'spot' : selectedTaskId ?? '—';
635
659
  const canSpawn = focus === 'tasks' && !isSpot;
636
660
  const launchHint = launch.length > 0 ? `1-${Math.min(9, launch.length)} launch · ` : '';
637
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "loom-deck" }), mode === 'create' ? (_jsxs(Text, { dimColor: true, children: ["nuova task \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " crea \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'sort' ? (_jsxs(Text, { dimColor: true, children: ["sort \u00B7 ", _jsx(Text, { color: "yellow", children: "p" }), " pri ", _jsx(Text, { color: "yellow", children: "s" }), " stato", ' ', _jsx(Text, { color: "yellow", children: "i" }), " id (asc\u2192desc\u2192off) \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'filter' ? (_jsxs(Text, { dimColor: true, children: ["filtri \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193\u2190\u2192" }), " naviga \u00B7 ", _jsx(Text, { color: "yellow", children: "spazio" }), ' ', "mostra/nascondi \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'edit' ? (_jsxs(Text, { dimColor: true, children: ["edit \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193" }), " campo \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2190\u2192" }), " valore \u00B7", ' ', _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva+commit \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : (_jsxs(Text, { dimColor: true, children: ["\u2191\u2193 naviga \u00B7 \u2190\u2192 pane \u00B7 \u23CE ", canSpawn ? 'spawn' : '—', " \u00B7 C nuova \u00B7 E edit \u00B7 S sort \u00B7 F filtri \u00B7 w salva \u00B7 ", launchHint, "q esci \u00B7 focus: ", _jsx(Text, { color: "cyan", children: focus })] })), mode === 'create' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "C \u203A " }), _jsx(Text, { children: draft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'sort' ? _jsx(SortModal, { sort: view.sort }) : null, mode === 'filter' ? _jsx(FilterModal, { view: view, cursor: filterCursor }) : null, mode === 'edit' && edit && selTask ? (_jsx(EditModal, { id: selTask.id, draft: edit, row: editRow })) : null, _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(TasksPane, { tasks: viewTasks, total: tasks.length, hidden: hiddenTasks, view: view, selected: selIndex, spotCount: spotCount, childCount: childCount, focused: focus === 'tasks', loadError: loadError, detail: detail }), _jsx(SessionsPane, { parentLabel: parentLabel, isSpot: isSpot, sessions: visibleSessions, total: childSessions.length, hidden: hiddenSessions, selectedId: selSessionId, focused: focus === 'sessions' })] }), note ? _jsx(Text, { color: "green", children: note }) : null] }));
661
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "loom-deck" }), mode === 'create' ? (_jsxs(Text, { dimColor: true, children: ["nuova task \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " crea \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'sort' ? (_jsxs(Text, { dimColor: true, children: ["sort \u00B7 ", _jsx(Text, { color: "yellow", children: "p" }), " pri ", _jsx(Text, { color: "yellow", children: "s" }), " stato", ' ', _jsx(Text, { color: "yellow", children: "i" }), " id (asc\u2192desc\u2192off) \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7", ' ', _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'filter' ? (_jsxs(Text, { dimColor: true, children: ["filtri \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193\u2190\u2192" }), " naviga \u00B7 ", _jsx(Text, { color: "yellow", children: "spazio" }), ' ', "mostra/nascondi \u00B7 ", _jsx(Text, { color: "yellow", children: "\u23CE" }), " ok \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : mode === 'edit' ? (_jsxs(Text, { dimColor: true, children: ["edit \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2191\u2193" }), " campo \u00B7 ", _jsx(Text, { color: "yellow", children: "\u2190\u2192" }), " valore \u00B7", ' ', _jsx(Text, { color: "yellow", children: "\u23CE" }), " salva+commit \u00B7 ", _jsx(Text, { color: "yellow", children: "esc" }), " annulla"] })) : (_jsxs(Text, { dimColor: true, children: ["\u2191\u2193 naviga \u00B7 \u2190\u2192 pane \u00B7 \u23CE ", canSpawn ? 'spawn' : '—', " \u00B7 C nuova \u00B7 E edit \u00B7 S sort \u00B7 F filtri \u00B7 w salva \u00B7 t term \u00B7 ", launchHint, "q esci \u00B7 focus: ", _jsx(Text, { color: "cyan", children: focus })] })), mode === 'create' ? (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", children: "C \u203A " }), _jsx(Text, { children: draft }), _jsx(Text, { inverse: true, children: " " })] })) : null, mode === 'sort' ? _jsx(SortModal, { sort: view.sort }) : null, mode === 'filter' ? _jsx(FilterModal, { view: view, cursor: filterCursor }) : null, mode === 'edit' && edit && selTask ? (_jsx(EditModal, { id: selTask.id, draft: edit, row: editRow })) : null, _jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(TasksPane, { tasks: viewTasks, total: tasks.length, hidden: hiddenTasks, view: view, selected: selIndex, spotCount: spotCount, childCount: childCount, focused: focus === 'tasks', loadError: loadError, detail: detail }), _jsx(SessionsPane, { parentLabel: parentLabel, isSpot: isSpot, sessions: visibleSessions, total: childSessions.length, hidden: hiddenSessions, selectedId: selSessionId, focused: focus === 'sessions' })] }), note ? _jsx(Text, { color: "green", children: note }) : null] }));
638
662
  }
639
663
  const SORT_UI = { pri: 'pri', prog: 'stato', id: 'id' };
640
664
  // Modali resi IN FLUSSO (come l'input box di create), non in overlay assoluto:
package/dist/config.js CHANGED
@@ -39,3 +39,20 @@ export function loadLaunch(projectRoot) {
39
39
  return [];
40
40
  }
41
41
  }
42
+ export function parseIdentity(raw) {
43
+ if (!raw || typeof raw !== 'object')
44
+ return null;
45
+ const { owner, name } = raw;
46
+ if (typeof owner !== 'string' || !owner || typeof name !== 'string' || !name)
47
+ return null;
48
+ return { owner, name };
49
+ }
50
+ /** File assente o malformato → nessuna identità. Mai un throw. */
51
+ export function loadIdentity(projectRoot) {
52
+ try {
53
+ return parseIdentity(JSON.parse(readFileSync(configFilePath(projectRoot), 'utf8')));
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamemind/loom-deck",
3
- "version": "0.7.0",
3
+ "version": "0.8.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": {