@detergent-software/atk 9.2.0-dev.1 → 9.2.0-dev.10

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 (136) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +32 -15
  3. package/build/command-manifest.json +81 -5
  4. package/build/commands/browse.d.ts.map +1 -1
  5. package/build/commands/browse.js +119 -16
  6. package/build/commands/browse.js.map +1 -1
  7. package/build/commands/diff.d.ts +4 -2
  8. package/build/commands/diff.d.ts.map +1 -1
  9. package/build/commands/diff.js +86 -13
  10. package/build/commands/diff.js.map +1 -1
  11. package/build/commands/drift.d.ts +2 -1
  12. package/build/commands/drift.d.ts.map +1 -1
  13. package/build/commands/drift.js +8 -3
  14. package/build/commands/drift.js.map +1 -1
  15. package/build/commands/freeze.d.ts +2 -2
  16. package/build/commands/freeze.d.ts.map +1 -1
  17. package/build/commands/freeze.js +45 -22
  18. package/build/commands/freeze.js.map +1 -1
  19. package/build/commands/info.d.ts.map +1 -1
  20. package/build/commands/info.js +18 -2
  21. package/build/commands/info.js.map +1 -1
  22. package/build/commands/install.d.ts +2 -2
  23. package/build/commands/install.d.ts.map +1 -1
  24. package/build/commands/install.js +285 -58
  25. package/build/commands/install.js.map +1 -1
  26. package/build/commands/outdated.d.ts +2 -1
  27. package/build/commands/outdated.d.ts.map +1 -1
  28. package/build/commands/outdated.js +11 -1
  29. package/build/commands/outdated.js.map +1 -1
  30. package/build/commands/pin.d.ts +2 -2
  31. package/build/commands/pin.d.ts.map +1 -1
  32. package/build/commands/pin.js +77 -46
  33. package/build/commands/pin.js.map +1 -1
  34. package/build/commands/schema.d.ts +1 -1
  35. package/build/commands/sync.d.ts +14 -2
  36. package/build/commands/sync.d.ts.map +1 -1
  37. package/build/commands/sync.js +86 -17
  38. package/build/commands/sync.js.map +1 -1
  39. package/build/commands/thaw.d.ts +2 -2
  40. package/build/commands/thaw.d.ts.map +1 -1
  41. package/build/commands/thaw.js +44 -21
  42. package/build/commands/thaw.js.map +1 -1
  43. package/build/commands/uninstall.d.ts +2 -2
  44. package/build/commands/uninstall.d.ts.map +1 -1
  45. package/build/commands/uninstall.js +35 -11
  46. package/build/commands/uninstall.js.map +1 -1
  47. package/build/commands/unpin.d.ts +2 -2
  48. package/build/commands/unpin.d.ts.map +1 -1
  49. package/build/commands/unpin.js +54 -25
  50. package/build/commands/unpin.js.map +1 -1
  51. package/build/commands/update.d.ts +3 -2
  52. package/build/commands/update.d.ts.map +1 -1
  53. package/build/commands/update.js +270 -74
  54. package/build/commands/update.js.map +1 -1
  55. package/build/components/BrowseList.d.ts +3 -1
  56. package/build/components/BrowseList.d.ts.map +1 -1
  57. package/build/components/BrowseList.js +12 -6
  58. package/build/components/BrowseList.js.map +1 -1
  59. package/build/components/BundleDetail.d.ts +3 -1
  60. package/build/components/BundleDetail.d.ts.map +1 -1
  61. package/build/components/BundleDetail.js +4 -2
  62. package/build/components/BundleDetail.js.map +1 -1
  63. package/build/components/DiffView.d.ts +34 -5
  64. package/build/components/DiffView.d.ts.map +1 -1
  65. package/build/components/DiffView.js +47 -8
  66. package/build/components/DiffView.js.map +1 -1
  67. package/build/components/Field.js +1 -1
  68. package/build/components/Field.js.map +1 -1
  69. package/build/components/HelpBar.d.ts +5 -1
  70. package/build/components/HelpBar.d.ts.map +1 -1
  71. package/build/components/HelpBar.js +18 -4
  72. package/build/components/HelpBar.js.map +1 -1
  73. package/build/components/InstallConfirmBar.d.ts +4 -2
  74. package/build/components/InstallConfirmBar.d.ts.map +1 -1
  75. package/build/components/InstallConfirmBar.js +15 -2
  76. package/build/components/InstallConfirmBar.js.map +1 -1
  77. package/build/components/index.d.ts +1 -1
  78. package/build/components/index.d.ts.map +1 -1
  79. package/build/components/index.js +1 -1
  80. package/build/components/index.js.map +1 -1
  81. package/build/hooks/useBrowseState.d.ts +31 -1
  82. package/build/hooks/useBrowseState.d.ts.map +1 -1
  83. package/build/hooks/useBrowseState.js +99 -15
  84. package/build/hooks/useBrowseState.js.map +1 -1
  85. package/build/hooks/useUninstallState.d.ts +22 -2
  86. package/build/hooks/useUninstallState.d.ts.map +1 -1
  87. package/build/hooks/useUninstallState.js +161 -16
  88. package/build/hooks/useUninstallState.js.map +1 -1
  89. package/build/lib/atk-api/generated/types.gen.d.ts +8 -8
  90. package/build/lib/atk-schemas/claude-plugin.d.ts +56 -0
  91. package/build/lib/atk-schemas/claude-plugin.d.ts.map +1 -0
  92. package/build/lib/atk-schemas/claude-plugin.js +199 -0
  93. package/build/lib/atk-schemas/claude-plugin.js.map +1 -0
  94. package/build/lib/atk-schemas/index.d.ts +3 -0
  95. package/build/lib/atk-schemas/index.d.ts.map +1 -1
  96. package/build/lib/atk-schemas/index.js +3 -0
  97. package/build/lib/atk-schemas/index.js.map +1 -1
  98. package/build/lib/config.d.ts +18 -0
  99. package/build/lib/config.d.ts.map +1 -1
  100. package/build/lib/config.js +32 -9
  101. package/build/lib/config.js.map +1 -1
  102. package/build/lib/diff-command.d.ts +43 -3
  103. package/build/lib/diff-command.d.ts.map +1 -1
  104. package/build/lib/diff-command.js +93 -15
  105. package/build/lib/diff-command.js.map +1 -1
  106. package/build/lib/format.d.ts +5 -0
  107. package/build/lib/format.d.ts.map +1 -1
  108. package/build/lib/format.js +7 -0
  109. package/build/lib/format.js.map +1 -1
  110. package/build/lib/install-targets.d.ts +90 -0
  111. package/build/lib/install-targets.d.ts.map +1 -0
  112. package/build/lib/install-targets.js +146 -0
  113. package/build/lib/install-targets.js.map +1 -0
  114. package/build/lib/installer.js +3 -2
  115. package/build/lib/installer.js.map +1 -1
  116. package/build/lib/named-entry.d.ts +24 -0
  117. package/build/lib/named-entry.d.ts.map +1 -0
  118. package/build/lib/named-entry.js +33 -0
  119. package/build/lib/named-entry.js.map +1 -0
  120. package/build/lib/resolver.d.ts +35 -0
  121. package/build/lib/resolver.d.ts.map +1 -1
  122. package/build/lib/resolver.js +65 -21
  123. package/build/lib/resolver.js.map +1 -1
  124. package/build/lib/sync.d.ts +9 -0
  125. package/build/lib/sync.d.ts.map +1 -1
  126. package/build/lib/sync.js +13 -8
  127. package/build/lib/sync.js.map +1 -1
  128. package/build/lib/uninstaller.d.ts +51 -3
  129. package/build/lib/uninstaller.d.ts.map +1 -1
  130. package/build/lib/uninstaller.js +134 -39
  131. package/build/lib/uninstaller.js.map +1 -1
  132. package/build/lib/updater.d.ts +6 -7
  133. package/build/lib/updater.d.ts.map +1 -1
  134. package/build/lib/updater.js +23 -33
  135. package/build/lib/updater.js.map +1 -1
  136. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,115 @@
