@skaile/workspaces 0.50.0 → 0.53.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.
Files changed (181) hide show
  1. package/CHANGELOG.md +232 -0
  2. package/dist/asset-feeds-OEGJ6NOF.js +205 -0
  3. package/dist/asset-feeds-OEGJ6NOF.js.map +1 -0
  4. package/dist/asset-manager/index.js +8 -8
  5. package/dist/asset-manager/installer.js +7 -7
  6. package/dist/asset-manager/src/index.d.ts +83 -7
  7. package/dist/asset-manager/src/index.d.ts.map +1 -1
  8. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  9. package/dist/{chunk-P4BDTJMI.js → chunk-2D5YE3FL.js} +4 -4
  10. package/dist/{chunk-P4BDTJMI.js.map → chunk-2D5YE3FL.js.map} +1 -1
  11. package/dist/{chunk-4IGNQG6E.js → chunk-2ZK7M2SG.js} +3 -3
  12. package/dist/{chunk-4IGNQG6E.js.map → chunk-2ZK7M2SG.js.map} +1 -1
  13. package/dist/{chunk-SXT6BAQJ.js → chunk-3IHBGDCG.js} +2 -2
  14. package/dist/{chunk-SXT6BAQJ.js.map → chunk-3IHBGDCG.js.map} +1 -1
  15. package/dist/{chunk-SDAKQCOB.js → chunk-3KPNDTMR.js} +3 -3
  16. package/dist/{chunk-SDAKQCOB.js.map → chunk-3KPNDTMR.js.map} +1 -1
  17. package/dist/{chunk-2Y4HIITD.js → chunk-7ZWLZ5H3.js} +13 -9
  18. package/dist/chunk-7ZWLZ5H3.js.map +1 -0
  19. package/dist/{chunk-6IDBWPK2.js → chunk-BQCGFPHW.js} +4 -3
  20. package/dist/chunk-BQCGFPHW.js.map +1 -0
  21. package/dist/chunk-BXC5547R.js +377 -0
  22. package/dist/chunk-BXC5547R.js.map +1 -0
  23. package/dist/{chunk-3A3OACI2.js → chunk-EOINYN5T.js} +2 -2
  24. package/dist/{chunk-3A3OACI2.js.map → chunk-EOINYN5T.js.map} +1 -1
  25. package/dist/{chunk-XGCXFX7X.js → chunk-F3HNW354.js} +3 -3
  26. package/dist/{chunk-XGCXFX7X.js.map → chunk-F3HNW354.js.map} +1 -1
  27. package/dist/{chunk-33WB7VFV.js → chunk-LKOMH3QC.js} +3 -3
  28. package/dist/{chunk-33WB7VFV.js.map → chunk-LKOMH3QC.js.map} +1 -1
  29. package/dist/{chunk-O4JH3KUE.js → chunk-MENCWIHW.js} +11 -3
  30. package/dist/chunk-MENCWIHW.js.map +1 -0
  31. package/dist/{chunk-PBO6SCT2.js → chunk-NXC6IEL4.js} +37 -14
  32. package/dist/chunk-NXC6IEL4.js.map +1 -0
  33. package/dist/{chunk-EBSIZYM6.js → chunk-QLLFCQU5.js} +12 -12
  34. package/dist/chunk-QLLFCQU5.js.map +1 -0
  35. package/dist/{chunk-X46FY45B.js → chunk-SKADPHAM.js} +12 -8
  36. package/dist/chunk-SKADPHAM.js.map +1 -0
  37. package/dist/{chunk-C5RYGUF4.js → chunk-TCNYSHPM.js} +157 -31
  38. package/dist/chunk-TCNYSHPM.js.map +1 -0
  39. package/dist/{chunk-JXHULIOA.js → chunk-UTHEIPEY.js} +387 -42
  40. package/dist/chunk-UTHEIPEY.js.map +1 -0
  41. package/dist/{chunk-LV73DB4G.js → chunk-V4BGVOJC.js} +26 -22
  42. package/dist/chunk-V4BGVOJC.js.map +1 -0
  43. package/dist/{chunk-ZJ3VRPOI.js → chunk-VE2UIJ7Y.js} +11 -15
  44. package/dist/chunk-VE2UIJ7Y.js.map +1 -0
  45. package/dist/{chunk-H74X4TSR.js → chunk-VGT2R662.js} +6 -6
  46. package/dist/{chunk-H74X4TSR.js.map → chunk-VGT2R662.js.map} +1 -1
  47. package/dist/{chunk-WBF7JLTY.js → chunk-W53D5Q52.js} +4 -4
  48. package/dist/{chunk-WBF7JLTY.js.map → chunk-W53D5Q52.js.map} +1 -1
  49. package/dist/{chunk-HNORRENR.js → chunk-WD2NRI3T.js} +5 -5
  50. package/dist/{chunk-HNORRENR.js.map → chunk-WD2NRI3T.js.map} +1 -1
  51. package/dist/cli/index.js +679 -477
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/src/asset-feeds.d.ts +17 -3
  54. package/dist/cli/src/asset-feeds.d.ts.map +1 -1
  55. package/dist/cli/src/commands/catalog.d.ts +3 -2
  56. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  57. package/dist/cli/src/commands/manage.d.ts +47 -8
  58. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  59. package/dist/cli/src/commands/project.d.ts.map +1 -1
  60. package/dist/cli/src/commands/source.d.ts +32 -0
  61. package/dist/cli/src/commands/source.d.ts.map +1 -1
  62. package/dist/cli/src/commands/update.d.ts +7 -0
  63. package/dist/cli/src/commands/update.d.ts.map +1 -1
  64. package/dist/connectors/config.js +7 -7
  65. package/dist/connectors/index.js +9 -9
  66. package/dist/connectors/src/config.d.ts +5 -13
  67. package/dist/connectors/src/config.d.ts.map +1 -1
  68. package/dist/connectors/src/index.d.ts +1 -1
  69. package/dist/connectors/src/index.d.ts.map +1 -1
  70. package/dist/core/driver-targets.js +1 -1
  71. package/dist/core/index.js +6 -6
  72. package/dist/core/manifest.js +1 -1
  73. package/dist/core/runtime-assets.js +4 -3
  74. package/dist/core/src/cache-paths.d.ts +6 -0
  75. package/dist/core/src/cache-paths.d.ts.map +1 -1
  76. package/dist/core/src/driver-targets.d.ts +12 -7
  77. package/dist/core/src/driver-targets.d.ts.map +1 -1
  78. package/dist/core/src/index.d.ts +3 -3
  79. package/dist/core/src/index.d.ts.map +1 -1
  80. package/dist/core/src/manifest.d.ts +6 -6
  81. package/dist/core/src/manifest.d.ts.map +1 -1
  82. package/dist/core/src/workspace-config.d.ts +36 -0
  83. package/dist/core/src/workspace-config.d.ts.map +1 -1
  84. package/dist/core/store.js +2 -2
  85. package/dist/core/workspace-config.js +3 -2
  86. package/dist/deploy/index.js +6 -6
  87. package/dist/discovery/index.js +2 -2
  88. package/dist/discovery/src/atomization-conventions.d.ts +30 -0
  89. package/dist/discovery/src/atomization-conventions.d.ts.map +1 -0
  90. package/dist/discovery/src/builtin-providers.d.ts.map +1 -1
  91. package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
  92. package/dist/discovery/src/discover.d.ts +38 -0
  93. package/dist/discovery/src/discover.d.ts.map +1 -1
  94. package/dist/discovery/src/index.d.ts +5 -2
  95. package/dist/discovery/src/index.d.ts.map +1 -1
  96. package/dist/discovery/src/reference-lint.d.ts +112 -0
  97. package/dist/discovery/src/reference-lint.d.ts.map +1 -0
  98. package/dist/discovery/src/requires-graph.d.ts +1 -1
  99. package/dist/discovery/src/tree-entries.d.ts +9 -1
  100. package/dist/discovery/src/tree-entries.d.ts.map +1 -1
  101. package/dist/{ensure-sources-EE7Z2HKS.js → ensure-sources-GJOXWVWO.js} +8 -8
  102. package/dist/{ensure-sources-EE7Z2HKS.js.map → ensure-sources-GJOXWVWO.js.map} +1 -1
  103. package/dist/factory-assets/connectors/deploy.js +9 -9
  104. package/dist/factory-assets/connectors/devserver.js +9 -9
  105. package/dist/factory-assets/connectors/flow/adapter.js +9 -9
  106. package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts +1 -1
  107. package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts.map +1 -1
  108. package/dist/factory-assets/connectors/flow/engine.js +7 -7
  109. package/dist/factory-assets/connectors/flow/run-flow.js +10 -10
  110. package/dist/factory-assets/connectors/flow.js +9 -9
  111. package/dist/factory-assets/connectors/git.js +9 -9
  112. package/dist/factory-assets/connectors/gmail/adapter.d.ts.map +1 -1
  113. package/dist/factory-assets/connectors/gmail.js +9 -9
  114. package/dist/factory-assets/connectors/googledrive.js +9 -9
  115. package/dist/factory-assets/connectors/local.js +9 -9
  116. package/dist/factory-assets/connectors/mattermost/adapter.d.ts.map +1 -1
  117. package/dist/factory-assets/connectors/mattermost.js +9 -9
  118. package/dist/factory-assets/connectors/memory.js +9 -9
  119. package/dist/factory-assets/connectors/minio/adapter.d.ts.map +1 -1
  120. package/dist/factory-assets/connectors/minio.js +9 -9
  121. package/dist/factory-assets/connectors/postgres/adapter.d.ts.map +1 -1
  122. package/dist/factory-assets/connectors/postgres.js +9 -9
  123. package/dist/factory-assets/connectors/s3/driver.d.ts.map +1 -1
  124. package/dist/factory-assets/connectors/s3.js +9 -9
  125. package/dist/factory-assets/connectors/sharepoint.js +9 -9
  126. package/dist/factory-assets/connectors/sqlite/adapter.d.ts.map +1 -1
  127. package/dist/factory-assets/connectors/sqlite.js +9 -9
  128. package/dist/factory-assets/connectors/static-server.js +9 -9
  129. package/dist/factory-assets/connectors/tunnel.js +9 -9
  130. package/dist/factory-assets/connectors/webdav.js +9 -9
  131. package/dist/factory-assets/connectors/xstate-store.js +9 -9
  132. package/dist/factory-assets/connectors/xstate.js +9 -9
  133. package/dist/{flows-AAHXXXCH.js → flows-AGPIE6JM.js} +3 -3
  134. package/dist/{flows-AAHXXXCH.js.map → flows-AGPIE6JM.js.map} +1 -1
  135. package/dist/library/index.js +8 -8
  136. package/dist/library/install.js +6 -6
  137. package/dist/open-library-ERKRGH4B.js +23 -0
  138. package/dist/{open-library-TXJMFG34.js.map → open-library-ERKRGH4B.js.map} +1 -1
  139. package/dist/paths-D6RUQ522.js +15 -0
  140. package/dist/{paths-OMVC4UWY.js.map → paths-D6RUQ522.js.map} +1 -1
  141. package/dist/{plugin-store-JWJHIPUT.js → plugin-store-SGNJTBYS.js} +8 -8
  142. package/dist/{plugin-store-JWJHIPUT.js.map → plugin-store-SGNJTBYS.js.map} +1 -1
  143. package/dist/runner/index.js +11 -11
  144. package/dist/runner/src/serve.d.ts +6 -4
  145. package/dist/runner/src/serve.d.ts.map +1 -1
  146. package/dist/sdk/asset-manager.js +8 -8
  147. package/dist/sdk/core.js +6 -6
  148. package/dist/sdk/flow.js +7 -7
  149. package/dist/sdk/index.js +17 -11
  150. package/dist/sdk/index.js.map +1 -1
  151. package/dist/sdk/runner.js +11 -11
  152. package/dist/sdk/src/transport.d.ts +2 -0
  153. package/dist/sdk/src/transport.d.ts.map +1 -1
  154. package/dist/{setup-ETNBVZQA.js → setup-IV2JFW3T.js} +8 -8
  155. package/dist/{setup-ETNBVZQA.js.map → setup-IV2JFW3T.js.map} +1 -1
  156. package/dist/source-FGOMQMZT.js +24 -0
  157. package/dist/source-FGOMQMZT.js.map +1 -0
  158. package/dist/store-client-GJT2XQ5Z.js +15 -0
  159. package/dist/{store-client-SLFAAKSV.js.map → store-client-GJT2XQ5Z.js.map} +1 -1
  160. package/dist/tui/index.js +11 -11
  161. package/dist/types/src/transport.d.ts +12 -0
  162. package/dist/types/src/transport.d.ts.map +1 -1
  163. package/dist/workspace-plugin/adapters/mcp.js +1 -1
  164. package/dist/workspace-plugin/adapters/omp.js +3 -3
  165. package/dist/workspace-plugin/index.js +1 -1
  166. package/package.json +1 -1
  167. package/dist/asset-feeds-O6R6J4RI.js +0 -128
  168. package/dist/asset-feeds-O6R6J4RI.js.map +0 -1
  169. package/dist/chunk-2Y4HIITD.js.map +0 -1
  170. package/dist/chunk-6IDBWPK2.js.map +0 -1
  171. package/dist/chunk-C5RYGUF4.js.map +0 -1
  172. package/dist/chunk-EBSIZYM6.js.map +0 -1
  173. package/dist/chunk-JXHULIOA.js.map +0 -1
  174. package/dist/chunk-LV73DB4G.js.map +0 -1
  175. package/dist/chunk-O4JH3KUE.js.map +0 -1
  176. package/dist/chunk-PBO6SCT2.js.map +0 -1
  177. package/dist/chunk-X46FY45B.js.map +0 -1
  178. package/dist/chunk-ZJ3VRPOI.js.map +0 -1
  179. package/dist/open-library-TXJMFG34.js +0 -23
  180. package/dist/paths-OMVC4UWY.js +0 -15
  181. package/dist/store-client-SLFAAKSV.js +0 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,237 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.53.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#277](https://github.com/skaile-ai/workspaces/pull/277) [`6a914a2`](https://github.com/skaile-ai/workspaces/commit/6a914a2ae40fb7be4463334e3e5dbb3faaf97738) Thanks [@peteralbert](https://github.com/peteralbert)! - Enable in-container OAuth token refresh for per-session SharePoint and WebDAV mounts. `connectorRefreshKind` now maps `sharepoint`→`sharepoint-mount` and `webdav`→`webdav-mount` (in addition to `git`), so the connector token mediator routes their proactive/reactive refreshes to the platform's `host.refresh_credential` handler instead of refusing them. The rclone drivers already carried the apply path (re-render INI + stop/respawn), so these mounts no longer go stale (`invalid_client`) ~1h after wake. `googledrive` remains excluded until its driver gains a refresh controller.
