@iseoane/agent-depot 0.6.3 → 0.6.5

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.es.md +14 -10
  3. package/README.md +22 -8
  4. package/dist/src/app-cli.js +10 -5
  5. package/dist/src/app-flow.js +2 -2
  6. package/dist/src/atomic-file.js +9 -0
  7. package/dist/src/cli.js +98 -67
  8. package/dist/src/executable-resolution.js +9 -3
  9. package/dist/src/git-source.js +3 -5
  10. package/dist/src/package-cli.js +9 -4
  11. package/dist/src/package-flow.js +2 -1
  12. package/dist/src/package-host-state.js +44 -6
  13. package/dist/src/package-state.js +2 -5
  14. package/dist/src/process-runner.js +234 -16
  15. package/dist/src/profile-cli.js +8 -6
  16. package/dist/src/progress.js +157 -0
  17. package/dist/src/source-state.js +2 -5
  18. package/dist/src/tui/adoption-actions.js +4 -3
  19. package/dist/src/tui/app-actions.js +16 -9
  20. package/dist/src/tui/bulk-actions.js +7 -6
  21. package/dist/src/tui/catalog-flow.js +3 -2
  22. package/dist/src/tui/catalog-panel.js +2 -2
  23. package/dist/src/tui/catalog-view.js +26 -6
  24. package/dist/src/tui/file-picker.js +52 -20
  25. package/dist/src/tui/installations-view.js +44 -11
  26. package/dist/src/tui/manage-actions.js +7 -6
  27. package/dist/src/tui/manage-panel.js +2 -2
  28. package/dist/src/tui/package-actions.js +63 -31
  29. package/dist/src/tui/panel-parts.js +20 -0
  30. package/dist/src/tui/profile-view.js +84 -15
  31. package/dist/src/tui/sources-mode.js +2 -1
  32. package/dist/src/tui/sources-recipes.js +22 -10
  33. package/dist/src/tui/sources-view.js +13 -12
  34. package/dist/src/tui/text-wrap.js +13 -0
  35. package/dist/src/tui/unmanaged-actions.js +3 -2
  36. package/dist/src/tui/updates-flow.js +10 -7
  37. package/dist/src/tui/updates-keys.js +3 -2
  38. package/dist/src/tui/updates-model.js +2 -2
  39. package/dist/src/tui/updates-panel.js +3 -2
  40. package/dist/src/tui/updates-view.js +6 -3
  41. package/dist/src/tui/view-state.js +12 -0
  42. package/dist/src/tui/working-model.js +33 -0
  43. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,100 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.5] - 2026-10-29