1
+ ## [9.2.0-dev.10](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.9&targetVersion=GTv9.2.0-dev.10) (2026-10-24)
2
+
3
+ ### Features
4
+
5
+ * **cli:** compare two published versions with atk diff name@a..b ([25e386e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/25e386e6c358f09e15ba25bd6d98339580af8535))
6
+
7
+ ### Bug Fixes
8
+
9
+ * **cli:** refuse atk diff name@1...2 as a malformed range ([417ab3e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/417ab3ea46b76f944df3e4f1eb453512ee2e5604))
10
+
11
+ ## [9.2.0-dev.9](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.8&targetVersion=GTv9.2.0-dev.9) (2026-10-24)
12
+
13
+ ### Features
14
+
15
+ * **cli:** pin, unpin, freeze and thaw take several names (ATK-762) ([4f89199](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4f89199939bf4c0e780fb84d77ee6ad348e10f38))
16
+
17
+ ### Bug Fixes
18
+
19
+ * **cli:** put type before outcome in freeze and thaw's assets (ATK-762) ([7824ad5](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7824ad5b5f0d138f8fffc4982268d06a575d0fdc))
20
+
21
+ ### Documentation
22
+
23
+ * **cli:** list several names for pin and unpin in the README (ATK-762) ([f06ac5c](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f06ac5c6f1988564a4215fcc6d740a97db86a91d))
24
+
25
+ ## [9.2.0-dev.8](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.7&targetVersion=GTv9.2.0-dev.8) (2026-10-24)
26
+
27
+ ### Features
28
+
29
+ * **cli:** sync only the named assets with atk sync <names> ([4e076f6](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4e076f6d881734e064ddb3ee3e2dbc98619307d7))
30
+
31
+ ### Bug Fixes
32
+
33
+ * **cli:** review fixes for atk sync <names> ([4a9b69b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4a9b69b1b7e0167a887ddc6f509a43bb6a0066cb))
34
+ * **cli:** tighten atk sync <names> after code review ([2a1005b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/2a1005b8c6542af77c7e90defb450efd56f54691))
35
+
36
+ ## [9.2.0-dev.7](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.6&targetVersion=GTv9.2.0-dev.7) (2026-10-24)
37
+
38
+ ### Features
39
+
40
+ * **cli:** mark rows in atk browse and install them as one plan ([1b871cb](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/1b871cb9cd368de426f8d3afaa518c67109ea6c8))
41
+
42
+ ### Bug Fixes
43
+
44
+ * **cli:** give browse's list the rows a wrapped bar takes ([0d8f1b2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/0d8f1b2bb1854ee531408327e7f3bcd1648f9889))
45
+ * **cli:** review fixes for marking rows in atk browse ([e42131d](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/e42131d10a0130507a7eefe122f03277a33b865a))
46
+
47
+ ### Refactoring
48
+
49
+ * **cli:** share the several-targets install path in lib ([c5866c8](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/c5866c8667e5aa5c7ad258f12735003c519e3e1e))
50
+
51
+ ## [9.2.0-dev.6](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.5&targetVersion=GTv9.2.0-dev.6) (2026-10-23)
52
+
53
+ ### Features
54
+
55
+ * **cli:** install bundles and [@org](https://emergentcode.visualstudio.com/org) among several names as one plan ([47425f3](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/47425f35d4986306dc024fca8ad6d767d064188f))
56
+ * **cli:** uninstall a bundle among several names in one pass ([8c75075](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8c75075465bd210989baba237955a68dcce2ad73))
57
+
58
+ ### Bug Fixes
59
+
60
+ * **cli:** review fixes for bundles and [@org](https://emergentcode.visualstudio.com/org) among install names ([fc66d79](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/fc66d7994d704cf1ceebb6f2b551737e3233f671))
61
+
62
+ ### Documentation
63
+
64
+ * **cli:** describe bundles and [@org](https://emergentcode.visualstudio.com/org) among several install names ([febfb7e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/febfb7e6a81d6308c2df788fddfc2f1cb02e54f7))
65
+
66
+ ## [9.2.0-dev.5](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.4&targetVersion=GTv9.2.0-dev.5) (2026-10-23)
67
+
68
+ ### Features
69
+
70
+ * **cli:** install several assets at once as one plan ([9f45771](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/9f45771cd13f2e9f62352c6ee7050afa3f3c2bb2))
71
+ * **cli:** uninstall several assets at once ([eeaf8e2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/eeaf8e26db6619593264ae33183086aec94a9d3e))
72
+ * **cli:** update several assets at once and pick them with atk update -i ([ad76872](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/ad768721353fe5f6a466aa33807dcbdedd488f29))
73
+
74
+ ### Bug Fixes
75
+
76
+ * **cli:** agree the verb in the bundle refusal of several names ([7bb43aa](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7bb43aaf6c0f6295e5b8253106ea76a68d0d45c0))
77
+ * **cli:** give atk update --interactive the short flag -I ([7ab8d77](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7ab8d777eabd3a5e90e4dc5d13fd7c2054c64457))
78
+ * **cli:** review fixes for install, uninstall and update with several names ([4bdfa9c](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/4bdfa9c3560460af7b91e480f24a6ee9c28d57dc))
79
+
80
+ ## [9.2.0-dev.4](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.3&targetVersion=GTv9.2.0-dev.4) (2026-10-23)
81
+
82
+ ### Features
83
+
84
+ * **cli:** compare atk diff with a chosen version or the locked one ([ef0e182](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/ef0e182764ae57223037c695dfeda286f66d95e7))
85
+
86
+ ### Bug Fixes
87
+
88
+ * **cli:** keep atk diff's latest wording and outdated colour ([95894f7](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/95894f7cddd0f1dbfab06fea1494055147609035))
89
+ * **cli:** say when atk diff --installed finds its version unpublished ([7aed9ae](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7aed9ae6d42afc32dedc12bddcf55952c1801a1c))
90
+
91
+ ## [9.2.0-dev.3](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.2&targetVersion=GTv9.2.0-dev.3) (2026-10-23)
92
+
93
+ ### Features
94
+
95
+ * **cli:** atk info says when a bundle is a claude.ai plugin (ATK-749) ([c8e1cba](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/c8e1cbada0e0aa08ab53cd6a782b1b1a1b1ad618))
96
+
97
+ ### Bug Fixes
98
+
99
+ * **cli:** keep a field's label width when its value wraps (ATK-749) ([acfe3ff](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/acfe3ff6def50721448389ef0887e93c70e380e2))
100
+ * **cli:** match atk info's claude.ai plugin flag to the generator (ATK-749) ([8400a95](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8400a95473f69a31f00e4f7cc80a16ccc684be3a))
101
+
102
+ ## [9.2.0-dev.2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.2.0-dev.1&targetVersion=GTv9.2.0-dev.2) (2026-10-23)
103
+
104
+ ### Features
105
+
106
+ * **cli:** add sync --check and --exit-code on outdated, diff and drift (ATK-610) ([1e9090e](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/1e9090e2802c661c0c35242c25f817fe0eb809b7))
107
+
108
+ ### Documentation
109
+
110
+ * **cli:** note that sync --check reads a stale registry too (ATK-610) ([97fc9d4](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/97fc9d4ee91c9c2a75168de4492b2d38ff6c1a7b))
111
+ * **cli:** say which assets fail sync --check and that CI needs a token (ATK-610) ([c0508a2](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/c0508a2890433665b8b8dabc023c2090bb23a028))
112
+
1
113
  ## [9.2.0-dev.1](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTv9.1.0&targetVersion=GTv9.2.0-dev.1) (2026-10-23)
2
114
 
3
115
  ### Features
package/README.md CHANGED
@@ -77,7 +77,7 @@ atk update
77
77
  | `atk checkout <name>` | Fetch a writable working copy of a registry asset for updating |
78
78
  | `atk config` | Manage user configuration (`~/.atkrc.json`) |
79
79
  | `atk deprecate <name>` | Deprecate an asset or bundle (or `--undo`) via PR, no new version |
80
- | `atk diff <asset>` | Show changes between installed and latest versions |
80
+ | `atk diff <asset>` | Show changes against the latest, a chosen version or two versions |
81
81
  | `atk doctor` | Run environment diagnostics |
82
82
  | `atk info <asset>` | Show asset details, what depends on it and the bundles with it |
83
83
  | `atk init` | Scaffold a new asset or bundle |
@@ -87,16 +87,16 @@ atk update
87
87
  | `atk logout` | Sign out of your Emergent account |
88
88
  | `atk move <asset>` | Move an installed asset between the project and the user scope |
89
89
  | `atk outdated` | Show assets with available updates |
90
- | `atk pin <asset>` | Pin an asset to its current version |
90
+ | `atk pin <assets...>` | Pin assets at their installed versions |
91
91
  | `atk promote <name>` | Promote an org-scoped asset or bundle to global via PR |
92
92
  | `atk prune` | Remove orphaned dependencies |
93
93
  | `atk publish` | Publish one or more assets, or a bundle, to the registry |
94
94
  | `atk register <dir>` | Drop a manifest.json into an existing local directory |
95
95
  | `atk release-notes` | Show CLI release notes, recent history or a specific version |
96
96
  | `atk search [query]` | Search by keyword (every word must match), type, tag or tool |
97
- | `atk sync` | Re-apply all installed assets from the lockfile |
97
+ | `atk sync [names...]` | Re-apply the lockfile's assets, all of them or only those named |
98
98
  | `atk uninstall <asset>` | Remove an asset and clean up placed files |
99
- | `atk unpin <asset>` | Remove a version pin |
99
+ | `atk unpin <assets...>` | Remove the version pins from assets |
100
100
  | `atk unpublish <name>` | Remove an asset, bundle or one version from the registry via PR |
101
101
  | `atk update [asset]` | Update installed assets to latest versions |
102
102
  | `atk validate [path...]` | Check asset or bundle directories for errors before publishing |
@@ -109,29 +109,46 @@ A short flag means the same long flag on every command that takes it, and a long
109
109
 
110
110
  | Short | Long | Short | Long |
111
111
  | ----- | ------------------------------------ | ----- | ----------------------------------------------------- |
112
- | `-a` | `--all` | `-O` | `--offline` (`init`) |
113
- | `-b` | `--bundle` | `-o` | `--org` |
114
- | `-C` | `--context` | `-P` | `--project-root` |
115
- | `-d` | `--description` (`init`, `register`) | `-p` | `--pin` |
116
- | `-e` | `--entrypoint` | `-R` | `--reset` |
117
- | `-F` | `--from` | `-r` | `--refresh` |
118
- | `-f` | `--force` | `-s` | `--sort` |
119
- | `-g` | `--tag` | `-T` | `--tool` |
120
- | `-h` | `--help` | `-t` | `--type` |
112
+ | `-a` | `--all` | `-n` | `--dry-run` |
113
+ | `-b` | `--bundle` | `-O` | `--offline` (`init`) |
114
+ | `-C` | `--context` | `-o` | `--org` |
115
+ | `-d` | `--description` (`init`, `register`) | `-P` | `--project-root` |
116
+ | `-e` | `--entrypoint` | `-p` | `--pin` |
117
+ | `-F` | `--from` | `-R` | `--reset` |
118
+ | `-f` | `--force` | `-r` | `--refresh` |
119
+ | `-g` | `--tag` | `-s` | `--sort` |
120
+ | `-h` | `--help` | `-T` | `--tool` |
121
+ | `-I` | `--interactive` (`update`) | `-t` | `--type` |
121
122
  | `-i` | `--from-installed` | `-u` | `--update` |
122
123
  | `-j` | `--json` | `-V` | `--version`, an asset's (`checkout`, `drift`, `info`) |
123
124
  | `-l` | `--limit` | `-v` | `--version`, the CLI's |
124
125
  | `-m` | `--message` | `-y` | `--yes` |
125
- | `-n` | `--dry-run` | | |
126
126
 
127
127
  CLI 9.0.0 changed the letters that meant two things. `-n` became `--dry-run` on `publish`, `unpublish`, `promote` and `deprecate`. Their old `-d` is now an unknown option on all four, so an old dry run fails rather than running for real. `--name` (`init`, `register`, `drift`), `--author` (`init`, `register`), `deprecate --reason`, `info --full`, `info --verbose` and `release-notes --prerelease` lost their short flags. `move` gained `-n` and `setup` gained `-j`.
128
128
 
129
129
  ### Offline and a stale cache
130
130
 
131
- The CLI keeps the last registry it read in `~/.atk/cache/registry.json` and re-reads it from the ATK API once it is older than `cacheTtlMinutes`. When the API can't be read (no network, a 5xx, a rate limit, or a failed sign-in), the read commands (`search`, `info`, `list`, `outdated`, `diff`, `browse`) use the cached copy, whatever its age, and say how old it is (in `--json` output, a `stale-registry` entry in `notices`). Commands that install, update or publish never use an old copy.
131
+ The CLI keeps the last registry it read in `~/.atk/cache/registry.json` and re-reads it from the ATK API once it is older than `cacheTtlMinutes`. When the API can't be read (no network, a 5xx, a rate limit, or a failed sign-in), the read commands (`search`, `info`, `list`, `outdated`, `diff`, `browse`, `sync --check`) use the cached copy, whatever its age, and say how old it is (in `--json` output, a `stale-registry` entry in `notices`). Commands that install, update or publish never use an old copy.
132
132
 
133
133
  `--offline`, before or after any command, never calls the API. The read commands take the registry from the cache of any age; anything else the API serves (a README, asset files) and every command that needs the current registry (`install`, `update`, `publish`, …) fails at once instead of retrying. `atk diff` needs the API for the latest files, so it doesn't work offline.
134
134
 
135
+ ### In CI
136
+
137
+ These fail a build when they find something (CLI 9.2.0+):
138
+
139
+ - `atk sync --check` exits 1 when the installed files don't match the lockfile: a file or config entry missing or changed, a local edit (which `atk sync` keeps but the check doesn't), a hook copy sync would remove because its locked version doesn't declare the tool, or an asset sync would refuse. Frozen assets and assets uninstalled for a tool pass, except such a hook copy left on disk. It changes nothing. Add `--user` to check the user scope. It can't be combined with `--force`.
140
+ - `atk outdated --exit-code` exits 1 when an installed asset has an update. A pin that doesn't match the installed version doesn't count.
141
+ - `atk diff <asset> --exit-code` exits 1 when the installed files differ from the latest version's. A newer version with the same files exits 0. `atk diff <asset>@<version>` compares with that version instead, and `--installed` with the version the lockfile records, so `atk diff <asset> --installed --exit-code` fails only on local edits. `atk diff <asset>@<a>..<b>` compares two published versions with each other and reads no local files: `-` lines come from `<a>` and `+` lines from `<b>`, each side resolves as `@<version>` does, and `--exit-code` exits 1 when their files differ.
142
+ - `atk drift [path] --exit-code` exits 1 when a local asset folder differs from its published version.
143
+
144
+ `atk audit` exits 1 on a checksum failure, as before. Without these flags, the commands exit 0 when they find something. An error is still exit 1 (with `--json`, `"outcome": "error"`), and a usage error is exit 2. All four call the ATK API, so give them a token. A CI checkout has no `.atkrc.json` (it is gitignored), so name the tool with `--tool`:
145
+
146
+ ```bash
147
+ export ATK_TOKEN="$(az account get-access-token --scope api://8da5aa72-0565-4ae8-bf5a-db89d8bd3186/access_as_user --query accessToken -o tsv)"
148
+ npx @detergent-software/atk sync --check --tool claude-code --json
149
+ npx @detergent-software/atk outdated --project --tool claude-code --exit-code
150
+ ```
151
+
135
152
  ```bash
136
153
  atk search commit --offline
137
154
  ```
@@ -224,11 +224,11 @@
224
224
  "name": "diff",
225
225
  "args": [
226
226
  {
227
- "description": "Asset name (supports @org/name syntax)",
227
+ "description": "Asset name (supports name@version, name@<a>..<b> for two versions, and @org/name syntax)",
228
228
  "name": "<name>"
229
229
  }
230
230
  ],
231
- "description": "Show changes between installed and latest version",
231
+ "description": "Show changes between installed files and the latest or a chosen version, or between two versions",
232
232
  "file": "diff.js",
233
233
  "options": [
234
234
  {
@@ -237,6 +237,16 @@
237
237
  "parse": "number",
238
238
  "defaultValue": 3
239
239
  },
240
+ {
241
+ "flags": "--exit-code",
242
+ "description": "Exit 1 when the compared files differ (for CI)",
243
+ "defaultValue": false
244
+ },
245
+ {
246
+ "flags": "--installed",
247
+ "description": "Compare with the version the lockfile records, which shows only local edits",
248
+ "defaultValue": false
249
+ },
240
250
  {
241
251
  "flags": "-j, --json",
242
252
  "description": "Output raw JSON (for scripting and CI)",
@@ -310,6 +320,11 @@
310
320
  "description": "Compare a local asset directory against the latest published version",
311
321
  "file": "drift.js",
312
322
  "options": [
323
+ {
324
+ "flags": "--exit-code",
325
+ "description": "Exit 1 when the folder drifts from the published version (for CI)",
326
+ "defaultValue": false
327
+ },
313
328
  {
314
329
  "flags": "-j, --json",
315
330
  "description": "Output raw JSON (for scripting and CI)",
@@ -337,6 +352,10 @@
337
352
  {
338
353
  "description": "Asset name to freeze (supports @org/name syntax)",
339
354
  "name": "<name>"
355
+ },
356
+ {
357
+ "description": "More assets to freeze with the first, in one pass",
358
+ "name": "[names...]"
340
359
  }
341
360
  ],
342
361
  "description": "Freeze an installed asset to prevent sync and update from overwriting it",
@@ -498,6 +517,10 @@
498
517
  {
499
518
  "description": "Asset, bundle, or org scope to install (supports name@version and @org)",
500
519
  "name": "[target]"
520
+ },
521
+ {
522
+ "description": "More assets, bundles or org scopes to install with the first, as one plan (name@version, @org/name, @org with --all)",
523
+ "name": "[targets...]"
501
524
  }
502
525
  ],
503
526
  "description": "Install an asset or bundle (no args syncs from lockfile)",
@@ -510,7 +533,7 @@
510
533
  },
511
534
  {
512
535
  "flags": "-a, --all",
513
- "description": "Install every asset from an org scope without prompting (for `atk install @org`; required with --json or no terminal)",
536
+ "description": "Install every asset from an org scope without prompting (for `atk install @org`; required with --json, no terminal, or other names)",
514
537
  "defaultValue": false
515
538
  },
516
539
  {
@@ -701,6 +724,11 @@
701
724
  "description": "Show available updates for installed assets",
702
725
  "file": "outdated.js",
703
726
  "options": [
727
+ {
728
+ "flags": "--exit-code",
729
+ "description": "Exit 1 when an installed asset has an update (for CI)",
730
+ "defaultValue": false
731
+ },
704
732
  {
705
733
  "flags": "-j, --json",
706
734
  "description": "Output raw JSON (for scripting and CI)",
@@ -750,6 +778,10 @@
750
778
  {
751
779
  "description": "Asset name (supports @org/name); an @version suffix must be the installed version. To pin another version, run atk install <name>@<version> --pin",
752
780
  "name": "<name>"
781
+ },
782
+ {
783
+ "description": "More assets to pin with the first, in one pass (each may carry @version)",
784
+ "name": "[names...]"
753
785
  }
754
786
  ],
755
787
  "description": "Pin an installed asset at its installed version",
@@ -1215,9 +1247,20 @@
1215
1247
  {
1216
1248
  "isDefault": false,
1217
1249
  "name": "sync",
1218
- "description": "Re-apply drifted installed assets, keeping local edits (use --force to re-apply all)",
1250
+ "args": [
1251
+ {
1252
+ "description": "Installed assets to sync, as @org/name or name (all of them when omitted)",
1253
+ "name": "[names...]"
1254
+ }
1255
+ ],
1256
+ "description": "Re-apply drifted installed assets, or only the named ones, keeping local edits (use --force to re-apply them all)",
1219
1257
  "file": "sync.js",
1220
1258
  "options": [
1259
+ {
1260
+ "flags": "--check",
1261
+ "description": "Exit 1 when installed files don't match the lockfile, local edits included; changes nothing (for CI)",
1262
+ "defaultValue": false
1263
+ },
1221
1264
  {
1222
1265
  "flags": "-n, --dry-run",
1223
1266
  "description": "Show what would be synced without making changes",
@@ -1225,7 +1268,7 @@
1225
1268
  },
1226
1269
  {
1227
1270
  "flags": "-f, --force",
1228
- "description": "Force re-sync all assets, not just drifted ones, overwriting local edits",
1271
+ "description": "Force re-sync all assets (or the named ones), not just drifted ones, overwriting local edits",
1229
1272
  "defaultValue": false
1230
1273
  },
1231
1274
  {
@@ -1246,6 +1289,18 @@
1246
1289
  "flags": "-T, --tool [tool]",
1247
1290
  "description": "Target tool (e.g., claude-code)"
1248
1291
  },
1292
+ {
1293
+ "flags": "-t, --type [type]",
1294
+ "description": "Type of a named asset, when the name is installed as more than one type",
1295
+ "choices": [
1296
+ "skill",
1297
+ "agent",
1298
+ "rule",
1299
+ "hook",
1300
+ "memory-template",
1301
+ "mcp-config"
1302
+ ]
1303
+ },
1249
1304
  {
1250
1305
  "flags": "--user",
1251
1306
  "description": "Use the user scope (the user root, ~ by default) instead of the project",
@@ -1260,6 +1315,10 @@
1260
1315
  {
1261
1316
  "description": "Asset name to thaw",
1262
1317
  "name": "<name>"
1318
+ },
1319
+ {
1320
+ "description": "More assets to thaw with the first, in one pass",
1321
+ "name": "[names...]"
1263
1322
  }
1264
1323
  ],
1265
1324
  "description": "Thaw a frozen asset to allow sync and update to overwrite it again",
@@ -1300,6 +1359,10 @@
1300
1359
  {
1301
1360
  "description": "Asset or bundle name to uninstall",
1302
1361
  "name": "<name>"
1362
+ },
1363
+ {
1364
+ "description": "More assets or bundles to uninstall with the first, in one pass",
1365
+ "name": "[names...]"
1303
1366
  }
1304
1367
  ],
1305
1368
  "description": "Uninstall an asset or bundle",
@@ -1368,6 +1431,10 @@
1368
1431
  {
1369
1432
  "description": "Asset name to unpin",
1370
1433
  "name": "<name>"
1434
+ },
1435
+ {
1436
+ "description": "More assets to unpin with the first, in one pass",
1437
+ "name": "[names...]"
1371
1438
  }
1372
1439
  ],
1373
1440
  "description": "Remove a version pin from an installed asset",
@@ -1459,6 +1526,10 @@
1459
1526
  {
1460
1527
  "description": "Asset name (updates all if omitted, supports name@version syntax)",
1461
1528
  "name": "[name]"
1529
+ },
1530
+ {
1531
+ "description": "More assets to update to their latest versions",
1532
+ "name": "[names...]"
1462
1533
  }
1463
1534
  ],
1464
1535
  "description": "Update installed assets",
@@ -1474,6 +1545,11 @@
1474
1545
  "description": "Override version pins on targeted assets, and overwrite local edits",
1475
1546
  "defaultValue": false
1476
1547
  },
1548
+ {
1549
+ "flags": "-I, --interactive",
1550
+ "description": "Pick the outdated assets to update from a list (needs an interactive terminal)",
1551
+ "defaultValue": false
1552
+ },
1477
1553
  {
1478
1554
  "flags": "-j, --json",
1479
1555
  "description": "Output raw JSON (for scripting and CI)",
@@ -1 +1 @@
1
- {"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgDxB,eAAO,MAAM,WAAW,4CAA4C,CAAC;AASrE,eAAO,MAAM,OAAO;;;;;;;iBAsDlB,CAAC;AAEH,eAAO,MAAM,IAAI,gDAKf,CAAC;AAiBH,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,IAAI,EAAE,CAAC,SAAS,CAAC,EACjB,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAC1E,EAAE,KAAK,+BA0BP"}
1
+ {"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmDxB,eAAO,MAAM,WAAW,4CAA4C,CAAC;AASrE,eAAO,MAAM,OAAO;;;;;;;iBAsDlB,CAAC;AAEH,eAAO,MAAM,IAAI,gDAKf,CAAC;AAiBH,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,IAAI,EAAE,CAAC,SAAS,CAAC,EACjB,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAC1E,EAAE,KAAK,+BA0BP"}
@@ -4,14 +4,16 @@ import { useEffect, useRef } from 'react';
4
4
  import { z } from 'zod';
5
5
  import BrowseList from '../components/BrowseList.js';
6
6
  import FilterBar from '../components/FilterBar.js';
7
- import HelpBar from '../components/HelpBar.js';
7
+ import HelpBar, { helpBarText } from '../components/HelpBar.js';
8
8
  import { errorGuidanceRows, ErrorView, Spinner } from '../components/index.js';
9
- import InstallConfirmBar from '../components/InstallConfirmBar.js';
9
+ import InstallConfirmBar, { resultLine } from '../components/InstallConfirmBar.js';
10
10
  import { TYPE_TAB_LABELS, TYPE_TABS, useBrowseInput, useBrowseState, } from '../hooks/useBrowseState.js';
11
11
  import { useCommand } from '../hooks/useCommand.js';
12
12
  import { useResolvedScope } from '../hooks/useResolvedScope.js';
13
13
  import { loadConfig } from '../lib/config.js';
14
14
  import { errorGuidance, errorMessage } from '../lib/errors.js';
15
+ import { wrappedExtraRows } from '../lib/format.js';
16
+ import { installPlannedTargets, planTargetsInstall } from '../lib/install-targets.js';
15
17
  import { LOCKFILE_LOCKED_MESSAGE, LockfileLockedError, readLockfile, withLockfileLock } from '../lib/lockfile.js';
16
18
  import { executeInstallPlanForTools } from '../lib/multi-tool-install.js';
17
19
  import { formatOrgName, resolveActiveOrg } from '../lib/org.js';
@@ -98,17 +100,20 @@ function BrowseInteractive({ cliOrg, filterArg, projectRoot, refresh, typeOpt, u
98
100
  const [state, dispatch, dispatchLoad] = useBrowseState(initialFilter, initialType);
99
101
  const { exit } = useApp();
100
102
  const { stdout } = useStdout();
101
- const isInstalling = state.status === 'ready' && state.mode.kind === 'installing';
103
+ const isInstalling = state.status === 'ready' && (state.mode.kind === 'installing' || state.mode.kind === 'installing-set');
102
104
  const stateRef = useRef(state);
103
105
  stateRef.current = state;
104
106
  const resolvedToolsRef = useRef(UNRESOLVED_TOOLS);
105
107
  const scopeRef = useRef(undefined);
106
108
  const terminalWidth = stdout?.columns ?? 120;
109
+ // The rows the help bar wraps to past its first. The cursor moves only while it shows, so the
110
+ // reducer scrolls by the list it leaves.
111
+ const helpRows = state.status === 'ready' ? wrappedExtraRows(helpBarText(helpBarPropsOf(state)), terminalWidth) : 0;
107
112
  // Track viewport height
108
113
  useEffect(() => {
109
114
  const height = stdout?.rows ?? 24;
110
- dispatch({ height: Math.max(5, height - CHROME_LINES), type: 'SET_VIEWPORT_HEIGHT' });
111
- }, [stdout?.rows, dispatch]);
115
+ dispatch({ height: Math.max(5, height - CHROME_LINES - helpRows), type: 'SET_VIEWPORT_HEIGHT' });
116
+ }, [stdout?.rows, helpRows, dispatch]);
112
117
  // Load data
113
118
  useEffect(() => {
114
119
  let cancelled = false;
@@ -170,25 +175,30 @@ function BrowseInteractive({ cliOrg, filterArg, projectRoot, refresh, typeOpt, u
170
175
  if (stateRef.current.status !== 'ready')
171
176
  return;
172
177
  const currentState = stateRef.current;
173
- if (currentState.mode.kind !== 'installing')
174
- return;
175
178
  const installingMode = currentState.mode;
179
+ if (installingMode.kind !== 'installing' && installingMode.kind !== 'installing-set')
180
+ return;
176
181
  let cancelled = false;
177
182
  (async () => {
178
183
  try {
179
184
  const resolved = scopeRef.current;
180
185
  if (!resolved)
181
186
  throw new Error('The install scope has not resolved yet.');
182
- await executeInstallFromBrowse({
183
- assetName: installingMode.assetName,
184
- assetOrg: installingMode.assetOrg,
187
+ const shared = {
185
188
  dispatch,
186
189
  isCancelled: () => cancelled,
187
- itemKind: installingMode.itemKind,
188
190
  registry: currentState.registry,
189
191
  resolved,
190
192
  tools: resolvedToolsRef.current,
191
- });
193
+ };
194
+ await (installingMode.kind === 'installing-set'
195
+ ? executeSetInstallFromBrowse({ ...shared, items: installingMode.items })
196
+ : executeInstallFromBrowse({
197
+ ...shared,
198
+ assetName: installingMode.assetName,
199
+ assetOrg: installingMode.assetOrg,
200
+ itemKind: installingMode.itemKind,
201
+ }));
192
202
  }
193
203
  catch (err) {
194
204
  if (!cancelled) {
@@ -229,7 +239,6 @@ function BrowseInteractive({ cliOrg, filterArg, projectRoot, refresh, typeOpt, u
229
239
  : state.typeFilter === 'all'
230
240
  ? state.allAssets.length + state.allBundles.length
231
241
  : state.allAssets.filter((a) => a.type === state.typeFilter).length;
232
- const canInstall = state.filteredItems.length > 0;
233
242
  const sortArrow = state.sortDirection === 'asc' ? '\u2191' : '\u2193';
234
243
  const sortIndicator = `Sort: ${BROWSE_SORT_FIELD_LABELS[state.sortField]} ${sortArrow}`;
235
244
  const expandedItem = state.expandedIndex !== null ? state.filteredItems[state.expandedIndex] : null;
@@ -238,9 +247,15 @@ function BrowseInteractive({ cliOrg, filterArg, projectRoot, refresh, typeOpt, u
238
247
  : expandedItem?.kind === 'bundle'
239
248
  ? buildBundleWebUrl(expandedItem.data.name, expandedItem.data.org)
240
249
  : null;
241
- // A failed install's hint and command make the install bar taller: the list gives up those rows
242
- const guidanceRows = mode.kind === 'install-result' && !mode.success ? errorGuidanceRows(mode) : 0;
243
- return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(FilterBar, { filterText: state.filterText, inputMode: inputMode, org: state.org, orgOnly: state.orgOnly, resultCount: state.filteredItems.length, sortIndicator: sortIndicator, terminalWidth: terminalWidth, totalCount: totalCount, typeFilter: state.typeFilter, typeTabLabels: TYPE_TAB_LABELS, typeTabs: TYPE_TABS }), state.toolWarning && (_jsxs(Text, { color: 'yellow', children: ['\u26A0', " Tool resolution failed: ", state.toolWarning, ". Install will not be available."] })), _jsx(BrowseList, { cursorIndex: state.cursorIndex, expandedIndex: state.expandedIndex, hasOrg: !!state.org, items: state.filteredItems, lockfile: state.lockfile, registryAssets: state.allAssets, scrollOffset: state.scrollOffset, terminalWidth: terminalWidth, typeFilter: state.typeFilter, viewportHeight: Math.max(1, state.viewportHeight - guidanceRows), webUrl: webUrl }), browsing ? (_jsx(HelpBar, { canInstall: canInstall, expanded: state.expandedIndex !== null, hasOrg: !!state.org, inputMode: inputMode })) : (_jsx(InstallConfirmBar, { mode: mode }))] }));
250
+ // The viewport already leaves the help bar's extra rows. Another bar taller than one row (a failed
251
+ // install's hint and command, a long set result) takes its extra rows from the list instead.
252
+ const finished = mode.kind === 'install-result' || mode.kind === 'install-set-result' ? mode : null;
253
+ const barRows = finished
254
+ ? (finished.success ? 0 : errorGuidanceRows(finished)) + wrappedExtraRows(resultLine(finished), terminalWidth)
255
+ : browsing
256
+ ? helpRows
257
+ : 0;
258
+ return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(FilterBar, { filterText: state.filterText, inputMode: inputMode, org: state.org, orgOnly: state.orgOnly, resultCount: state.filteredItems.length, sortIndicator: sortIndicator, terminalWidth: terminalWidth, totalCount: totalCount, typeFilter: state.typeFilter, typeTabLabels: TYPE_TAB_LABELS, typeTabs: TYPE_TABS }), state.toolWarning && (_jsxs(Text, { color: 'yellow', children: ['\u26A0', " Tool resolution failed: ", state.toolWarning, ". Install will not be available."] })), _jsx(BrowseList, { cursorIndex: state.cursorIndex, expandedIndex: state.expandedIndex, hasOrg: !!state.org, items: state.filteredItems, lockfile: state.lockfile, marked: state.marked, registryAssets: state.allAssets, scrollOffset: state.scrollOffset, terminalWidth: terminalWidth, typeFilter: state.typeFilter, viewportHeight: Math.max(1, state.viewportHeight + helpRows - barRows), webUrl: webUrl }), browsing ? _jsx(HelpBar, { ...helpBarPropsOf(state) }) : _jsx(InstallConfirmBar, { mode: mode })] }));
244
259
  }
245
260
  /**
246
261
  * JSON / non-TTY render branch.
@@ -381,4 +396,92 @@ async function executeInstallFromBrowse({ assetName, assetOrg, dispatch, isCance
381
396
  }
382
397
  });
383
398
  }
399
+ /**
400
+ * Install the marked rows through `atk install a b my-bundle`'s plan and install steps: resolved
401
+ * together into one plan, one install across tools with its rollback, a marked asset already in
402
+ * place left out (one in place only as a dependency made direct), and a bundle in place whole left
403
+ * out. As for one row, browse has no deprecation refusal, cross-scope notice or bundle report.
404
+ */
405
+ async function executeSetInstallFromBrowse({ dispatch, isCancelled, items, registry, resolved, tools, }) {
406
+ const { root: projectDir, scope } = resolved;
407
+ if ('error' in tools) {
408
+ throw tools.error;
409
+ }
410
+ await withLockfileLock(projectDir, async () => {
411
+ if (!isCancelled()) {
412
+ dispatch({ message: `Resolving dependencies for ${items.length} marked...`, type: 'INSTALL_PROGRESS' });
413
+ }
414
+ // Read under the lock: the lockfile may have moved on since browse loaded it.
415
+ const lockfile = await readLockfile(projectDir);
416
+ const targets = items.map((item) => item.kind === 'bundle'
417
+ ? { bundle: item.data, kind: 'bundle' }
418
+ : {
419
+ kind: 'asset',
420
+ name: item.data.name,
421
+ ...(item.data.org ? { org: item.data.org } : {}),
422
+ type: item.data.type,
423
+ });
424
+ const planned = planTargetsInstall(registry, targets, lockfile, tools, { force: false, projectDir });
425
+ await installPlannedTargets(planned, tools, {
426
+ onProgress: (message) => {
427
+ if (!isCancelled())
428
+ dispatch({ message, type: 'INSTALL_PROGRESS' });
429
+ },
430
+ onTool: (toolName) => {
431
+ if (!isCancelled())
432
+ dispatch({ message: `Installing for ${toolName}...`, type: 'INSTALL_PROGRESS' });
433
+ },
434
+ projectDir,
435
+ scope,
436
+ });
437
+ const newLockfile = await readLockfile(projectDir);
438
+ if (!isCancelled()) {
439
+ dispatch({
440
+ message: setInstallMessage(planned, {
441
+ tools: tools.map((t) => t.tool).join(', '),
442
+ where: scopeLabel(resolved),
443
+ }),
444
+ newLockfile,
445
+ success: true,
446
+ type: 'INSTALL_COMPLETE',
447
+ });
448
+ }
449
+ });
450
+ }
451
+ /** The help bar for the browsing view: what it can do now, and how many rows are marked. */
452
+ function helpBarPropsOf(state) {
453
+ return {
454
+ canInstall: state.filteredItems.length > 0 || state.marked.size > 0,
455
+ expanded: state.expandedIndex !== null,
456
+ hasOrg: !!state.org,
457
+ inputMode: state.mode.kind === 'browsing' ? state.mode.inputMode : 'navigate',
458
+ markedCount: state.marked.size,
459
+ };
460
+ }
461
+ /**
462
+ * What a set install did with each marked row, in one line: what it installed and for which tools,
463
+ * what was already in place, and what became a direct install; the scope once, after the first part.
464
+ */
465
+ function setInstallMessage({ assets, bundlesInPlace, toInstall }, { tools, where }) {
466
+ const assetsWith = (outcome) => assets.filter((a) => a.outcome === outcome).map((a) => formatOrgName(a.org, a.name));
467
+ const bundleLabel = ({ bundle }) => `bundle ${formatOrgName(bundle.org, bundle.name)} (${bundle.assetCount} assets)`;
468
+ const installed = [
469
+ ...assetsWith('installed'),
470
+ ...toInstall.flatMap((target) => (target.kind === 'bundle' ? [bundleLabel(target)] : [])),
471
+ ];
472
+ const inPlace = [...assetsWith('already-installed'), ...bundlesInPlace.map(bundleLabel)];
473
+ const direct = assetsWith('upgraded-to-direct');
474
+ const are = (names) => (names.length === 1 ? 'is' : 'are');
475
+ const parts = [
476
+ ...(installed.length > 0 ? [`Installed ${installed.join(', ')} for ${tools}`] : []),
477
+ ...(inPlace.length > 0 ? [`${inPlace.join(', ')} ${are(inPlace)} already installed`] : []),
478
+ ...(direct.length > 0
479
+ ? [`${direct.join(', ')} ${are(direct)} now ${direct.length === 1 ? 'a direct install' : 'direct installs'}`]
480
+ : []),
481
+ ];
482
+ const [first, ...rest] = parts;
483
+ if (!first)
484
+ return `Nothing to install in ${where}`;
485
+ return [`${first} in ${where}`, ...rest].join('; ');
486
+ }
384
487
  //# sourceMappingURL=browse.js.map