8
+
9
+ ## 0.52.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#271](https://github.com/skaile-ai/workspaces/pull/271) [`4e1c9bf`](https://github.com/skaile-ai/workspaces/commit/4e1c9bf836b4f403b8bcd6115a910419fb44afc2) Thanks [@mortegro](https://github.com/mortegro)! - `skaile update` now offers to sync sources from upstream before re-deploying
14
+ (default yes). Re-deploy resolves against the local source cache, which is frozen
15
+ at the commit captured when each source was first cloned — so without a sync,
16
+ `update` could never see newer GitHub commits on an unpinned source and always
17
+ reported "Nothing to update" (the catalog-refresh stage misled users into
18
+ thinking it had checked).
19
+
20
+ `update` now runs a Stage 2a source sync behind a confirmation prompt before the
21
+ asset re-deploy. The prompt defaults to yes; `--no-sync` skips it, `-y`/`--yes`
22
+ syncs without asking, and a non-interactive shell syncs by default. The sync
23
+ clones missing sources and advances existing clones with `git pull --ff-only`
24
+ (no `--depth=1`, so it fast-forwards instead of tripping the installer's
25
+ shallow-divergence path), then refreshes the manifest cache.
26
+
27
+ New export from the CLI: `syncProjectSources(projectDir, { dev? })`, factored out
28
+ of `autoSyncForSearch` and shared by both call sites.
29
+
30
+ ### Patch Changes
31
+
32
+ - [#273](https://github.com/skaile-ai/workspaces/pull/273) [`f689c55`](https://github.com/skaile-ai/workspaces/commit/f689c55cb21a5e52a8a8d00d9ee53738fe15547b) Thanks [@mortegro](https://github.com/mortegro)! - Fix flow installs not pulling their dependencies. Installing a `flow:` asset
33
+ (e.g. `skaile install` of a flow declared in `skaile.yaml`) deployed only the
34
+ bare `.flow.yaml` and none of the contracts / skills / sub-flows the flow
35
+ declares under `requires:`. The provenance walker only read a `requires`/
36
+ `dependencies` list for `bundle` kinds (`bundleDeps`), so a flow's transitive
37
+ closure was always empty. `manifestDeps` now also reads a flow's `.flow.yaml` /
38
+ `.flow.json` `requires:`, and `bundleDepRefs` whole-document-parses those flow
39
+ files. Sub-flow requires expand recursively, so installing a top-level flow now
40
+ deploys its full dependency tree.
41
+
42
+ - [#268](https://github.com/skaile-ai/workspaces/pull/268) [`1739497`](https://github.com/skaile-ai/workspaces/commit/1739497dd081f07868e4aee2231b9586d0afd80c) Thanks [@peteralbert](https://github.com/peteralbert)! - Fix a resumed session's workspace being unreadable (`readResource` → 503
43
+ "runtime not reachable") on the tier-1 native-resume / subagents-recreate path.
44
+ `recreateSessionForInit` disposed and recreated the agent session but never
45
+ reassigned the outer `resourceManager` (so the resource-capability getters and
46
+ reads kept hitting the disposed manager) and never re-announced resources, so
47
+ the gateway never learned the recreated session's live resources. The recreate
48
+ now mirrors the first-build wiring: it repoints `resourceManager` at the live
49
+ session, resumes watching, and re-emits `resources_available` /
50
+ `commands_available`. The connect-time emitter is renamed
51
+ `emitAvailabilityOnConnect` → `emitAvailability` (it is now also invoked after a
52
+ recreate, not only on client connect).
53
+
54
+ ## 0.51.0
55
+
56
+ ### Minor Changes
57
+
58
+ - [#252](https://github.com/skaile-ai/workspaces/pull/252) [`3b0c1bd`](https://github.com/skaile-ai/workspaces/commit/3b0c1bd5dedfdcc41a4a5633c0678d4e79d30b39) Thanks [@mortegro](https://github.com/mortegro)! - Reference-aware asset atomization in discovery. The structural file filters that
59
+ decide which files belong to one skill/agent/etc. were purely path-based and
60
+ silently dropped helper files an asset referenced from non-conventional
61
+ locations (e.g. a skill linking `modes/*.md` or `scripts/*.ts`).
62
+
63
+ Four layered mechanisms, none of which use an LLM:
64
+
65
+ 1. **Reference lint (always on)** — discovery scans each asset's text for
66
+ path-like references and reports any that resolve to a real in-dir file the
67
+ filter dropped, via the new `DiscoveryResult.warnings`.
68
+ 2. **Convention companion dirs** — the skill/agent filters now capture a broader
69
+ allowlist of well-known helper subdirs (`scripts/`, `modes/`, `templates/`,
70
+ …), mirrored in the virtual-tree path so store/local SHA256 parity holds.
71
+ 3. **Reference closure (`atomization: "closure"`)** — transitively pulls in
72
+ referenced in-dir files. Opt-in.
73
+ 4. **Full-dir fallback (`atomization: "full-dir"`)** — captures the whole asset
74
+ directory minus nested sub-assets. Opt-in.
75
+
76
+ Plus a dependency post-pass that turns cross-asset prose references into soft
77
+ `body-link` requires edges. The `atomization` option is honored on **both** the
78
+ local-FS (`discoverAssetsInTree`) and virtual-tree (`discoverAssetsInTreeEntries`)
79
+ paths at exact SHA256 parity — both share the scan/closure logic via a new
80
+ `AssetFs` abstraction.
81
+
82
+ New `atomization` option on `DiscoveryOptions` + `DiscoverAssetsInTreeEntriesOptions`
83
+ (default `"strict"`). New exports: `AtomizationPolicy`,
84
+ `DEFAULT_ATOMIZATION_POLICY`, `scanAssetReferences`, `lintUncoveredReferences`,
85
+ `extractPathCandidates`, `closeOverReferences`, `createNodeAssetFs`,
86
+ `SKILL_COMPANION_DIRS`, `AGENT_COMPANION_DIRS`, and the `AssetFs` /
87
+ `ReferenceWarning` / `AssetReference` / `ClosureResult` / `ReferenceClass` types.
88
+ `DiscoveryResult` gains a required `warnings: ReferenceWarning[]` field.
89
+
90
+ - [#256](https://github.com/skaile-ai/workspaces/pull/256) [`3609659`](https://github.com/skaile-ai/workspaces/commit/36096594d71275940bbdcb219ec5da7b46d9a3c4) Thanks [@mortegro](https://github.com/mortegro)! - Global cross-backend install path ([#206](https://github.com/skaile-ai/workspaces/issues/206)). `skaile install --global` now fans the
91
+ deps declared in the user-scope SSOT (`~/.skaile/skaile.yaml`) out to **every**
92
+ global backend the user runs (`claude-code` / `omp` / `codex`), each landing in
93
+ its `DRIVER_TARGETS[target].global` dir, instead of being bolted onto a single
94
+ project-scoped target.
95
+
96
+ In global mode the manager's state — config SSOT, lock, cache, and history —
97
+ roots at `~/.skaile`: a global lock is written to `~/.skaile/skaile.lock.yaml`
98
+ (not the project lock), and staging/fetch goes through `globalCacheRoot()`
99
+ (`~/.skaile/cache`) so a global asset is not re-fetched per project. The old
100
+ `if (this.global) return;` suppression in `AssetManager`'s manifest writers is
101
+ lifted: `skaile add --global <ref>` / `skaile remove --global <ref>` now record
102
+ into `~/.skaile/skaile.yaml` (bootstrapping it on a fresh machine), routing the
103
+ edit to the SSOT rather than `<projectDir>/skaile.yaml`.
104
+
105
+ The backend set is resolvable: an explicit `global_backends:` key in
106
+ `~/.skaile/skaile.yaml`, else auto-detected from existing `~/.<backend>` dirs. A
107
+ `--backend <ids>` filter (comma-separated, on `install` / `add` / `remove` /
108
+ `list`) narrows the fan-out to a subset and implies `--global`.
109
+
110
+ `skaile list --global` reports reconciliation state **per backend** —
111
+ `declared` / `installed` / `drift` (the [#205](https://github.com/skaile-ai/workspaces/issues/205) vocabulary, now backend-aware), so
112
+ an asset deployed to `claude-code` but not `codex` reads `installed` for the
113
+ former and `drift`/`declared` for the latter. `--json` emits
114
+ `{ ref, kind, name, backend, state }`. Project and global scopes stay isolated:
115
+ a project `skaile.yaml` dep deploys into `.claude/` and never touches the global
116
+ `~/.claude/` deploy.
117
+
118
+ New exports:
119
+
120
+ - `@skaile/workspaces/core`: `globalSkaileDir`, `globalBackendRoot`,
121
+ `resolveGlobalBackends`, `resolveGlobalSkWorkspaceConfig`; `SkWorkspaceConfig`
122
+ gains an optional `global_backends?: DriverTarget[]` field.
123
+ - `@skaile/workspaces/asset-manager`: `AssetManager.globalState(opts?)` returning
124
+ `BackendDeclaredAsset[]`; new `AssetManagerOptions.backends`.
125
+
126
+ Additive — no breaking changes to existing surfaces.
127
+
128
+ - [#255](https://github.com/skaile-ai/workspaces/pull/255) [`4a3e756`](https://github.com/skaile-ai/workspaces/commit/4a3e7565f00cb00e86f7d6bb00d3b07e60827b69) Thanks [@mortegro](https://github.com/mortegro)! - `skaile manage`: group on-disk assets by domain. The source / library / factory
129
+ feeds scan with `scanRepo`, which never read the `DOMAIN.md` sidecars, so their
130
+ entries arrived with no `domainSlug` and rendered flat under one publisher band
131
+ (e.g. every `@skaile-ai/*` asset stuffed under `skaile-ai` with no subgroups) —
132
+ only the remote store feed carried domains. The feed layer (`asset-feeds.ts`)
133
+ now parses each root's `DOMAIN.md` files and stamps `domainSlug` per entry
134
+ (reusing `@skaile/workspaces/discovery`'s `parseDomainMd` / `assignDomain` /
135
+ `buildDomainIndex`), falling back to the source's short repo name for entries no
136
+ domain claims. Adds `AssetManager.searchRepoRoots()` so the feed can relate a
137
+ scanned entry back to its source root.
138
+
139
+ - [#266](https://github.com/skaile-ai/workspaces/pull/266) [`94dec67`](https://github.com/skaile-ai/workspaces/commit/94dec67b595c1f998ec0ce8140da33879495fd16) Thanks [@mortegro](https://github.com/mortegro)! - `skaile manage`: the **Sources** tab is now a collapsible tree instead of a flat
140
+ list. Registered github sources render as `Sources → org → repo → domain → asset`
141
+ — the source-origin slice of the Assets feed with an added github-**org** band
142
+ (e.g. `Sources → skaile-ai → ai-assets-skaileup → <domains>`). Domains default
143
+ **collapsed** (collapse-at-domain), so you land on the org/repo/domain skeleton
144
+ and drill in as needed.
145
+
146
+ The tab supports the same install/remove flow as Assets: `←/→` collapse/expand,
147
+ `space/+/-` to stage (org and repo headers bulk-select their subtree; a domain
148
+ header is navigational), `a/Enter` to apply, `i` for info, `d` for the domain
149
+ info region. Source CRUD stays in the shell (`skaile source …`). Sources with no
150
+ discoverable assets still appear as an empty org/repo band so a freshly-added
151
+ source is visible. The Assets-tab tree is unchanged.
152
+
153
+ - [#254](https://github.com/skaile-ai/workspaces/pull/254) [`3034e45`](https://github.com/skaile-ai/workspaces/commit/3034e45f1c299bfe2693c1967a81000f515e6b95) Thanks [@mortegro](https://github.com/mortegro)! - Remove the deprecated `resolveAuth` shim from the `@skaile/workspaces/connectors`
154
+ public surface. Connectors now resolve credentials through
155
+ `SecretProviderChain.resolve()` directly — `ctx.secrets?.resolve(declaration.auth ?? "")`
156
+ in place of `resolveAuth(declaration.auth, ctx.secrets)`. All built-in connectors
157
+ (s3, minio, sqlite, postgres, gmail, mattermost) and the internal
158
+ `resolveMountCredential` path are migrated; the external `@skaile/connector-redis`
159
+ plugin is migrated in lockstep.
160
+
161
+ Behaviour is **identical whenever a `SecretProvider` is wired** (the production
162
+ path — the runner always builds a chain via `createCliSecretProviderChain` /
163
+ `createForgeSecretProviderChain`): `resolveAuth(x, secrets)` already delegated to
164
+ `secrets.resolve(x)` in that case, so `env:` lookups and the CLI chain's inline
165
+ passthrough are unchanged. The only contract change is the removed shim's
166
+ no-provider fallback — when `secrets` was `undefined`, `resolveAuth` did a direct
167
+ `process.env` lookup for `env:` refs and returned any other value as a raw
168
+ literal; `secrets?.resolve(...)` now yields `undefined` in that case. See
169
+ `MIGRATION.md`.
170
+
171
+ ### Patch Changes
172
+
173
+ - [#265](https://github.com/skaile-ai/workspaces/pull/265) [`e30e31f`](https://github.com/skaile-ai/workspaces/commit/e30e31fc00f678fb2f9f8e796a8e8687b84153f1) Thanks [@mortegro](https://github.com/mortegro)! - Fix `ERR_FS_EISDIR` crash on `skaile add` / `install` when a catalog entry
174
+ resolved to a clone directory instead of a manifest file. `candidateToCatalogEntry`
175
+ now resolves a concrete manifest path via the suffix-aware `matchManifestPath`
176
+ (covering `prompt`/`flow`), falling back to the candidate's first file rather than
177
+ the whole clone dir. As a backstop, `copyFromManifest` / `symlinkFromManifest`
178
+ now copy a directory source recursively instead of throwing.
179
+
180
+ - [#270](https://github.com/skaile-ai/workspaces/pull/270) [`79e7211`](https://github.com/skaile-ai/workspaces/commit/79e7211a40db89de4e507d39dedfd400d1ed9aed) Thanks [@mortegro](https://github.com/mortegro)! - Fix flow dependency extraction reading the wrong node field. The flow
181
+ requires-graph extractor (`discovery` + the flow kind provider) read the
182
+ skill ref from the top-level `nodes[].skill`, but real flows nest it under
183
+ `nodes[].data.skill` (the shape the engine executes via
184
+ `node.data?.skill ?? node.id`). The mismatch meant installing a flow pulled
185
+ zero skills. Extraction now reads `nodes[].data.skill` (falling back to the
186
+ legacy top-level `nodes[].skill`) and also picks up sub-flow refs from
187
+ `nodes[].data.flow`.
188
+
189
+ - [#269](https://github.com/skaile-ai/workspaces/pull/269) [`b4daa33`](https://github.com/skaile-ai/workspaces/commit/b4daa3357ca151d6bfa7f3b428d3689d831563ff) Thanks [@mortegro](https://github.com/mortegro)! - `skaile manage` Assets tab domains now collapse by default on first open. Previously all domain sections were expanded, resulting in an overwhelming list for sources with many domains (e.g. 15 domains / 105 assets). A user expand within the session is preserved across background data refreshes.
190
+
191
+ - [#264](https://github.com/skaile-ai/workspaces/pull/264) [`f770d53`](https://github.com/skaile-ai/workspaces/commit/f770d534c2022f82fd832a114bee0e852738d6e9) Thanks [@mortegro](https://github.com/mortegro)! - `skaile manage`: group the Sources band by source, and stop listing factory
192
+ skills twice. Two fixes to the Assets-tab tree:
193
+
194
+ - **Group by source under Sources.** The second-level node under the 🔗 Sources
195
+ band was the asset-intrinsic `publisher` (every `@skaile-ai/*` source stuffed
196
+ under one `skaile-ai` band). It is now the source's short repo name — "which
197
+ repo did this come from" — which is what the Sources band is about. Other
198
+ origins (library / factory / store) still group by publisher. Carried on a new
199
+ optional `OriginEntry.group` and threaded onto both header and asset rows so
200
+ collapse/selection keys stay consistent; the canonical install ref still uses
201
+ `publisher`.
202
+ - **No duplicate factory.** The bundled factory tree is injected into the
203
+ repositories map as an implicit source, so `am.search()` returned it too and
204
+ the first-party skills appeared under **both** the Sources band and the
205
+ dedicated 🏭 Skaile Factory Assets band. The source feed now drops entries
206
+ whose path is under `factoryAssetRoots()`, leaving them only in the factory
207
+ band. An explicitly `source add`ed factory-URL clone is unaffected.
208
+
209
+ Source-feed entries that match no `DOMAIN.md` now hang directly under the source
210
+ node instead of a synthetic same-named fallback domain (avoids `source → ◈ same
211
+ source` self-nesting). `stampDomains` gains a `synthesizeFallback` flag for this.
212
+
213
+ - [#257](https://github.com/skaile-ai/workspaces/pull/257) [`e4314d6`](https://github.com/skaile-ai/workspaces/commit/e4314d670e9d960a7f774e2bb6b9f586bfa0e25f) Thanks [@mortegro](https://github.com/mortegro)! - `skaile search` robustness ([#208](https://github.com/skaile-ai/workspaces/issues/208)). The store-unavailable case is no longer
214
+ silently swallowed on a default search — it degrades to local results but prints
215
+ a one-line `store unavailable …` notice (explicit `--store` still surfaces the
216
+ real error). `search` now honours air-gapped mode (`catalog.cache_ttl: 0`): it
217
+ makes no store network call and says so, and `--store` errors clearly instead of
218
+ fetching. On an unsynced workspace, a default `search` auto-syncs the project's
219
+ sources and re-queries (with a `sources not synced — syncing now…` message)
220
+ instead of just telling the user to run `skaile source sync`; auto-sync is
221
+ skipped when air-gapped and falls back to the old hint on failure.
222
+
223
+ - [#250](https://github.com/skaile-ai/workspaces/pull/250) [`53d2e48`](https://github.com/skaile-ai/workspaces/commit/53d2e48347ef0a9daf304106e97ba4948eabac3b) Thanks [@Frozen666](https://github.com/Frozen666)! - Promote `listen()` / `close()` onto the `ServerTransport` interface so any
224
+ transport (WebSocket today, broker-relayed later) can be started/stopped
225
+ polymorphically. The runner's `serve()` no longer `instanceof`-gates the
226
+ WebSocket transport for listen/close/onConnect; in-process and in-memory
227
+ transports implement the two methods as no-ops. WebSocket behavior is
228
+ unchanged. Prerequisite for the platform session-network-isolation transport.
229
+
230
+ Behavior note: dropping the `onConnect` instanceof guard means a `serve()` run
231
+ driven over the in-memory test transport now emits `commands_available` /
232
+ `resources_available` on client connect (it mirrors the WebSocket path). The
233
+ in-process transport's `onConnect` still never fires, so it is unaffected.
234
+
3
235
  ## 0.50.0
4
236
 
5
237
  ### Minor Changes
@@ -0,0 +1,205 @@
1
+ import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-UTHEIPEY.js';
2
+ import './chunk-JXT46QFC.js';
3
+ import './chunk-3KPNDTMR.js';
4
+ import './chunk-ZAP2BDHG.js';
5
+ import { factoryAssetRoots } from './chunk-W53D5Q52.js';
6
+ import { scanRepo } from './chunk-NXC6IEL4.js';
7
+ import './chunk-DZCK7GRW.js';
8
+ import { walkDir, parseFrontmatter } from './chunk-BQCGFPHW.js';
9
+ import './chunk-GEEW6SY2.js';
10
+ import './chunk-F3HNW354.js';
11
+ import './chunk-MENCWIHW.js';
12
+ import './chunk-K5GBV4SA.js';
13
+ import './chunk-JB35MVBN.js';
14
+ import './chunk-CU5Z7GH3.js';
15
+ import './chunk-NSBPE2FW.js';
16
+ import { existsSync, readFileSync } from 'fs';
17
+ import * as path from 'path';
18
+
19
+ function toPosix(p) {
20
+ return p.split(path.sep).join("/");
21
+ }
22
+ function loadDomainMds(root) {
23
+ const out = [];
24
+ for (const { path: filePath, name } of walkDir(root)) {
25
+ if (name !== DOMAIN_MD_FILENAME) continue;
26
+ try {
27
+ const { data } = parseFrontmatter(readFileSync(filePath, "utf8"));
28
+ out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));
29
+ } catch {
30
+ }
31
+ }
32
+ return out;
33
+ }
34
+ function stampDomains(root, fallbackSlug, entries, synthesizeFallback = true) {
35
+ const domainMds = loadDomainMds(root);
36
+ const assignables = entries.map((entry) => ({
37
+ entry,
38
+ ref: `${entry.kind}:${entry.name}`,
39
+ relativePath: toPosix(path.relative(root, entry.source))
40
+ }));
41
+ let usedFallback = false;
42
+ for (const a of assignables) {
43
+ const slug = assignDomain(a.relativePath, domainMds);
44
+ if (slug) a.entry.domainSlug = slug;
45
+ else if (synthesizeFallback) {
46
+ a.entry.domainSlug = fallbackSlug;
47
+ usedFallback = true;
48
+ } else a.entry.domainSlug = "";
49
+ }
50
+ const { index } = buildDomainIndex(assignables, domainMds);
51
+ const catalog = Object.values(index).map((d) => ({
52
+ slug: d.slug,
53
+ title: d.title,
54
+ ...d.description ? { description: d.description } : {},
55
+ ...d.relation ? { relation: d.relation } : {},
56
+ ...d.order !== void 0 ? { order: d.order } : {},
57
+ ...d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}
58
+ }));
59
+ if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });
60
+ return catalog;
61
+ }
62
+ async function libraryAssets() {
63
+ const notes = [];
64
+ const domains = [];
65
+ try {
66
+ const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([
67
+ import('./open-library-ERKRGH4B.js'),
68
+ import('./library/index.js'),
69
+ import('./core/index.js')
70
+ ]);
71
+ const { manager, close } = await openLibraryManager();
72
+ try {
73
+ const newRoot = getGlobalCacheDir();
74
+ const legacyRoot = path.join(skaileHomeDir(), "sources");
75
+ const libs = (await manager.listLibraries()).filter(
76
+ (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot)
77
+ );
78
+ const entries = [];
79
+ for (const lib of libs) {
80
+ if (!existsSync(lib.path)) continue;
81
+ const libEntries = [...scanRepo(lib.path, `library:${lib.name}`)];
82
+ domains.push(...stampDomains(lib.path, `library:${lib.name}`, libEntries));
83
+ entries.push(...libEntries);
84
+ }
85
+ return { entries, domains, notes };
86
+ } finally {
87
+ close();
88
+ }
89
+ } catch (err) {
90
+ notes.push({
91
+ feed: "libraries",
92
+ message: err instanceof Error ? err.message : String(err)
93
+ });
94
+ return { entries: [], domains, notes };
95
+ }
96
+ }
97
+ async function storeAssets(projectDir) {
98
+ const notes = [];
99
+ try {
100
+ const { resolveCatalogSource } = await import('./open-library-ERKRGH4B.js');
101
+ const { source, close } = await resolveCatalogSource({ projectDir });
102
+ try {
103
+ const assets = await source.listAssets();
104
+ const entries = assets.map((a) => ({
105
+ name: a.name,
106
+ kind: a.kind ?? "skill",
107
+ description: a.description ?? "",
108
+ source: `store://${a.id}`,
109
+ publisher: a.publisher,
110
+ repo: a.repo,
111
+ domainSlug: a.domainSlug,
112
+ category: a.category,
113
+ version: a.version,
114
+ requires: [],
115
+ dependencies: []
116
+ }));
117
+ const domains = await loadDomains(source);
118
+ return { entries, domains, notes };
119
+ } finally {
120
+ close();
121
+ }
122
+ } catch (err) {
123
+ const msg = err instanceof Error ? err.message : String(err);
124
+ if (!/local/i.test(msg)) {
125
+ notes.push({ feed: "store", message: msg });
126
+ }
127
+ return { entries: [], domains: [], notes };
128
+ }
129
+ }
130
+ async function loadDomains(source) {
131
+ const fn = source.listDomains;
132
+ if (typeof fn !== "function") return [];
133
+ const domains = await fn.call(source);
134
+ return Array.isArray(domains) ? domains : [];
135
+ }
136
+ function sourceAssets(am) {
137
+ const factoryPrefixes = factoryAssetRoots().map((r) => r.endsWith(path.sep) ? r : r + path.sep);
138
+ const isFactoryPath = (p) => factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));
139
+ const all = am.search().filter((e) => !isFactoryPath(e.source));
140
+ const entries = [];
141
+ const domains = [];
142
+ for (const { repoName, root } of am.searchRepoRoots()) {
143
+ if (isFactoryPath(root)) continue;
144
+ const prefix = root.endsWith(path.sep) ? root : root + path.sep;
145
+ const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));
146
+ if (owned.length === 0) continue;
147
+ const sourceName = repoName.split("/").pop() || repoName;
148
+ domains.push(...stampDomains(root, sourceName, owned, false));
149
+ for (const entry of owned) entries.push({ origin: "source", entry, group: sourceName });
150
+ }
151
+ return { entries, domains };
152
+ }
153
+ function factoryAssets() {
154
+ const notes = [];
155
+ const domains = [];
156
+ try {
157
+ const entries = [];
158
+ for (const root of factoryAssetRoots()) {
159
+ const rootEntries = [...scanRepo(root, "skaile-factory")];
160
+ domains.push(...stampDomains(root, "skaile-factory", rootEntries));
161
+ entries.push(...rootEntries);
162
+ }
163
+ return { entries, domains, notes };
164
+ } catch (err) {
165
+ notes.push({
166
+ feed: "factory",
167
+ message: err instanceof Error ? err.message : String(err)
168
+ });
169
+ return { entries: [], domains, notes };
170
+ }
171
+ }
172
+ function tag(origin, entries) {
173
+ return entries.map((entry) => ({ origin, entry }));
174
+ }
175
+ async function gatherAssetFeeds(am, projectDir) {
176
+ const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);
177
+ const factory = factoryAssets();
178
+ const src = sourceAssets(am);
179
+ return {
180
+ entries: [
181
+ ...src.entries,
182
+ ...tag("library", libs.entries),
183
+ ...tag("factory", factory.entries),
184
+ ...tag("store", store.entries)
185
+ ],
186
+ // Store domains first (server-curated), then the on-disk feeds; dedupe by
187
+ // slug, first-wins, so a store-served domain shadows a same-slug local one.
188
+ domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),
189
+ notes: [...libs.notes, ...store.notes, ...factory.notes]
190
+ };
191
+ }
192
+ function dedupeBySlug(domains) {
193
+ const seen = /* @__PURE__ */ new Set();
194
+ const out = [];
195
+ for (const d of domains) {
196
+ if (seen.has(d.slug)) continue;
197
+ seen.add(d.slug);
198
+ out.push(d);
199
+ }
200
+ return out;
201
+ }
202
+
203
+ export { gatherAssetFeeds };
204
+ //# sourceMappingURL=asset-feeds-OEGJ6NOF.js.map
205
+ //# sourceMappingURL=asset-feeds-OEGJ6NOF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-OEGJ6NOF.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nfunction loadDomainMds(root: string): DomainMd[] {\n const out: DomainMd[] = [];\n for (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nfunction stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): CatalogDomain[] {\n const domainMds = loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and tagged with\n * `repository = \"library:<name>\"` so the manage TUI groups them into their\n * own headers.\n */\nasync function libraryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: CatalogEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n const libEntries = [...scanRepo(lib.path, `library:${lib.name}`)];\n domains.push(...stampDomains(lib.path, `library:${lib.name}`, libEntries));\n entries.push(...libEntries);\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => CatalogEntry[];\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nfunction sourceAssets(am: AssetManagerLike): { entries: OriginEntry[]; domains: CatalogDomain[] } {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = am.search().filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...stampDomains(root, sourceName, owned, false));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Synchronous, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nfunction factoryAssets(): { entries: CatalogEntry[]; domains: CatalogDomain[]; notes: FeedNote[] } {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...scanRepo(root, \"skaile-factory\")];\n domains.push(...stampDomains(root, \"skaile-factory\", rootEntries));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local + synchronous, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = factoryAssets();\n const src = sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...tag(\"library\", libs.entries),\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
@@ -1,14 +1,14 @@
1
- export { AssetManager, MissingManifestError, NON_DEPLOYABLE_KINDS, parseCanonicalLockKey } from '../chunk-C5RYGUF4.js';
2
- export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-2Y4HIITD.js';
3
- import '../chunk-SDAKQCOB.js';
1
+ export { AssetManager, MissingManifestError, NON_DEPLOYABLE_KINDS, parseCanonicalLockKey } from '../chunk-TCNYSHPM.js';
2
+ export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-7ZWLZ5H3.js';
3
+ import '../chunk-3KPNDTMR.js';
4
4
  import '../chunk-ZAP2BDHG.js';
5
- import '../chunk-WBF7JLTY.js';
6
- import '../chunk-PBO6SCT2.js';
5
+ import '../chunk-W53D5Q52.js';
6
+ import '../chunk-NXC6IEL4.js';
7
7
  import '../chunk-DZCK7GRW.js';
8
- import '../chunk-6IDBWPK2.js';
8
+ import '../chunk-BQCGFPHW.js';
9
9
  import '../chunk-GEEW6SY2.js';
10
- import '../chunk-XGCXFX7X.js';
11
- import '../chunk-O4JH3KUE.js';
10
+ import '../chunk-F3HNW354.js';
11
+ import '../chunk-MENCWIHW.js';
12
12
  import '../chunk-K5GBV4SA.js';
13
13
  import '../chunk-JB35MVBN.js';
14
14
  export { appendHistory, clearHistory, getRecentHistory, loadHistory } from '../chunk-KTBKW2FI.js';
@@ -1,13 +1,13 @@
1
- export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-2Y4HIITD.js';
2
- import '../chunk-SDAKQCOB.js';
1
+ export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-7ZWLZ5H3.js';
2
+ import '../chunk-3KPNDTMR.js';
3
3
  import '../chunk-ZAP2BDHG.js';
4
- import '../chunk-WBF7JLTY.js';
5
- import '../chunk-PBO6SCT2.js';
4
+ import '../chunk-W53D5Q52.js';
5
+ import '../chunk-NXC6IEL4.js';
6
6
  import '../chunk-DZCK7GRW.js';
7
- import '../chunk-6IDBWPK2.js';
7
+ import '../chunk-BQCGFPHW.js';
8
8
  import '../chunk-GEEW6SY2.js';
9
- import '../chunk-XGCXFX7X.js';
10
- import '../chunk-O4JH3KUE.js';
9
+ import '../chunk-F3HNW354.js';
10
+ import '../chunk-MENCWIHW.js';
11
11
  import '../chunk-K5GBV4SA.js';
12
12
  import '../chunk-JB35MVBN.js';
13
13
  import '../chunk-WRBF34YE.js';