11
+
12
+ ### Added
13
+
14
+ - The TUI shows a spinner, a specific label and, after one second, the elapsed
15
+ time (for example `⠹ Installing foo… 3s`) for every wait: loading views,
16
+ Source, Catalog, Package, App, Installation and Profile actions, update checks
17
+ and applies, and the background App status and export-name checks. Set
18
+ `AGENT_DEPOT_NO_ANIMATION=1` for a static `·` and no elapsed time instead of
19
+ the animated spinner.
20
+ - CLI commands now report progress on stderr during long steps instead of
21
+ printing nothing: `source refresh`/`remove`, `skill remove` and host
22
+ changes, `install`, `update`, `uninstall`, `self update`, `app`, `package
23
+ install|update|uninstall` and profile `export`/`import`. On an interactive
24
+ terminal a spinner with the step label and, after one second, the elapsed time
25
+ is shown and erased before results or errors print; when stderr is not a
26
+ terminal (CI, pipes, logs) each step prints one plain line such as
27
+ `Installing portable/demo…`. `AGENT_DEPOT_NO_ANIMATION=1` prints that static
28
+ line with a `·` on a terminal, and so do the steps that run `git` (`source
29
+ refresh` and confirmed installs from a Git Source), because git can ask for
30
+ credentials on the terminal and a spinner must never redraw over a prompt.
31
+ stdout is unchanged, so JSON output stays parseable.
32
+ - On an interactive terminal the CLI spinner now shows the last three lines of
33
+ output of the step's child processes (npm, pi, git, App and Package commands)
34
+ under it, cut to the terminal width and erased with the spinner when the step
35
+ ends; a failure still prints its captured output. Steps that may prompt stay on
36
+ one static line, and with no terminal or `AGENT_DEPOT_NO_ANIMATION=1` nothing
37
+ changes. The TUI is not affected.
38
+ - A global `--verbose` flag, accepted anywhere on the command line, streams the
39
+ full child output to stderr line by line as it runs, instead of the spinner and
40
+ tail; steps print one plain line each, so it is also safe next to a prompt.
41
+ stdout is unchanged. Terminal escape sequences in child output are stripped,
42
+ and a failing command's output still appears again in its error message.
43
+
44
+ ### Fixed
45
+
46
+ - A fresh Pi Package install no longer reports `local changes` and `verified=unknown`
47
+ when npm's generated dependency-free root `package-lock.json` records `bin`
48
+ metadata. The lock is accepted only when its `bin` equals npm's normalized form
49
+ of the `HEAD` manifest `bin`; any other lock content or local change still blocks.
50
+ - On Windows, Pi Package checkouts are no longer reported as "not the root of
51
+ its Git checkout", which hid their installed version and update evidence. The
52
+ checkout root is now compared by canonical path.
53
+ - On Windows, concurrent Agent Depot processes now wait for a busy Source,
54
+ Package or Git cache lock instead of failing with a raw `EPERM`/`EACCES`.
55
+ - On Windows, installing a Package whose host CLI is an npm global such as `pi`
56
+ no longer fails with `spawn ... ENOENT`. Executable lookup now only accepts
57
+ `PATHEXT` candidates, so npm's extensionless shell shim is skipped in favor of
58
+ `pi.cmd`, and `.cmd`/`.bat` files run through `cmd.exe` with escaped arguments.
59
+ - Importing a Profile no longer reports `Loading Profile…` while it installs and
60
+ uninstalls; it says `Applying import…`.
61
+ - On Windows, a timeout or output limit on any command now terminates its whole
62
+ process tree with `taskkill /T /F` instead of only the command itself. For an
63
+ npm `.cmd` shim such as `pi` that used to stop only `cmd.exe` and leave the host
64
+ CLI running with the output pipes open. The result is still reported as
65
+ terminated by `SIGTERM` and is returned within two seconds of the kill even if
66
+ a process survives.
67
+ - On Linux and macOS, a timeout or output limit on any command now terminates the
68
+ command and every process it had started (found with `ps`) instead of only the
69
+ command itself: `SIGTERM` first, then `SIGKILL` after two seconds. A wrapper that
70
+ forks, or a command that ignores `SIGTERM`, used to leave `agent-depot` waiting
71
+ forever for output pipes that never closed. The result is still reported as
72
+ terminated by `SIGTERM` and is returned within about four seconds of the kill even
73
+ if a process that had already left the command's process tree survives. The
74
+ command stays in Agent Depot's process group, so Ctrl-C still stops it and
75
+ `git`/`ssh` can still prompt on the terminal.
76
+ - The Sources list no longer flashes "No sources or App recipes" while recipes
77
+ load.
78
+ - The Package approval, lifecycle confirmation and forget panels no longer
79
+ overlap their text when they are taller than the terminal, as a long working
80
+ directory makes them. They now page like the Updates preview (arrows, PgUp/PgDn,
81
+ `j`/`k`), show `Review N of M` and the visible row window while more remains, and
82
+ take the panel's own view instead of sitting under the list. `y` and `n` answer
83
+ from any page, as before.
84
+ - The Profile export name stage and the file picker now stay inside short
85
+ terminals. In the name stage a long folder or destination keeps its label and
86
+ its end (the last folders and the file name) behind a `…`, and with room for
87
+ three rows also its first row and a count of the rows left out; the typed name
88
+ keeps its end so the cursor stays in view. The confirmation that follows pages
89
+ the whole destination. The picker trims its path and key hints to one line each
90
+ when the full ones would leave no room for the folder list, and its full-path
91
+ view (`p`) scrolls with `j`/`k` or PgUp/PgDn.
92
+ - The Profile export and import confirmation could not be scrolled past as many
93
+ rows as it has source lines, so the end of a long wrapped destination stayed out
94
+ of reach on a short terminal. Its paging now counts wrapped rows.
95
+
96
+ ## [0.6.4] - 2026-10-23
97
+
98
+ ### Fixed
99
+
100
+ - Portable Skills can now contain up to 32 MiB of files instead of 8 MiB,
101
+ allowing larger Skills such as Archify to install and update. The 2 MiB
102
+ per-file limit and all other safety checks remain unchanged.
103
+
10
104
  ## [0.6.3] - 2026-10-13
