opencode-wyvern 0.1.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.
@@ -0,0 +1,527 @@
1
+
2
+ # ---- OpenCode Wyvern (auto-generato) ----
3
+ # Personalizza i valori qui sotto e rilancia: . $PROFILE
4
+
5
+ $env:OC_SERVER = '__OC_SERVER__' # alias SSH (vedi ~/.ssh/config)
6
+ $env:OC_HOST = '__OC_HOST__' # IP o hostname del server
7
+ $env:OC_USER = '__OC_USER__' # utente SSH
8
+ $env:OC_DIR = '__OC_DIR__' # cartella remota di default (es. ~/Server)
9
+
10
+ $global:OC_OPENCODE = $null # cache path, si azzera a ogni reload
11
+
12
+ function Get-OcPath {
13
+ if (-not $global:OC_OPENCODE) {
14
+ $remotePath = "(command -v opencode || ls -t ~/.nvm/versions/node/*/bin/opencode 2>/dev/null | head -n1)"
15
+ $found = ssh -o BatchMode=yes $env:OC_SERVER $remotePath 2>$null | Select-Object -First 1
16
+ if ($found) { $global:OC_OPENCODE = $found.Trim() }
17
+ else { $global:OC_OPENCODE = 'opencode' }
18
+ }
19
+ return $global:OC_OPENCODE
20
+ }
21
+
22
+ function oc-connect {
23
+ $key = "$env:USERPROFILE\.ssh\id_ed25519"
24
+ $pub = "$env:USERPROFILE\.ssh\id_ed25519.pub"
25
+
26
+ if (-not (Test-Path $key)) {
27
+ Write-Host "Genero SSH key..." -ForegroundColor Cyan
28
+ ssh-keygen -t ed25519 -C "$env:OC_USER@client" -f $key -N "" | Out-Null
29
+ }
30
+
31
+ Write-Host "Installo chiave sul server (inserisci password una volta)..." -ForegroundColor Cyan
32
+ Get-Content $pub | ssh $env:OC_SERVER "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && echo CHIAVE_INSTALLATA"
33
+
34
+ Write-Host "Verifico autenticazione a chiave..." -ForegroundColor Cyan
35
+ $res = ssh -o BatchMode=yes $env:OC_SERVER "echo OK" 2>&1
36
+ if ($res -eq 'OK') {
37
+ Write-Host "Autenticazione SSH a chiave attiva!" -ForegroundColor Green
38
+ } else {
39
+ Write-Host "ERRORE: la chiave non è stata accettata. Riepilogo:" -ForegroundColor Red
40
+ Write-Host " $res" -ForegroundColor Red
41
+ }
42
+ }
43
+
44
+ function oc {
45
+ $oc = Get-OcPath
46
+ ssh -t $env:OC_SERVER "cd $env:OC_DIR && $oc"
47
+ }
48
+
49
+ function oc-ssh {
50
+ ssh $env:OC_SERVER
51
+ }
52
+
53
+ function Get-OcAllSessions {
54
+ $py = @'
55
+ import json,sys
56
+ raw=sys.stdin.read()
57
+ i=raw.find("[\n")
58
+ if i<0:
59
+ i=raw.find("[")
60
+ d=json.loads(raw[i:])
61
+ for s in d:
62
+ u=s.get("updated") or 0
63
+ title=(s.get("title") or "").replace("\t"," ").replace("\n"," ")
64
+ print(str(u)+"\t"+s.get("id","")+"\t"+title+"\t"+s.get("directory",""))
65
+ '@
66
+ $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($py))
67
+ $oc = Get-OcPath
68
+ $cmd = "cd ~ && $oc --pure session list --format json 2>/dev/null | python3 -c `"import base64;exec(base64.b64decode('$b64').decode())`""
69
+ $lines = @(ssh -n -o BatchMode=yes $env:OC_SERVER $cmd 2>$null)
70
+ if ($LASTEXITCODE -eq 0 -and $lines.Count -gt 0) { Save-OcCache $lines }
71
+ $lines | ForEach-Object {
72
+ $parts = $_ -split "`t", 4
73
+ [pscustomobject]@{
74
+ Updated = [long]$parts[0]
75
+ Id = $parts[1]
76
+ Title = $parts[2]
77
+ Dir = if ($parts.Count -gt 3) { $parts[3] } else { "~" }
78
+ }
79
+ } | Sort-Object -Property Updated -Descending
80
+ }
81
+
82
+ function Get-OcLastFile {
83
+ return Join-Path (Split-Path (Get-OcCacheFile)) "last.tsv"
84
+ }
85
+
86
+ function Save-OcLastSession {
87
+ param([Parameter(Mandatory)][string]$Id, [string]$Dir = "~")
88
+ $file = Get-OcLastFile
89
+ New-Item -ItemType Directory -Path (Split-Path $file) -Force | Out-Null
90
+ Set-Content -Path $file -Value ("{0}`t{1}" -f $Id, $Dir) -Encoding UTF8
91
+ }
92
+
93
+ function Get-OcLastSession {
94
+ $file = Get-OcLastFile
95
+ if (-not (Test-Path $file)) { return $null }
96
+ $line = Get-Content $file | Select-Object -First 1
97
+ if (-not $line) { return $null }
98
+ $parts = $line -split "`t", 2
99
+ if ($parts.Count -lt 1 -or -not $parts[0]) { return $null }
100
+ return [pscustomobject]@{
101
+ Id = $parts[0]
102
+ Dir = if ($parts.Count -gt 1) { $parts[1] } else { "~" }
103
+ }
104
+ }
105
+
106
+ function Get-OcServerStatus {
107
+ $err = ssh -o BatchMode=yes -o ConnectTimeout=5 $env:OC_SERVER "echo PING_OK" 2>&1
108
+ if ($LASTEXITCODE -eq 0 -and ($err -contains 'PING_OK')) {
109
+ return [pscustomobject]@{ Up = $true; Message = 'OK' }
110
+ }
111
+ $msg = ($err | Where-Object { $_ } | Select-Object -Last 1)
112
+ if (-not $msg) { $msg = "Il server non risponde (timeout)." }
113
+ return [pscustomobject]@{ Up = $false; Message = "$msg" }
114
+ }
115
+
116
+ function Get-OcCacheFile {
117
+ return Join-Path $env:USERPROFILE ".cache\opencode-wyvern\sessions.tsv"
118
+ }
119
+
120
+ function Save-OcCache {
121
+ param([Parameter(Mandatory)][string[]]$Lines)
122
+ $file = Get-OcCacheFile
123
+ New-Item -ItemType Directory -Path (Split-Path $file) -Force | Out-Null
124
+ Set-Content -Path $file -Value $Lines -Encoding UTF8
125
+ }
126
+
127
+ function Get-OcCachedSessions {
128
+ param([switch]$TodayOnly)
129
+ $file = Get-OcCacheFile
130
+ if (-not (Test-Path $file)) { return }
131
+ $rows = @(Get-Content $file | ForEach-Object {
132
+ $parts = $_ -split "`t", 4
133
+ if ($parts.Count -lt 3) { return }
134
+ $u = 0L
135
+ [long]::TryParse($parts[0], [ref]$u) | Out-Null
136
+ [pscustomobject]@{
137
+ Updated = $u
138
+ Id = $parts[1]
139
+ Title = $parts[2]
140
+ Dir = if ($parts.Count -gt 3) { $parts[3] } else { "~" }
141
+ }
142
+ })
143
+ $rows = @($rows | Where-Object { $_.Id } | Sort-Object Updated -Descending)
144
+ if ($TodayOnly) {
145
+ $cutoff = [DateTimeOffset]::UtcNow.AddHours(-24).ToUnixTimeMilliseconds()
146
+ $rows = @($rows | Where-Object { $_.Updated -ge $cutoff })
147
+ }
148
+ $rows
149
+ }
150
+
151
+ function Get-OcRecentSessions {
152
+ $cutoff = [DateTimeOffset]::UtcNow.AddHours(-24).ToUnixTimeMilliseconds()
153
+ @(Get-OcAllSessions) | Where-Object { $_.Updated -ge $cutoff }
154
+ }
155
+
156
+ function oc-sessions {
157
+ $sessions = Get-OcRecentSessions
158
+ if (-not $sessions) {
159
+ Write-Host "Nessuna sessione nelle ultime 24 ore (o SSH a chiave non configurato - esegui oc-connect)." -ForegroundColor Yellow
160
+ return
161
+ }
162
+ Write-Host "Sessioni nelle ultime 24 ore:" -ForegroundColor Cyan
163
+ $i = 0
164
+ foreach ($s in $sessions) {
165
+ $i++
166
+ Write-Host (" {0}. [{1}] {2}" -f $i, $s.Id.Substring(0, [Math]::Min(12, $s.Id.Length)), $s.Title)
167
+ }
168
+ }
169
+
170
+ function oc-help {
171
+ Write-Host ""
172
+ Write-Host "OpenCode Wyvern - comandi" -ForegroundColor Cyan
173
+ Write-Host "============================" -ForegroundColor Cyan
174
+ Write-Host ""
175
+ Write-Host (" {0,-24} {1}" -f "oc-connect", "Setup SSH key + autorizzazione server")
176
+ Write-Host (" {0,-24} {1}" -f "oc", "Nuova sessione opencode in $env:OC_DIR")
177
+ Write-Host (" {0,-24} {1}" -f "oc-ssh", "Apri sessione SSH interattiva")
178
+ Write-Host (" {0,-24} {1}" -f "oc-sessions", "Lista sessioni opencode (ultime 24h)")
179
+ Write-Host (" {0,-24} {1}" -f "oc-find [testo]", "Cerca sessioni globali per titolo e riapri")
180
+ Write-Host (" {0,-24} {1}" -f "oc-delete [testo]", "Cerca ed elimina sessioni")
181
+ Write-Host (" {0,-24} {1}" -f "oc-resume", "Apre 1 tab per ogni sessione (ultime 24h)")
182
+ Write-Host (" {0,-24} {1}" -f "oc-go -Id <id> [-Dir <path>]", "Riprende una sessione specifica")
183
+ Write-Host (" {0,-24} {1}" -f "oc-recap", "Riepilogo sessioni (usa cache se il server è giù)")
184
+ Write-Host (" {0,-24} {1}" -f "oc-help", "Questo aiuto")
185
+ Write-Host ""
186
+ Write-Host "Nota: oc-ssh, oc-sessions, oc-resume richiedono SSH key configurata" -ForegroundColor DarkGray
187
+ Write-Host ""
188
+ }
189
+
190
+ function oc-find {
191
+ param(
192
+ [string]$Search = '',
193
+ [int]$Limit = 30
194
+ )
195
+ $all = Get-OcAllSessions
196
+ if (-not $all) {
197
+ Write-Host "Nessuna sessione trovata (o SSH a chiave non configurato)." -ForegroundColor Yellow
198
+ return
199
+ }
200
+ if ($Search) {
201
+ $all = $all | Where-Object { $_.Title -match [regex]::Escape($Search) }
202
+ }
203
+ if ($Search -and $all.Count -gt 0) {
204
+ Write-Host ("Risultati per `"$Search`" (max {0}):" -f $Limit) -ForegroundColor Cyan
205
+ } elseif (-not $Search) {
206
+ Write-Host "Sessioni recenti (max $Limit):" -ForegroundColor Cyan
207
+ } else {
208
+ Write-Host "Nessuna sessione corrispondente." -ForegroundColor Yellow
209
+ return
210
+ }
211
+ $shown = @()
212
+ $i = 0
213
+ foreach ($s in $all | Select-Object -First $Limit) {
214
+ $i++
215
+ $date = [DateTimeOffset]::FromUnixTimeMilliseconds($s.Updated).DateTime.ToString('yyyy-MM-dd HH:mm')
216
+ $dir = $s.Dir -replace [regex]::Escape($env:USERPROFILE + '\'), '~/'
217
+ Write-Host (" {0,2}. [{1}] {2} ({3}) {4}" -f $i, $s.Id.Substring(0,12), $s.Title, $date, $dir)
218
+ $shown += $s
219
+ }
220
+ Write-Host ""
221
+ $choice = Read-Host "Numero da aprire (INVIO per annullare)"
222
+ if ($choice -match '^\d+$') {
223
+ $idx = [int]$choice - 1
224
+ if ($idx -ge 0 -and $idx -lt $shown.Count) {
225
+ $sel = $shown[$idx]
226
+ oc-go -Id $sel.Id -Dir $sel.Dir -NoRecap
227
+ } else {
228
+ Write-Host "Indice fuori range." -ForegroundColor Yellow
229
+ }
230
+ }
231
+ }
232
+
233
+ function oc-delete {
234
+ param(
235
+ [string]$Search = ''
236
+ )
237
+ $all = Get-OcAllSessions
238
+ if (-not $all) {
239
+ Write-Host "Nessuna sessione trovata." -ForegroundColor Yellow
240
+ return
241
+ }
242
+ if ($Search) {
243
+ $all = $all | Where-Object { $_.Title -match [regex]::Escape($Search) -or $_.Id -match [regex]::Escape($Search) }
244
+ }
245
+ if (-not $all) {
246
+ Write-Host ("Nessuna sessione corrispondente a `"$Search`".") -ForegroundColor Yellow
247
+ return
248
+ }
249
+ Write-Host "Sessioni (seleziona da eliminare):" -ForegroundColor Cyan
250
+ $i = 0
251
+ foreach ($s in $all | Select-Object -First 30) {
252
+ $i++
253
+ $date = [DateTimeOffset]::FromUnixTimeMilliseconds($s.Updated).DateTime.ToString('yyyy-MM-dd HH:mm')
254
+ Write-Host (" {0,2}. [{1}] {2} ({3})" -f $i, $s.Id.Substring(0,12), $s.Title, $date)
255
+ }
256
+ Write-Host ""
257
+ $choice = Read-Host "Numero da eliminare (INVIO per annullare)"
258
+ if ($choice -notmatch '^\d+$') { return }
259
+ $idx = [int]$choice - 1
260
+ if ($idx -lt 0 -or $idx -ge [Math]::Min($all.Count, 30)) {
261
+ Write-Host "Indice fuori range." -ForegroundColor Yellow
262
+ return
263
+ }
264
+ $sel = ($all | Select-Object -First 30)[$idx]
265
+ $confirm = Read-Host "Eliminare definitivamente la sessione '$($sel.Title)'? (s/N)"
266
+ if ($confirm -notmatch '^(s|si|sì|y|yes)$') {
267
+ Write-Host "Annullato." -ForegroundColor Yellow
268
+ return
269
+ }
270
+ $oc = Get-OcPath
271
+ $remoteCmd = "cd ~ && $oc --pure session delete $($sel.Id) </dev/null"
272
+ ssh -o BatchMode=yes $env:OC_SERVER $remoteCmd 2>&1 | Select-Object -Last 2 | Out-Host
273
+ Write-Host "Sessione eliminata: $($sel.Title)" -ForegroundColor Green
274
+ }
275
+
276
+ function oc-go {
277
+ param(
278
+ [Parameter(Mandatory)]
279
+ [string]$Id,
280
+ [string]$Dir = "~",
281
+ [switch]$NoRecap
282
+ )
283
+ $oc = Get-OcPath
284
+ if ($Dir -eq '~' -or [string]::IsNullOrEmpty($Dir)) {
285
+ $remoteCmd = "cd ~ && $oc -s $Id"
286
+ } else {
287
+ $remoteCmd = "mkdir -p '$Dir' && cd '$Dir' && $oc -s $Id"
288
+ }
289
+ Save-OcLastSession -Id $Id -Dir $Dir
290
+ ssh -t -o ConnectTimeout=8 $env:OC_SERVER $remoteCmd
291
+ $exitCode = $LASTEXITCODE
292
+ $errTime = Get-Date
293
+ if (-not $NoRecap) { Show-OcRecap -LastExit $exitCode -ErrTime $errTime }
294
+ }
295
+
296
+ function Show-OcRecap {
297
+ param(
298
+ [int]$LastExit = -1,
299
+ [datetime]$ErrTime = (Get-Date)
300
+ )
301
+ Write-Host ""
302
+ Write-Host ("{0}" -f ('=' * 56)) -ForegroundColor DarkCyan
303
+ Write-Host "Connessione terminata. Riepilogo per riprendere:" -ForegroundColor Cyan
304
+ Write-Host ("{0}" -f ('=' * 56)) -ForegroundColor DarkCyan
305
+ $retry = $true
306
+ while ($retry) {
307
+ $retry = $false
308
+ $sessions = @(Get-OcRecentSessions)
309
+ $fromCache = $false
310
+ if ($sessions.Count -eq 0) {
311
+ $status = Get-OcServerStatus
312
+ if ($status.Up) {
313
+ Write-Host " Server raggiungibile ma nessuna sessione nelle ultime 24 ore." -ForegroundColor Yellow
314
+ return
315
+ }
316
+ $cached = @(Get-OcCachedSessions -TodayOnly)
317
+ if ($cached.Count -eq 0) {
318
+ Write-Host (" [!] Connessione persa alle {0} (SSH exit {1})" -f $ErrTime.ToString('yyyy-MM-dd HH:mm:ss'), $LastExit) -ForegroundColor Red
319
+ Write-Host (" Errore: {0}" -f $status.Message) -ForegroundColor Yellow
320
+ Write-Host " Nessun riepilogo salvato: il client non si era mai collegato." -ForegroundColor Yellow
321
+ Write-Host " Quando il server torna su: oc-recap" -ForegroundColor DarkGray
322
+ return
323
+ }
324
+ $sessions = $cached
325
+ $fromCache = $true
326
+ }
327
+ if ($fromCache) {
328
+ $stamp = (Get-Item (Get-OcCacheFile)).LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss')
329
+ Write-Host (" [!] Connessione persa alle {0} (SSH exit {1})" -f $ErrTime.ToString('yyyy-MM-dd HH:mm:ss'), $LastExit) -ForegroundColor Red
330
+ Write-Host (" Errore: {0}" -f $status.Message) -ForegroundColor Yellow
331
+ Write-Host (" Riepilogo salvato localmente il {0}: quando la connessione torna, riprendi da qui." -f $stamp) -ForegroundColor DarkGray
332
+ Write-Host ""
333
+ }
334
+ $i = 0
335
+ foreach ($s in $sessions) {
336
+ $i++
337
+ $last = if ($s.Updated) { [DateTimeOffset]::FromUnixTimeMilliseconds($s.Updated).LocalDateTime.ToString('HH:mm') } else { '' }
338
+ Write-Host ""
339
+ Write-Host (" {0,2}) {1} [{2}...]" -f $i, $s.Title, $s.Id.Substring(0, [Math]::Min(12, $s.Id.Length))) -ForegroundColor White
340
+ Write-Host (" in: {0} (ultimo aggiornamento {1})" -f $s.Dir, $last) -ForegroundColor DarkGray
341
+ Write-Host (" run: opencode -s {0}" -f $s.Id) -ForegroundColor DarkGray
342
+ Write-Host (" oc-go: oc-go -Id '{0}' -Dir '{1}'" -f $s.Id, $s.Dir) -ForegroundColor DarkGray
343
+ }
344
+ Write-Host ""
345
+ $choice = Read-Host "Scelta (1..$i riprendi, r riprova, n nuova sessione, INVIO esci)"
346
+ if ($choice -match '^\d+$') {
347
+ $idx = [int]$choice - 1
348
+ if ($idx -ge 0 -and $idx -lt $i) {
349
+ $sel = $sessions[$idx]
350
+ Write-Host (" Riprendo: {0}" -f $sel.Title) -ForegroundColor Cyan
351
+ oc-go -Id $sel.Id -Dir $sel.Dir
352
+ } else {
353
+ Write-Host " Indice fuori range." -ForegroundColor Yellow
354
+ }
355
+ return
356
+ }
357
+ if ($choice -match '^r$') {
358
+ $retry = $true
359
+ $LastExit = -1
360
+ $ErrTime = Get-Date
361
+ $global:OC_OPENCODE = $null
362
+ Write-Host " Riprovo la connessione..." -ForegroundColor Cyan
363
+ continue
364
+ }
365
+ if ($choice -match '^n$') {
366
+ Write-Host " Nuova sessione..." -ForegroundColor Cyan
367
+ oc
368
+ return
369
+ }
370
+ Write-Host " Per riprendere in seguito: oc-recap oppure oc-resume" -ForegroundColor DarkGray
371
+ return
372
+ }
373
+ }
374
+
375
+ Set-Alias oc-recap Show-OcRecap
376
+
377
+ function Get-WarpConfigDir {
378
+ $candidates = @(
379
+ "$env:APPDATA\warp\Warp\data\tab_configs",
380
+ "$env:APPDATA\warp\WarpPreview\data\tab_configs",
381
+ "$env:LOCALAPPDATA\warp\Warp\data\tab_configs",
382
+ "$env:LOCALAPPDATA\warp\WarpPreview\data\tab_configs"
383
+ )
384
+ foreach ($d in $candidates) {
385
+ if (Test-Path $d) { return $d }
386
+ }
387
+ # crea la dir stabile se Warp è installato
388
+ if (Test-Path "$env:APPDATA\warp\Warp\data") {
389
+ New-Item -ItemType Directory -Path "$env:APPDATA\warp\Warp\data\tab_configs" -Force | Out-Null
390
+ return "$env:APPDATA\warp\Warp\data\tab_configs"
391
+ }
392
+ return $null
393
+ }
394
+
395
+ function Get-OcTerminal {
396
+ if ($env:TERM_PROGRAM -match 'warp') { return 'warp' }
397
+ $cur = $PID
398
+ $depth = 0
399
+ while ($cur -gt 0 -and $depth -lt 15) {
400
+ $pr = Get-CimInstance Win32_Process -Filter "ProcessId=$cur" -ErrorAction SilentlyContinue
401
+ if (-not $pr) { break }
402
+ $name = $pr.Name
403
+ if ($name -match '^Warp') { return 'warp' }
404
+ if ($name -match 'WindowsTerminal') { return 'wt' }
405
+ $cur = [int]$pr.ParentProcessId
406
+ $depth++
407
+ }
408
+ if ($env:WT_SESSION -and (Get-Command wt -ErrorAction SilentlyContinue)) { return 'wt' }
409
+ return $null
410
+ }
411
+
412
+ function Invoke-OcWarpResume {
413
+ param([Parameter(Mandatory)]$Sessions)
414
+ $dir = Get-WarpConfigDir
415
+ if (-not $dir) { return $false }
416
+ $oc = Get-OcPath
417
+ $created = @()
418
+ $i = 0
419
+ foreach ($s in $Sessions) {
420
+ $i++
421
+ $stem = "oc-resume-$i-$(Get-Random -Minimum 1000 -Maximum 9999)"
422
+ $file = Join-Path $dir "$stem.toml"
423
+ $title = ($s.Title -replace '[\\"]', ' ').Trim()
424
+ if ($s.Dir -eq '~' -or [string]::IsNullOrEmpty($s.Dir)) {
425
+ $remoteCmd = "cd ~ && $oc -s $($s.Id)"
426
+ } else {
427
+ $remoteCmd = "mkdir -p '$($s.Dir)' && cd '$($s.Dir)' && $oc -s $($s.Id)"
428
+ }
429
+ $cmd = ('ssh -t {0} "{1}"; oc-recap' -f $env:OC_SERVER, $remoteCmd).Replace('"', '\"')
430
+ $toml = @"
431
+ name = "[oc] $title"
432
+ title = "$title"
433
+ [[panes]]
434
+ id = "main"
435
+ type = "terminal"
436
+ shell = "pwsh"
437
+ commands = ["$cmd"]
438
+ "@
439
+ Set-Content -Path $file -Value $toml -Encoding UTF8
440
+ $created += @{ File = $file; Stem = $stem }
441
+ Write-Host " Apro tab Warp: $($s.Title)" -ForegroundColor Cyan
442
+ Start-Process "warp://tab_config/$stem"
443
+ Start-Sleep -Seconds 2
444
+ }
445
+ Write-Host ""
446
+ Write-Host "Se qualche tab non si e' aperta, riavvia lanciando: oc-resume -Warp" -ForegroundColor DarkGray
447
+ Write-Host "Per raggrupparle: Ctrl+click su tutte le tab, poi tasto destro e 'New group with tab'." -ForegroundColor DarkGray
448
+ Start-Job -ScriptBlock {
449
+ param($files)
450
+ Start-Sleep -Seconds 60
451
+ foreach ($f in $files) { Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue }
452
+ } -ArgumentList @($created.File) | Out-Null
453
+ return $true
454
+ }
455
+
456
+ function oc-resume {
457
+ param(
458
+ [switch]$Copy,
459
+ [switch]$Tabs,
460
+ [switch]$Warp,
461
+ [switch]$AllTabs
462
+ )
463
+ $sessions = @(Get-OcRecentSessions)
464
+
465
+ if ($sessions.Count -eq 0) {
466
+ $status = Get-OcServerStatus
467
+ if (-not $status.Up) {
468
+ Write-Host ""
469
+ Write-Host (" [!] Errore di connessione al server ({0})" -f $status.Message) -ForegroundColor Red
470
+ $last = Get-OcLastSession
471
+ if ($last) {
472
+ Write-Host " Riprendo automaticamente l'ultima sessione terminata..." -ForegroundColor Cyan
473
+ Write-Host ""
474
+ oc-go -Id $last.Id -Dir $last.Dir
475
+ } else {
476
+ Write-Host " Nessuna sessione da riprendere automaticamente." -ForegroundColor Yellow
477
+ Show-OcRecap
478
+ }
479
+ return
480
+ }
481
+ Write-Host "Nessuna sessione nelle ultime 24 ore (o SSH a chiave non configurato - esegui oc-connect)." -ForegroundColor Yellow
482
+ return
483
+ }
484
+
485
+ $first = $sessions[0]
486
+ $rest = @($sessions | Select-Object -Skip 1)
487
+
488
+ $term = Get-OcTerminal
489
+ $useWarp = $Warp -or ($term -eq 'warp' -and -not $Copy -and -not $Tabs)
490
+ $useTabs = $Tabs -or ($term -eq 'wt' -and -not $Copy -and -not $Warp)
491
+
492
+ if ($useWarp) {
493
+ $toOpen = if ($AllTabs) { $sessions } else { $rest }
494
+ if ($toOpen.Count -gt 0) {
495
+ if (-not (Invoke-OcWarpResume $toOpen) -and -not $AllTabs) {
496
+ Write-Host "Apertura tab Warp fallita, proseguo solo con quella corrente." -ForegroundColor Yellow
497
+ }
498
+ }
499
+ if (-not $AllTabs) {
500
+ Write-Host (" Riprendo qui: {0}" -f $first.Title) -ForegroundColor Cyan
501
+ oc-go -Id $first.Id -Dir $first.Dir
502
+ }
503
+ return
504
+ }
505
+
506
+ if ($useTabs) {
507
+ $toOpen = if ($AllTabs) { $sessions } else { $rest }
508
+ foreach ($s in $toOpen) {
509
+ Write-Host " Apro tab: $($s.Title)" -ForegroundColor Cyan
510
+ wt -w 0 new-tab --title "$($s.Title)" pwsh -Command "oc-go -Id '$($s.Id)' -Dir '$($s.Dir)'; if (\$LASTEXITCODE -ne 0 -or -not \$?) { Read-Host 'Premi INVIO per chiudere' }"
511
+ Start-Sleep -Milliseconds 800
512
+ }
513
+ if (-not $AllTabs) {
514
+ Write-Host (" Riprendo qui: {0}" -f $first.Title) -ForegroundColor Cyan
515
+ oc-go -Id $first.Id -Dir $first.Dir
516
+ }
517
+ return
518
+ }
519
+
520
+ Write-Host "Terminale corrente non Warp/Windows Terminal (o -Copy): copia e incolla i comandi" -ForegroundColor Yellow
521
+ Write-Host ""
522
+ foreach ($s in $sessions) {
523
+ Write-Host (" oc-go -Id '{0}' -Dir '{1}'" -f $s.Id, $s.Dir) -ForegroundColor Cyan
524
+ }
525
+ Write-Host ""
526
+ Write-Host "Suggerimenti: -Warp apre i tab in Warp, -Tabs in Windows Terminal, -AllTabs apre tutte le sessioni in tab nuove." -ForegroundColor DarkGray
527
+ }
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Baseline di interfaccia/struttura per una nuova feature o schermata
3
+ ---
4
+
5
+ # Baseline UI
6
+
7
+ Prima di scrivere codice per un'interfaccia nuova o modificata, produci una baseline concisa che guidi l'implementazione. Non leggere file se non strettamente necessario: rispondi solo con la baseline.
8
+
9
+ ## Struttura richiesta
10
+
11
+ 1. **Obiettivo** - in una frase, cosa fa la UI.
12
+ 2. **Componenti** - elenco dei componenti/schermate coinvolte (albero ASCII opzionale).
13
+ 3. **Stati** - loading, empty, error, success; quali gestire e come (spinner, messaggi, retry).
14
+ 4. **Accessibilità** - focus visibile, contrasto, alternative testuali, scorciatoie da tastiera.
15
+ 5. **Dati/API** - chiamate necessarie, dove finiscono i dati, comportamento offline/fallback.
16
+ 6. **Vincoli di sicurezza** - non incollare mai chiavi, token o segreti; usa variabili d'ambiente.
17
+ 7. **Checklist di primo passo** - 3-6 voci concrete da seguire.
18
+
19
+ ## Vincoli
20
+
21
+ - Non scrivere codice: solo la baseline in Markdown.
22
+ - Massimo ~80 righe; se serve più spazio, usa elenchi puntati, non testo piatto.
23
+ - Se mancano informazioni (framework, linguaggio, stile), dichiaralo e proponi un default ragionevole invece di assumere.