11
105
 
12
106
  ### Fixed
package/README.es.md CHANGED
@@ -533,8 +533,8 @@ confirmar):
533
533
  | Vista | Teclas |
534
534
  | --- | --- |
535
535
  | Sources | `j`/`k` recorren Sources de Git y recetas App. `Enter` abre el Catálogo de una Source de Git o previsualiza/aprueba una receta App. Aprobarla no instala la App. `n` añade una Source reutilizable o guía la creación de recetas; `r` refresca Sources o recarga el estado de recetas; `d` elimina. `space`/`a` solo marcan Sources de Git. La previsualización de refresco muestra todas las Sources seleccionadas; usa `j`/`k` o PgUp/PgDn para revisarlas antes de `y`; `n` cancela. |
536
- | Catalog | `Enter`/flechas expanden y contraen; `space` marca el Skill disponible resaltado; `a` marca o desmarca los Skills disponibles visibles de su Source; `i` inicia la instalación de los Skills marcados o del Skill, Package o App aprobada cuya ausencia se verificó y que está resaltada. Confirma la instalación de una App por separado con `y`. `/` filtra y `s` amplía la vista de una Source a todas. Las marcas de otras Sources se conservan y no se instalan Skills ocultos por el filtro. |
537
- | Installations | `Enter`/flechas expanden y contraen. En filas globales de usuario, `u` inicia la desinstalación de una App o Skill gestionada, o elimina un Skill no gestionado; los Skills de proyecto no se pueden eliminar. `h` o `i` añade Hosts a una instalación global de usuario, y `A` o `Enter` adopta un Skill no gestionado. `space` marca una hoja y `a` marca todas las hojas visibles; las acciones por lotes omiten instalaciones de proyecto con un motivo. En una fila de App, `u` previsualiza la desinstalación y `h` selecciona setup/teardown; las recetas modificadas requieren una nueva aprobación. En una fila de Package, `i` previsualiza el plan de instalación congelado y lo ejecuta, aprobando antes el recibo si falta; `Enter` solo aprueba la declaración de instalación; `U` actualiza, `u` desinstala y `f` olvida el registro de selección. Las filas de Package no se pueden marcar para acciones por lotes. |
536
+ | Catalog | `Enter`/flechas expanden y contraen; `space` marca el Skill disponible resaltado; `a` marca o desmarca los Skills disponibles visibles de su Source; `i` inicia la instalación de los Skills marcados o del Skill, Package o App aprobada cuya ausencia se verificó y que está resaltada. Un Package abre el mismo panel paginado que en Installations; confirma la instalación de una App por separado con `y`. `/` filtra y `s` amplía la vista de una Source a todas. Las marcas de otras Sources se conservan y no se instalan Skills ocultos por el filtro. |
537
+ | Installations | `Enter`/flechas expanden y contraen. En filas globales de usuario, `u` inicia la desinstalación de una App o Skill gestionada, o elimina un Skill no gestionado; los Skills de proyecto no se pueden eliminar. `h` o `i` añade Hosts a una instalación global de usuario, y `A` o `Enter` adopta un Skill no gestionado. `space` marca una hoja y `a` marca todas las hojas visibles; las acciones por lotes omiten instalaciones de proyecto con un motivo. En una fila de App, `u` previsualiza la desinstalación y `h` selecciona setup/teardown; las recetas modificadas requieren una nueva aprobación. En una fila de Package, `i` previsualiza el plan de instalación congelado y lo ejecuta, aprobando antes el recibo si falta; `Enter` solo aprueba la declaración de instalación; `U` actualiza, `u` desinstala y `f` olvida el registro de selección. Cada una abre su propio panel, que se pagina cuando es más alto que el terminal: las flechas o PageUp/PageDown avanzan una página visible, `j`/`k` una fila, y `Review N of M` con la ventana de filas indica dónde estás. `y` y `n` responden desde cualquier página. Las filas de Package no se pueden marcar para acciones por lotes. |
538
538
  | Updates | `space` marca una actualización, `a` marca todas las actualizaciones disponibles y `t`/`p`/`g` selecciona todo, el ámbito de proyecto o el global de usuario. `Enter` previsualiza las filas marcadas; `u` previsualiza el lote seleccionado; ninguna aplica actualizaciones. `r` vuelve a comprobar y las flechas izquierda/derecha pliegan o despliegan el grupo "cannot be checked". En una previsualización, las flechas o PageUp/PageDown avanzan una página visible y `j`/`k` una fila. Revisa todas las filas antes de `y`; `n` cancela. Las filas desconocidas son informativas. Packages y Apps solo se gestionan en el ámbito global de usuario; Packages requieren aprobación de acción antes de ejecutar una actualización. |
539
539
  | Profile | Pulsa `e` para exportar o `i` para importar. Consulta el [flujo de Profile](#flujo-de-profile) para ver la navegación, la selección, las previsualizaciones y las cancelaciones. |
540
540
 
@@ -561,12 +561,16 @@ la raíz del proyecto, salvo que la aplicación indique otro directorio inicial.
561
561
  3. Elige una carpeta de destino. Muévete con `j`/`k` o las flechas; pulsa
562
562
  `Enter` para abrir una carpeta o selecciona **Use this folder** para elegir
563
563
  la carpeta actual. Usa Izquierda o Retroceso para subir a la carpeta
564
- superior. Pulsa `p` para mostrar la ruta completa y otra vez `p` para volver
565
- al listado. `Esc` vuelve a la lista de exportación.
564
+ superior. Pulsa `p` para mostrar la ruta completa, que se desplaza con `j`/`k` o
565
+ `PageUp`/`PageDown` si es más alta que el terminal, y otra vez `p` para volver
566
+ al listado. En un terminal bajo, el selector acorta la ruta y las indicaciones
567
+ de teclas a una línea cada una para que la lista de carpetas siga visible.
568
+ `Esc` vuelve a la lista de exportación.
566
569
  4. Edita el nombre del archivo. El valor predeterminado es
567
570
  `agent-depot-profile.json`. `Ctrl+U` borra el nombre y se añade `.json` si
568
- falta una extensión. El editor muestra la ruta completa de destino. Pulsa
569
- `Enter` para revisarla. Si la ruta ya existe, incluido un enlace simbólico,
571
+ falta una extensión. El editor muestra la carpeta, el nombre y el destino; en un terminal bajo, una
572
+ carpeta o un destino largos conservan su final tras un `…` (la confirmación del
573
+ paso siguiente muestra el destino completo). Pulsa `Enter` para revisarla. Si la ruta ya existe, incluido un enlace simbólico,
570
574
  aparece un error en línea y puedes elegir otro nombre; la exportación nunca
571
575
  sobrescribe. `Esc` vuelve al selector de carpetas.
572
576
  5. Revisa la previsualización de exportación, incluidas las exclusiones y
@@ -582,8 +586,8 @@ la raíz del proyecto, salvo que la aplicación indique otro directorio inicial.
582
586
  los archivos y otra vez `f` para filtrar de nuevo por `.json`. Navega con
583
587
  `j`/`k` o las flechas; pulsa `Enter` para abrir una carpeta o seleccionar un
584
588
  archivo, e Izquierda o Retroceso para subir a la carpeta superior. Pulsa `p`
585
- para mostrar la ruta completa y otra vez `p` para volver. `Esc` vuelve a la
586
- vista Profile.
589
+ para mostrar la ruta completa (se desplaza con `j`/`k` o `PageUp`/`PageDown`
590
+ si es larga) y otra vez `p` para volver. `Esc` vuelve a la vista Profile.
587
591
  2. El archivo seleccionado se analiza y previsualiza. Si no se puede leer o
588
592
  no es válido, aparece un error en línea y el selector permanece abierto para
589
593
  elegir otro archivo. La lista de importación marca cada elemento como
@@ -602,7 +606,7 @@ Las recetas importadas siguen sin aprobarse. La importación no instala Apps ni
602
606
  Packages ni ejecuta comandos del Host. Las réplicas solo describen identidades
603
607
  en la máquina de origen; no transfieren aprobaciones ni estado de instalación.
604
608
 
605
- Las demás previsualizaciones desplazables de la TUI muestran todas las declaraciones y los términos de ejecución. Usa `j`/`k` para avanzar una fila o las flechas y `PageUp`/`PageDown` para avanzar una página visible. Confirma solo después de mostrar cada fila física. Si cambias el tamaño del terminal, revisa las filas que aún no se han mostrado.
609
+ Las demás previsualizaciones desplazables de la TUI muestran todas las declaraciones y los términos de ejecución. Usa `j`/`k` para avanzar una fila o las flechas y `PageUp`/`PageDown` para avanzar una página visible. Las previsualizaciones de Apps, recetas, actualización de Sources y Updates solo aceptan `y` después de mostrar cada fila física; si cambias el tamaño del terminal, revisa las filas que aún no se han mostrado. Los paneles de Packages y las confirmaciones de Perfil muestran la misma ventana de filas y aceptan `y`/`n` desde cualquier página.
606
610
 
607
611
  Al eliminar una receta App se comprueban su comando de versión aprobado y su
608
612
  seguimiento. Si la App está instalada o sigue registrada, la TUI ofrece el flujo
@@ -1105,7 +1109,7 @@ Packages. Las exclusiones y los conflictos no pueden seleccionarse.
1105
1109
  La exportación y la importación comparten un navegador de directorios que empieza en la
1106
1110
  raíz del proyecto, lista carpetas (y, al importar, archivos `.json`) y se mueve con
1107
1111
  `j`/`k` o las flechas. La exportación elige una carpeta y luego rellena
1108
- `agent-depot-profile.json` en un campo editable que muestra el destino completo;
1112
+ `agent-depot-profile.json` en un campo editable que muestra la carpeta, el nombre y el destino (en un terminal bajo una ruta larga conserva su final, y la confirmación pagina el destino completo);
1109
1113
  `Ctrl+U` lo borra, un nombre sin extensión recibe `.json`, y un archivo o enlace
1110
1114
  simbólico existente se rechaza en ese paso para volver a escribirlo. La importación
1111
1115
  navega hasta un archivo existente; `f` alterna entre archivos `.json` y todos los
package/README.md CHANGED
@@ -136,6 +136,15 @@ agent-depot tui
136
136
  agent-depot --version
137
137
  ```
138
138
 
139
+ While a command runs a long step, progress goes to stderr (stdout never changes). On
140
+ a terminal a spinner shows the step and, under it, the last three lines the step's
141
+ child processes (npm, pi, git, App commands) print; both are erased when the step
142
+ ends, and a failure still prints its captured output. Steps that may prompt (for
143
+ example `git` asking for credentials) stay on one static line. Add `--verbose`
144
+ anywhere on the command line to stream the full child output to stderr line by line
145
+ instead, with plain step lines and no spinner. Without a terminal, or with
146
+ `AGENT_DEPOT_NO_ANIMATION=1`, only the plain step line is printed.
147
+
139
148
  GitHub directory URLs such as `https://github.com/cursor/plugins/tree/main/pstack`
140
149
  are also supported: Agent Depot clones the repository, reads the URL's ref, and
141
150
  only discovers Skills inside that directory. Skill paths stay repository-relative.
@@ -483,8 +492,8 @@ Keys per view (prompts also accept `Esc` to cancel and `y`/`n` to confirm):
483
492
  | View | Keys |
484
493
  | --- | --- |
485
494
  | Sources | `j`/`k` move through Git Sources and App recipes. `Enter` opens the Catalog for a Git Source or previews/approves an App recipe. Approval does not install the App. `n` adds a Git Source or guides recipe authoring; `r` refreshes Sources or reloads recipe status; `d` removes. `space`/`a` mark Git Sources only. Refresh previews show every selected Source; review every physical row before `y`. `n` cancels. |
486
- | Catalog | `Enter`/arrows expand and collapse, `space` marks the highlighted loose Skill, `a` marks or clears the visible loose Skills in its Source, and `i` starts installation for marked Skills or the highlighted Skill, Package, or verified-absent approved App. Confirm an App install separately with `y`. `/` filters and `s` widens a Source view to all Sources. Marks in other Sources remain unchanged; Skills hidden by the filter are not installed. |
487
- | Installations | `Enter`/arrows expand and collapse. On user-global rows, `u` starts an App or managed Skill uninstall, or removes an unmanaged Skill; project Skills cannot be removed. `h` or `i` adds Hosts to a user-global Skill installation, and `A` or `Enter` adopts an unmanaged Skill. `space` marks a leaf and `a` marks all visible leaves; bulk actions skip project installations with a reason. On an App row, `u` previews uninstall and `h` selects setup/teardown; changed recipes must be approved again before commands run. On a Package row, `i` previews the frozen install plan and runs it, approving the receipt first when it is missing; `Enter` approves the install declaration only, `U` updates, `u` uninstalls, and `f` forgets the selection record. Package rows are not markable for bulk actions. |
495
+ | Catalog | `Enter`/arrows expand and collapse, `space` marks the highlighted loose Skill, `a` marks or clears the visible loose Skills in its Source, and `i` starts installation for marked Skills or the highlighted Skill, Package, or verified-absent approved App. A Package opens the same paged panel as in Installations; confirm an App install separately with `y`. `/` filters and `s` widens a Source view to all Sources. Marks in other Sources remain unchanged; Skills hidden by the filter are not installed. |
496
+ | Installations | `Enter`/arrows expand and collapse. On user-global rows, `u` starts an App or managed Skill uninstall, or removes an unmanaged Skill; project Skills cannot be removed. `h` or `i` adds Hosts to a user-global Skill installation, and `A` or `Enter` adopts an unmanaged Skill. `space` marks a leaf and `a` marks all visible leaves; bulk actions skip project installations with a reason. On an App row, `u` previews uninstall and `h` selects setup/teardown; changed recipes must be approved again before commands run. On a Package row, `i` previews the frozen install plan and runs it, approving the receipt first when it is missing; `Enter` approves the install declaration only, `U` updates, `u` uninstalls, and `f` forgets the selection record. Each of these opens its own panel, which pages when it is taller than the terminal: arrows or PageUp/PageDown move by a visible page, `j`/`k` by one row, and `Review N of M` with the row window shows where you are. `y` and `n` answer from any page. Package rows are not markable for bulk actions. |
488
497
  | Updates | `space` marks an update, `a` marks all available updates, and `t`/`p`/`g` selects all, project, or user-global scope. `Enter` previews the marked rows; `u` previews the selected batch; neither applies updates. `r` checks again, and left/right arrows fold or unfold the "cannot be checked" group. In a preview, arrows or PageUp/PageDown move by a visible page and `j`/`k` move one row. Review every row before `y`; `n` cancels. Unknown rows are informational. Packages and Apps are user-global only; Packages need action approval before update execution. |
489
498
  | Profile | Press `e` to export or `i` to import. See [Profile workflow](#profile-workflow) for navigation, selection, previews, and cancellation. |
490
499
 
@@ -508,11 +517,15 @@ the application supplies another start directory.
508
517
  3. Choose a destination folder. Move with `j`/`k` or the arrows, press `Enter`
509
518
  to open a folder, or select **Use this folder** to choose the current one.
510
519
  Use Left or Backspace to go to the parent. Press `p` to show the full
511
- directory path; press `p` again to return to the listing. `Esc` returns to
512
- the checklist.
520
+ directory path, which scrolls with `j`/`k` or `PageUp`/`PageDown` when it is
521
+ taller than the terminal; press `p` again to return to the listing. On a
522
+ short terminal the picker shortens the path and the key hints to one line
523
+ each so the folder list stays visible. `Esc` returns to the checklist.
513
524
  4. Edit the file name. The default is `agent-depot-profile.json`. `Ctrl+U`
514
525
  clears the name, and a name without an extension gets `.json`. The editor
515
- shows the complete destination path. Press `Enter` to review it. If the path
526
+ shows the folder, the name and the destination; on a short terminal a long
527
+ folder or destination keeps its end behind a `…` (the confirmation in the next
528
+ step shows the whole destination). Press `Enter` to review it. If the path
516
529
  already exists, including a symlink, the view reports an inline error and
517
530
  lets you choose another name; export never overwrites. `Esc` returns to the
518
531
  folder picker.
@@ -527,7 +540,8 @@ the application supplies another start directory.
527
540
  `.json` files and folders. Press `f` to show every file, `f` again to filter
528
541
  back to `.json`. Navigate with `j`/`k` or the arrows, use `Enter` to open a
529
542
  folder or select a file, and Left or Backspace to go to the parent. Press
530
- `p` to show the full directory path and `p` again to return. `Esc` returns
543
+ `p` to show the full directory path (it scrolls with `j`/`k` or
544
+ `PageUp`/`PageDown` when it is long) and `p` again to return. `Esc` returns
531
545
  to the Profile view.
532
546
  2. The selected file is parsed and previewed. If it is invalid or cannot be
533
547
  read, an inline error appears and the picker stays open so you can choose
@@ -545,7 +559,7 @@ Imported recipes remain unapproved. Import does not install Apps or Packages or
545
559
  run Host commands. Replica entries describe identity on the exporting machine
546
560
  only. They do not confer approval or installed state.
547
561
 
548
- Scrollable previews elsewhere in the TUI show every declaration and execution term. Use `j`/`k` to move one row, or the arrows and `PageUp`/`PageDown` to move by a visible page. Confirm only after every physical row has been displayed. Resizing a preview requires reviewing any rows that have not yet been displayed.
562
+ Scrollable previews elsewhere in the TUI show every declaration and execution term. Use `j`/`k` to move one row, or the arrows and `PageUp`/`PageDown` to move by a visible page. App, recipe, Sources refresh and Updates previews accept `y` only after every physical row has been displayed, and resizing one requires reviewing any rows not yet displayed. Package panels and Profile confirmations show the same row window and accept `y`/`n` from any page.
549
563
 
550
564
  Removing an App recipe checks its approved version command and tracked state.
551
565
  If the App is installed or tracked, the TUI offers the existing uninstall flow;
@@ -1006,7 +1020,7 @@ automatically installs Apps or Packages. Exclusions and conflicts cannot be sele
1006
1020
  Export and import share one directory browser that starts at the project root, lists
1007
1021
  folders (and, for import, `.json` files), and moves with `j`/`k` or the arrows. Export
1008
1022
  picks a folder, then pre-fills `agent-depot-profile.json` in an editable name field that
1009
- shows the complete destination; `Ctrl+U` clears it, a name without an extension gains
1023
+ shows the folder, the name and the destination (a long path keeps its end on a short terminal, and the confirmation pages the whole destination); `Ctrl+U` clears it, a name without an extension gains
1010
1024
  `.json`, and an existing file or symlink is refused at that step so you can retype.
1011
1025
  Import browses to an existing file; `f` switches between `.json` files and every file,
1012
1026
  and a malformed file reports inline while the browser stays open. Export creates a new
@@ -1,8 +1,12 @@
1
1
  import { PROJECT_HOSTS } from "./project-manifest.js";
2
2
  import { APP_RECIPE_SCHEMA } from "./app-schema.js";
3
3
  import { checkAppUpdates } from "./app-flow.js";
4
+ import { NO_PROGRESS } from "./progress.js";
4
5
  import { CliUsageError } from "./usage-error.js";
5
- export async function runAppCommand(values, apps, output) {
6
+ const ACTION_PROGRESS = {
7
+ install: "Installing", update: "Updating", uninstall: "Uninstalling", setup: "Setting up", teardown: "Tearing down",
8
+ };
9
+ export async function runAppCommand(values, apps, output, progress = NO_PROGRESS) {
6
10
  const [command, ...args] = values;
7
11
  const usage = "Usage: agent-depot app schema|list; app validate <file>...; app approve <name> [--yes]; app install|update|uninstall <name> [--yes] [--manual-done]; app setup|teardown <name> --host <host>... [--yes] [--manual-done]; app uninstall <name> --forget [--yes] (--manual-done requires a declared manual step)";
8
12
  if (command === "schema" && args.length === 0) {
@@ -10,12 +14,12 @@ export async function runAppCommand(values, apps, output) {
10
14
  return 0;
11
15
  }
12
16
  if (command === "list" && args.length === 0) {
13
- for (const update of await checkAppUpdates(apps))
17
+ for (const update of await progress.run("Checking App updates", () => checkAppUpdates(apps)))
14
18
  await show(update.entry, update);
15
19
  return 0;
16
20
  }
17
21
  if (command === "install" || command === "update" || command === "uninstall" || command === "setup" || command === "teardown") {
18
- return runLifecycle(command, args, apps, output, usage);
22
+ return runLifecycle(command, args, apps, output, usage, progress);
19
23
  }
20
24
  if (command !== "validate" && command !== "approve")
21
25
  throw new CliUsageError(usage);
@@ -76,7 +80,7 @@ export async function runAppCommand(values, apps, output) {
76
80
  output(columns.join("\t"));
77
81
  }
78
82
  }
79
- async function runLifecycle(action, args, apps, output, usage) {
83
+ async function runLifecycle(action, args, apps, output, usage, progress) {
80
84
  const hostAction = action === "setup" || action === "teardown";
81
85
  const hosts = [];
82
86
  const remaining = [];
@@ -145,7 +149,8 @@ async function runLifecycle(action, args, apps, output, usage) {
145
149
  output(`Manual (never executed): ${plan.manual}`);
146
150
  }
147
151
  async function applyPlan(plan) {
148
- const result = done ? await apps.completeManual(plan, true) : await apps.executeLifecycle(plan, true);
152
+ const label = `${done ? "Checking" : ACTION_PROGRESS[action]} App ${name}${plan.host ? ` (${plan.host})` : ""}`;
153
+ const result = await progress.run(label, () => done ? apps.completeManual(plan, true) : apps.executeLifecycle(plan, true));
149
154
  output(`${name}${plan.host ? ` (${plan.host})` : ""}: ${describeAppResult(result)}`);
150
155
  if (result.reason)
151
156
  output(result.reason);
@@ -387,12 +387,12 @@ export function createAppOperations(environment = {}) {
387
387
  let result;
388
388
  try {
389
389
  result = await runner(plan.executable, plan.argv.slice(1), {
390
- cwd: home, captureStdout: true, maxOutputBytes: APP_OUTPUT_LIMIT, maxStderrBytes: APP_OUTPUT_LIMIT,
390
+ cwd: home, captureStdout: true, streamStdout: true, maxOutputBytes: APP_OUTPUT_LIMIT, maxStderrBytes: APP_OUTPUT_LIMIT,
391
391
  });
392
392
  }
393
393
  catch (error) {
394
394
  const code = error?.code;
395
- if (!code || !["ENOENT", "EACCES", "ENOEXEC", "EPERM"].includes(code))
395
+ if (!code || !["ENOENT", "EACCES", "ENOEXEC", "EPERM", "EINVAL"].includes(code))
396
396
  throw error;
397
397
  const detail = error instanceof Error ? error.message : String(error);
398
398
  return manualFallback(plan, code === "ENOENT"
@@ -24,3 +24,12 @@ export async function writeFileAtomically(file, contents, options = {}) {
24
24
  });
25
25
  }
26
26
  }
27
+ /**
28
+ * Whether a failed `mkdir` of a lock directory means another process holds it. Windows reports
29
+ * EPERM or EACCES instead of EEXIST while the holder's `rmdir` is still completing, so a concurrent
30
+ * waiter must retry rather than fail.
31
+ */
32
+ export function isLockContention(error, platform = process.platform) {
33
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
34
+ return code === "EEXIST" || (platform === "win32" && (code === "EPERM" || code === "EACCES"));
35
+ }