@skaile/workspaces 3.1.0 → 3.2.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 (114) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +5 -1
  3. package/dist/{asset-feeds-3XCKB542.js → asset-feeds-25DVAGVD.js} +4 -4
  4. package/dist/{asset-feeds-3XCKB542.js.map → asset-feeds-25DVAGVD.js.map} +1 -1
  5. package/dist/asset-manager/index.js +1 -1
  6. package/dist/{chunk-VBMY3VCW.js → chunk-3IGXVTOS.js} +3 -3
  7. package/dist/{chunk-VBMY3VCW.js.map → chunk-3IGXVTOS.js.map} +1 -1
  8. package/dist/{chunk-5QGMEPUX.js → chunk-B4J6TV4G.js} +21 -5
  9. package/dist/chunk-B4J6TV4G.js.map +1 -0
  10. package/dist/{chunk-ZGTM4BOU.js → chunk-CO4DNTIL.js} +2 -2
  11. package/dist/{chunk-ZGTM4BOU.js.map → chunk-CO4DNTIL.js.map} +1 -1
  12. package/dist/{chunk-PUEHYNBT.js → chunk-CTCCFKZV.js} +3 -3
  13. package/dist/{chunk-PUEHYNBT.js.map → chunk-CTCCFKZV.js.map} +1 -1
  14. package/dist/{chunk-HNR2IT34.js → chunk-DEW2G3QZ.js} +8 -8
  15. package/dist/{chunk-HNR2IT34.js.map → chunk-DEW2G3QZ.js.map} +1 -1
  16. package/dist/{chunk-BENN6MO4.js → chunk-DPFSO4T2.js} +4 -4
  17. package/dist/{chunk-BENN6MO4.js.map → chunk-DPFSO4T2.js.map} +1 -1
  18. package/dist/{chunk-S5FV6ISY.js → chunk-EYBGQJNF.js} +26 -6
  19. package/dist/chunk-EYBGQJNF.js.map +1 -0
  20. package/dist/{chunk-PT2ZUW2E.js → chunk-F5YPDCN6.js} +2 -2
  21. package/dist/{chunk-PT2ZUW2E.js.map → chunk-F5YPDCN6.js.map} +1 -1
  22. package/dist/{chunk-7KFN2EPR.js → chunk-FLBDLF3I.js} +4 -4
  23. package/dist/{chunk-7KFN2EPR.js.map → chunk-FLBDLF3I.js.map} +1 -1
  24. package/dist/{chunk-N4GFUWWC.js → chunk-HNBWU5RA.js} +3 -3
  25. package/dist/{chunk-N4GFUWWC.js.map → chunk-HNBWU5RA.js.map} +1 -1
  26. package/dist/{chunk-MJ6EME7A.js → chunk-IMA5H7AG.js} +6 -6
  27. package/dist/{chunk-MJ6EME7A.js.map → chunk-IMA5H7AG.js.map} +1 -1
  28. package/dist/{chunk-TARN55L3.js → chunk-K5OKE4G2.js} +6 -6
  29. package/dist/{chunk-TARN55L3.js.map → chunk-K5OKE4G2.js.map} +1 -1
  30. package/dist/{chunk-H26KDA7J.js → chunk-RW4I42MW.js} +71 -34
  31. package/dist/chunk-RW4I42MW.js.map +1 -0
  32. package/dist/{chunk-7HXS7WZF.js → chunk-ST4RC2JF.js} +2 -2
  33. package/dist/{chunk-7HXS7WZF.js.map → chunk-ST4RC2JF.js.map} +1 -1
  34. package/dist/{chunk-X73AMPOT.js → chunk-XL7ZDZ4O.js} +5 -5
  35. package/dist/{chunk-X73AMPOT.js.map → chunk-XL7ZDZ4O.js.map} +1 -1
  36. package/dist/cli/index.js +30 -30
  37. package/dist/connectors/index.js +4 -4
  38. package/dist/{ensure-sources-AST427RF.js → ensure-sources-YOJ5HOCL.js} +4 -4
  39. package/dist/{ensure-sources-AST427RF.js.map → ensure-sources-YOJ5HOCL.js.map} +1 -1
  40. package/dist/factory-assets/connectors/box.js +4 -4
  41. package/dist/factory-assets/connectors/deploy.js +4 -4
  42. package/dist/factory-assets/connectors/devserver.js +4 -4
  43. package/dist/factory-assets/connectors/flow/CONNECTOR.md +26 -9
  44. package/dist/factory-assets/connectors/flow/adapter.d.ts +15 -7
  45. package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
  46. package/dist/factory-assets/connectors/flow/adapter.js +4 -4
  47. package/dist/factory-assets/connectors/flow/contract/expression.d.ts +3 -1
  48. package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
  49. package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
  50. package/dist/factory-assets/connectors/flow/contract.js +2 -2
  51. package/dist/factory-assets/connectors/flow/engine/bindings.d.ts +12 -0
  52. package/dist/factory-assets/connectors/flow/engine/bindings.d.ts.map +1 -1
  53. package/dist/factory-assets/connectors/flow/engine/check-provenance.d.ts +13 -2
  54. package/dist/factory-assets/connectors/flow/engine/check-provenance.d.ts.map +1 -1
  55. package/dist/factory-assets/connectors/flow/engine/engine.d.ts +5 -1
  56. package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
  57. package/dist/factory-assets/connectors/flow/engine/index.d.ts +1 -0
  58. package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
  59. package/dist/factory-assets/connectors/flow/engine/optional-failure.d.ts +24 -0
  60. package/dist/factory-assets/connectors/flow/engine/optional-failure.d.ts.map +1 -0
  61. package/dist/factory-assets/connectors/flow/engine/router-settlement.d.ts.map +1 -1
  62. package/dist/factory-assets/connectors/flow/engine/types.d.ts +3 -1
  63. package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
  64. package/dist/factory-assets/connectors/flow/engine.js +2 -2
  65. package/dist/factory-assets/connectors/flow/prompt-fragments.js +1 -1
  66. package/dist/factory-assets/connectors/flow/run-flow.js +5 -5
  67. package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts +9 -1
  68. package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts.map +1 -1
  69. package/dist/factory-assets/connectors/flow.js +4 -4
  70. package/dist/factory-assets/connectors/git.js +4 -4
  71. package/dist/factory-assets/connectors/gmail.js +4 -4
  72. package/dist/factory-assets/connectors/googledrive.js +4 -4
  73. package/dist/factory-assets/connectors/local.js +4 -4
  74. package/dist/factory-assets/connectors/mattermost.js +4 -4
  75. package/dist/factory-assets/connectors/memory.js +4 -4
  76. package/dist/factory-assets/connectors/minio.js +4 -4
  77. package/dist/factory-assets/connectors/postgres.js +4 -4
  78. package/dist/factory-assets/connectors/s3.js +4 -4
  79. package/dist/factory-assets/connectors/sharepoint.js +4 -4
  80. package/dist/factory-assets/connectors/sqlite.js +4 -4
  81. package/dist/factory-assets/connectors/static-server.js +4 -4
  82. package/dist/factory-assets/connectors/tunnel.js +4 -4
  83. package/dist/factory-assets/connectors/webdav.js +4 -4
  84. package/dist/factory-assets/connectors/xstate-store.js +4 -4
  85. package/dist/factory-assets/connectors/xstate.js +4 -4
  86. package/dist/{flows-LQSVM45J.js → flows-7KUZGZ77.js} +3 -3
  87. package/dist/{flows-LQSVM45J.js.map → flows-7KUZGZ77.js.map} +1 -1
  88. package/dist/{helpers-IZDG57TR.js → helpers-YNZ5SWNX.js} +4 -4
  89. package/dist/{helpers-IZDG57TR.js.map → helpers-YNZ5SWNX.js.map} +1 -1
  90. package/dist/{library-publish-XGJ7IOU2.js → library-publish-JBZ2TEQI.js} +8 -8
  91. package/dist/{library-publish-XGJ7IOU2.js.map → library-publish-JBZ2TEQI.js.map} +1 -1
  92. package/dist/{open-library-YDS66KLH.js → open-library-H2LAGVTM.js} +7 -7
  93. package/dist/{open-library-YDS66KLH.js.map → open-library-H2LAGVTM.js.map} +1 -1
  94. package/dist/{project-NR53RZOT.js → project-WBIYBTN3.js} +5 -5
  95. package/dist/{project-NR53RZOT.js.map → project-WBIYBTN3.js.map} +1 -1
  96. package/dist/runner/index.js +8 -8
  97. package/dist/sdk/asset-manager.js +1 -1
  98. package/dist/sdk/flow.js +2 -2
  99. package/dist/sdk/index.js +8 -8
  100. package/dist/sdk/runner.js +8 -8
  101. package/dist/{source-DDZKKQH2.js → source-UCMNKSA2.js} +8 -8
  102. package/dist/{source-DDZKKQH2.js.map → source-UCMNKSA2.js.map} +1 -1
  103. package/dist/{source-manifest-publish-OTVKHRX3.js → source-manifest-publish-BZ777MH5.js} +9 -9
  104. package/dist/{source-manifest-publish-OTVKHRX3.js.map → source-manifest-publish-BZ777MH5.js.map} +1 -1
  105. package/dist/tui/index.js +8 -8
  106. package/dist/types/src/flow.d.ts +38 -12
  107. package/dist/types/src/flow.d.ts.map +1 -1
  108. package/dist/workspace-plugin/adapters/mcp.js +1 -1
  109. package/dist/workspace-plugin/adapters/omp.js +3 -3
  110. package/dist/workspace-plugin/index.js +1 -1
  111. package/package.json +1 -1
  112. package/dist/chunk-5QGMEPUX.js.map +0 -1
  113. package/dist/chunk-H26KDA7J.js.map +0 -1
  114. package/dist/chunk-S5FV6ISY.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#520](https://github.com/skaile-ai/workspaces/pull/520) [`04a0b61`](https://github.com/skaile-ai/workspaces/commit/04a0b61fada3da62a78bb1e35e491a7d0155be1e) Thanks [@peteralbert](https://github.com/peteralbert)! - Resolve an optional flow node's exhausted failure to `skipped` uniformly, instead of only for `sub-flow` and `subprompt`.
8
+
9
+ `isBlocking` admits a hard `flow` edge only for `complete` and `skipped`, so an optional `agent` (deadline expiry or `fail_node(recoverable: false)`), `function`, or `check` node that ran out of retry budget ended `failed` and blocked every hard-edge dependent forever — a permanent stall at the node the author explicitly marked expendable, which is the opposite of what `control.optional` means. The rule was stated at four terminal-failure sites and implemented at two; all nine now route through one pure `resolveTerminalFailureStatus(node)` in `engine/optional-failure.ts`. `agent`, `subprompt`, `function`, and `sub-flow` are the kinds this can change; `check` routes through the same helper but is inert, because `control.optional` on a check is refused at parse time. `outputHistory` is untouched — every degraded attempt still records `result: "failed"`, so the skip is a compatibility status, not a rewritten history.
10
+
11
+ A `router` is the one deliberate exception and still fails when optional. `unsettledRouterBranchIds` excludes a `skipped` router from the gated set while every outgoing hard edge counts as satisfied, so skipping a router admits _all_ of its branches at once — the opposite of selecting one. Optional routers are not hypothetical: v1 normalization maps legacy groups and unknown node kinds onto exactly that shape.
12
+
13
+ Two consequences worth knowing before upgrading. A node about to be written `skipped` no longer reports `error.recoverable: true`, and `retryNode` still refuses `skipped` — so an optional `function`/`agent` node that declared `control.retries` loses the manual operator-retry escape hatch it had while it ended `failed`. That is the intended trade: recovery from a skip is deliberately one-way, because a skip has already released the graph (dependents typically become available in the same drain) and `retryNode` owns no cascade or invalidation machinery, whereas a `failed` node still blocks its dependents and leaves nothing downstream to unwind. `skipped` is also not only a failure resolution — `skip_node` and router loser-pruning produce it — so accepting it would quietly turn `retryNode` into an un-skip and a way to resurrect a pruned router branch. `retryNode`'s refusal message now names the skip instead of reporting a generic non-recoverable failure.
14
+
15
+ Binding a skipped producer's output is now legible. `FlowExpressionError` gains `skippedProducer`, and a `nodes.<id>.output.fields.<name>` read over a `skipped` producer raises `Flow expression cannot read <path>: producer node <id> was skipped and produced no output` instead of the bare `path does not exist`, which said nothing about the upstream skip. `missingPath` is still set, so router settlement classifies it unchanged. Only `skipped` is narrowed this way — `not_started`, `running`, `failed`, and complete-but-fieldless producers keep the generic message.
16
+
17
+ Finally, router settlement no longer waits on a terminal producer: `pendingProducerOutput` treated anything other than `complete` as "not ready yet", so a router reading a skipped (or failed) producer's declared output parked forever. Only a genuinely non-terminal producer is pending now; an unreadable route expression propagates on the path it already took, carrying the message that names the skipped producer.
18
+
19
+ - [#523](https://github.com/skaile-ai/workspaces/pull/523) [`d5d3c31`](https://github.com/skaile-ai/workspaces/commit/d5d3c31d6e98cedba1556564dc139a7620fc400f) Thanks [@peteralbert](https://github.com/peteralbert)! - Make flow binding provenance transitive, so a passthrough node cannot launder it.
20
+
21
+ `BindingOrigin.obtainedBy` was computed one hop from the producing node's kind alone. A
22
+ three-line passthrough `function` node that read an agent's output and re-emitted it as its own
23
+ output relabelled that value `obtainedBy: "runtime"`, and a downstream `check` then reported
24
+ `provenance: "verified"` over it — with `(verified)` shown to the human approver in the escalation
25
+ summary. A value is now runtime-obtained only when no agent-produced value contributed to it
26
+ anywhere in its ancestry, restoring the full claim that provenance is derived and cannot be
27
+ asserted by flow configuration.
28
+
29
+ **Persisted shape (additive).** `NodeExecution` and `NodeExecutionSnapshot` gain an optional
30
+ `inputOrigins?: Record<string, BindingOrigin[]>` — the origins resolved for the attempt that
31
+ produced `output`, a latest-pointer that moves with it. `function`, `check`, `subprompt`, and
32
+ `sub-flow` nodes write it in the same mutation that writes `output`; `agent` nodes record it at
33
+ `start_node` for auditability (their output is agent-obtained by kind, so nothing reads it back);
34
+ `gate` and `router` nodes record nothing, because neither resolves bindings and neither emits
35
+ `output.fields` a downstream binding could read. Classification stays one lookup rather than an
36
+ ancestry walk: a producer's persisted origins were themselves classified under this rule when it
37
+ ran, so they already carry any upstream taint.
38
+
39
+ **Runs that were mid-flight when the field did not exist** have completed nodes with no
40
+ `inputOrigins`. Absence is read **fail-closed** — as agent-obtained, never as clean — so those
41
+ runs' checks report the weaker `"asserted"` instead of a possibly false `"verified"`. Nothing
42
+ throws, and a run self-heals as its nodes re-run and as new runs start. `{}` (a node that
43
+ genuinely bound nothing) is distinct from absence and reads as runtime-obtained, and a malformed
44
+ persisted entry is read fail-closed rather than thrown on. Hosts that persist and rehydrate
45
+ `FlowExecution` should store the new field; a host that drops it degrades that run's checks to
46
+ `"asserted"` rather than misreporting them.
47
+
3
48
  ## 3.1.0
4
49
 
5
50
  ### Minor Changes
package/README.md CHANGED
@@ -120,7 +120,11 @@ response parsing through the same consumed-output validation every kind uses;
120
120
  there is no corrective reprompt, so a parse or validation failure is an
121
121
  ordinary node failure. A subprompt's output is classified agent-obtained, so a
122
122
  downstream check always derives "asserted" provenance over it, never
123
- "verified". Gate nodes
123
+ "verified". That classification is transitive — every executed node persists
124
+ the origins it resolved on `NodeExecution.inputOrigins`, so a deterministic
125
+ node that carries an agent's value forward cannot launder it to
126
+ runtime-obtained, and a producer with no persisted origins (a run predating
127
+ the field) is read as agent-obtained rather than clean. Gate nodes
124
128
  park immediately through the durable approval state, complete on approval, and
125
129
  preserve rejection feedback for revision and a later approval re-request.
126
130
  Router nodes evaluate ordered bounded expressions only after admission. The first
@@ -65,7 +65,7 @@ async function libraryAssets() {
65
65
  const domains = [];
66
66
  try {
67
67
  const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([
68
- import('./open-library-YDS66KLH.js'),
68
+ import('./open-library-H2LAGVTM.js'),
69
69
  import('./library/index.js'),
70
70
  import('./core/index.js')
71
71
  ]);
@@ -98,7 +98,7 @@ async function libraryAssets() {
98
98
  async function storeAssets(projectDir) {
99
99
  const notes = [];
100
100
  try {
101
- const { resolveCatalogSource } = await import('./open-library-YDS66KLH.js');
101
+ const { resolveCatalogSource } = await import('./open-library-H2LAGVTM.js');
102
102
  const { source, close } = await resolveCatalogSource({ projectDir });
103
103
  try {
104
104
  const assets = await source.listAssets();
@@ -202,5 +202,5 @@ function dedupeBySlug(domains) {
202
202
  }
203
203
 
204
204
  export { gatherAssetFeeds };
205
- //# sourceMappingURL=asset-feeds-3XCKB542.js.map
206
- //# sourceMappingURL=asset-feeds-3XCKB542.js.map
205
+ //# sourceMappingURL=asset-feeds-25DVAGVD.js.map
206
+ //# sourceMappingURL=asset-feeds-25DVAGVD.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAqFA,SAAS,QAAQ,CAAA,EAAmB;AAClC,EAAA,OAAO,CAAA,CAAE,KAAA,CAAW,IAAA,CAAA,GAAG,CAAA,CAAE,KAAK,GAAG,CAAA;AACnC;AAGA,eAAe,cAAc,IAAA,EAAmC;AAC9D,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,WAAA,MAAiB,EAAE,IAAA,EAAM,QAAA,EAAU,MAAK,IAAK,OAAA,CAAQ,IAAI,CAAA,EAAG;AAC1D,IAAA,IAAI,SAAS,kBAAA,EAAoB;AACjC,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,IAAA,EAAK,GAAI,iBAAiB,YAAA,CAAa,QAAA,EAAU,MAAM,CAAC,CAAA;AAChE,MAAA,GAAA,CAAI,IAAA,CAAK,cAAc,OAAA,CAAa,IAAA,CAAA,QAAA,CAAS,MAAM,QAAQ,CAAC,CAAA,EAAG,IAAI,CAAC,CAAA;AAAA,IACtE,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACX;AACA,EAAA,OAAO,GAAA;AACT;AAcA,eAAe,YAAA,CACb,IAAA,EACA,YAAA,EACA,OAAA,EACA,qBAAqB,IAAA,EACK;AAC1B,EAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,IAAI,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,IAC1C,KAAA;AAAA,IACA,KAAK,CAAA,EAAG,KAAA,CAAM,IAAI,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA;AAAA,IAChC,cAAc,OAAA,CAAa,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,KAAA,CAAM,MAAM,CAAC;AAAA,GACzD,CAAE,CAAA;AAEF,EAAA,IAAI,YAAA,GAAe,KAAA;AACnB,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,CAAA,CAAE,YAAA,EAAc,SAAS,CAAA;AACnD,IAAA,IAAI,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,UAAA,GAAa,IAAA;AAAA,SAAA,IACtB,kBAAA,EAAoB;AAC3B,MAAA,CAAA,CAAE,MAAM,UAAA,GAAa,YAAA;AACrB,MAAA,YAAA,GAAe,IAAA;AAAA,IACjB,CAAA,MAAO,CAAA,CAAE,KAAA,CAAM,UAAA,GAAa,EAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,gBAAA,CAAiB,aAAa,SAAS,CAAA;AACzD,EAAA,MAAM,UAA2B,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAChE,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,GAAI,EAAE,WAAA,GAAc,EAAE,aAAa,CAAA,CAAE,WAAA,KAAgB,EAAC;AAAA,IACtD,GAAI,EAAE,QAAA,GAAW,EAAE,UAAU,CAAA,CAAE,QAAA,KAAa,EAAC;AAAA,IAC7C,GAAI,EAAE,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM,GAAI,EAAC;AAAA,IAClD,GAAI,CAAA,CAAE,WAAA,CAAY,CAAC,CAAA,GAAI,EAAE,cAAA,EAAgB,CAAA,CAAE,WAAA,CAAY,CAAC,CAAA,EAAE,GAAI;AAAC,GACjE,CAAE,CAAA;AACF,EAAA,IAAI,YAAA,UAAsB,IAAA,CAAK,EAAE,MAAM,YAAA,EAAc,KAAA,EAAO,cAAc,CAAA;AAC1E,EAAA,OAAO,OAAA;AACT;AAUA,eAAe,aAAA,GAIZ;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,UAA2B,EAAC;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,CAAC,EAAE,kBAAA,EAAmB,EAAG,EAAE,aAAA,EAAc,EAAG,EAAE,iBAAA,EAAmB,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MAC3F,OAAO,4BAAmB,CAAA;AAAA,MAC1B,OAAO,oBAA4B,CAAA;AAAA,MACnC,OAAO,iBAAyB;AAAA,KACjC,CAAA;AACD,IAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,MAAM,kBAAA,EAAmB;AACpD,IAAA,IAAI;AAIF,MAAA,MAAM,UAAU,iBAAA,EAAkB;AAClC,MAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,aAAA,EAAc,EAAG,SAAS,CAAA;AACvD,MAAA,MAAM,IAAA,GAAA,CAAQ,MAAM,OAAA,CAAQ,aAAA,EAAc,EAAG,MAAA;AAAA,QAC3C,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,IAAA,CAAK,UAAA,CAAW,OAAO,CAAA,IAAK,CAAC,CAAA,CAAE,IAAA,CAAK,UAAA,CAAW,UAAU;AAAA,OACrE;AACA,MAAA,MAAM,UAAyB,EAAC;AAChC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,IAAI,CAAA,EAAG;AAI3B,QAAA,MAAM,UAAA,GAAa,CAAC,GAAI,MAAM,SAAS,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,IAAI,CAAE,CAAA;AAC3D,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAI,MAAM,YAAA,CAAa,GAAA,CAAI,MAAM,GAAA,CAAI,IAAA,EAAM,UAAA,EAAY,KAAK,CAAE,CAAA;AAC3E,QAAA,KAAA,MAAW,KAAA,IAAS,UAAA,EAAY,OAAA,CAAQ,IAAA,CAAK,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO,KAAA,EAAO,GAAA,CAAI,IAAA,EAAM,CAAA;AAAA,MAC5F;AACA,MAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,IACnC,CAAA,SAAE;AACA,MAAA,KAAA,EAAM;AAAA,IACR;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,IAAA,EAAM,WAAA;AAAA,MACN,SAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG;AAAA,KACzD,CAAA;AACD,IAAA,OAAO,EAAE,OAAA,EAAS,EAAC,EAAG,SAAS,KAAA,EAAM;AAAA,EACvC;AACF;AAYA,eAAe,YAAY,UAAA,EAIxB;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,oBAAA,EAAqB,GAAI,MAAM,OAAO,4BAAmB,CAAA;AACjE,IAAA,MAAM,EAAE,QAAQ,KAAA,EAAM,GAAI,MAAM,oBAAA,CAAqB,EAAE,YAAY,CAAA;AACnE,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,UAAA,EAAW;AACvC,MAAA,MAAM,OAAA,GAA0B,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACjD,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,IAAA,EAAO,EAAE,IAAA,IAAsB,OAAA;AAAA,QAC/B,WAAA,EAAa,EAAE,WAAA,IAAe,EAAA;AAAA,QAC9B,MAAA,EAAQ,CAAA,QAAA,EAAW,CAAA,CAAE,EAAE,CAAA,CAAA;AAAA,QACvB,WAAW,CAAA,CAAE,SAAA;AAAA,QACb,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,YAAY,CAAA,CAAE,UAAA;AAAA,QACd,UAAU,CAAA,CAAE,QAAA;AAAA,QACZ,SAAS,CAAA,CAAE,OAAA;AAAA,QACX,UAAU,EAAC;AAAA,QACX,cAAc;AAAC,OACjB,CAAE,CAAA;AAIF,MAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,MAAM,CAAA;AACxC,MAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,IACnC,CAAA,SAAE;AACA,MAAA,KAAA,EAAM;AAAA,IACR;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,MAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAG3D,IAAA,IAAI,CAAC,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA,EAAG;AACvB,MAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,KAAK,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO,EAAE,OAAA,EAAS,IAAI,OAAA,EAAS,IAAI,KAAA,EAAM;AAAA,EAC3C;AACF;AAGA,eAAe,YAAY,MAAA,EAA2C;AACpE,EAAA,MAAM,KAAM,MAAA,CAA4D,WAAA;AACxE,EAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAO,EAAC;AACtC,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,CAAG,IAAA,CAAK,MAAM,CAAA;AACpC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,UAAU,EAAC;AAC7C;AAcA,eAAe,aACb,EAAA,EAC+D;AAI/D,EAAA,MAAM,eAAA,GAAkB,iBAAA,EAAkB,CAAE,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,CAAE,QAAA,CAAc,IAAA,CAAA,GAAG,CAAA,GAAI,CAAA,GAAI,CAAA,GAAS,IAAA,CAAA,GAAI,CAAA;AAChG,EAAA,MAAM,gBAAgB,CAAC,CAAA,KACrB,eAAA,CAAgB,IAAA,CAAK,CAAC,EAAA,KAAO,CAAA,KAAM,EAAA,CAAG,KAAA,CAAM,GAAG,EAAE,CAAA,IAAK,CAAA,CAAE,UAAA,CAAW,EAAE,CAAC,CAAA;AAExE,EAAA,MAAM,GAAA,GAAA,CAAO,MAAM,EAAA,CAAG,MAAA,EAAO,EAAG,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,aAAA,CAAc,CAAA,CAAE,MAAM,CAAC,CAAA;AACtE,EAAA,MAAM,UAAyB,EAAC;AAChC,EAAA,MAAM,UAA2B,EAAC;AAMlC,EAAA,KAAA,MAAW,EAAE,QAAA,EAAU,IAAA,EAAK,IAAK,EAAA,CAAG,iBAAgB,EAAG;AACrD,IAAA,IAAI,aAAA,CAAc,IAAI,CAAA,EAAG;AACzB,IAAA,MAAM,SAAS,IAAA,CAAK,QAAA,CAAc,IAAA,CAAA,GAAG,CAAA,GAAI,OAAO,IAAA,GAAY,IAAA,CAAA,GAAA;AAC5D,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,IAAA,IAAQ,CAAA,CAAE,MAAA,CAAO,UAAA,CAAW,MAAM,CAAC,CAAA;AAChF,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACxB,IAAA,MAAM,aAAa,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,CAAE,KAAI,IAAK,QAAA;AAGhD,IAAA,OAAA,CAAQ,IAAA,CAAK,GAAI,MAAM,YAAA,CAAa,MAAM,UAAA,EAAY,KAAA,EAAO,KAAK,CAAE,CAAA;AACpE,IAAA,KAAA,MAAW,KAAA,IAAS,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,EAAE,QAAQ,QAAA,EAAU,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,CAAA;AAAA,EACxF;AACA,EAAA,OAAO,EAAE,SAAS,OAAA,EAAQ;AAC5B;AAUA,eAAe,aAAA,GAIZ;AACD,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,UAA2B,EAAC;AAClC,EAAA,IAAI;AACF,IAAA,MAAM,UAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,IAAA,IAAQ,mBAAkB,EAAG;AACtC,MAAA,MAAM,cAAc,CAAC,GAAI,MAAM,QAAA,CAAS,IAAA,EAAM,gBAAgB,CAAE,CAAA;AAChE,MAAA,OAAA,CAAQ,KAAK,GAAI,MAAM,aAAa,IAAA,EAAM,gBAAA,EAAkB,WAAW,CAAE,CAAA;AACzE,MAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,WAAW,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAM;AAAA,EACnC,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,IAAA,EAAM,SAAA;AAAA,MACN,SAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG;AAAA,KACzD,CAAA;AACD,IAAA,OAAO,EAAE,OAAA,EAAS,EAAC,EAAG,SAAS,KAAA,EAAM;AAAA,EACvC;AACF;AAGA,SAAS,GAAA,CAAI,QAAqB,OAAA,EAAwC;AACxE,EAAA,OAAO,QAAQ,GAAA,CAAI,CAAC,WAAW,EAAE,MAAA,EAAQ,OAAM,CAAE,CAAA;AACnD;AASA,eAAsB,gBAAA,CACpB,IACA,UAAA,EAC0B;AAC1B,EAAA,MAAM,CAAC,IAAA,EAAM,KAAK,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,aAAA,EAAc,EAAG,WAAA,CAAY,UAAU,CAAC,CAAC,CAAA;AAClF,EAAA,MAAM,OAAA,GAAU,MAAM,aAAA,EAAc;AACpC,EAAA,MAAM,GAAA,GAAM,MAAM,YAAA,CAAa,EAAE,CAAA;AACjC,EAAA,OAAO;AAAA,IACL,OAAA,EAAS;AAAA,MACP,GAAG,GAAA,CAAI,OAAA;AAAA,MACP,GAAG,IAAA,CAAK,OAAA;AAAA,MACR,GAAG,GAAA,CAAI,SAAA,EAAW,OAAA,CAAQ,OAAO,CAAA;AAAA,MACjC,GAAG,GAAA,CAAI,OAAA,EAAS,KAAA,CAAM,OAAO;AAAA,KAC/B;AAAA;AAAA;AAAA,IAGA,OAAA,EAAS,YAAA,CAAa,CAAC,GAAG,MAAM,OAAA,EAAS,GAAG,GAAA,CAAI,OAAA,EAAS,GAAG,IAAA,CAAK,OAAA,EAAS,GAAG,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,IAC7F,KAAA,EAAO,CAAC,GAAG,IAAA,CAAK,KAAA,EAAO,GAAG,KAAA,CAAM,KAAA,EAAO,GAAG,OAAA,CAAQ,KAAK;AAAA,GACzD;AACF;AAGA,SAAS,aAAa,OAAA,EAA2C;AAC/D,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,MAAM,MAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,IAAI,CAAA,EAAG;AACtB,IAAA,IAAA,CAAK,GAAA,CAAI,EAAE,IAAI,CAAA;AACf,IAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,EACZ;AACA,EAAA,OAAO,GAAA;AACT","file":"asset-feeds-3XCKB542.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}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (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 */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await 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 grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\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: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\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: () => Promise<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 */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ 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 = (await 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(...(await 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). Local disk only, 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 */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await 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 disk only, 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 = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...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
+ {"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-25DVAGVD.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}. */\nasync function loadDomainMds(root: string): Promise<DomainMd[]> {\n const out: DomainMd[] = [];\n for await (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 */\nasync function stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): Promise<CatalogDomain[]> {\n const domainMds = await 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 grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\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: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...(await scanRepo(lib.path, lib.name))];\n domains.push(...(await stampDomains(lib.path, lib.name, libEntries, false)));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\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: () => Promise<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 */\nasync function sourceAssets(\n am: AssetManagerLike,\n): Promise<{ 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 = (await 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(...(await 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). Local disk only, 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 */\nasync function factoryAssets(): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...(await scanRepo(root, \"skaile-factory\"))];\n domains.push(...(await 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 disk only, 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 = await factoryAssets();\n const src = await sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...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,4 +1,4 @@
1
- export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-ZGTM4BOU.js';
1
+ export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName } from '../chunk-CO4DNTIL.js';
2
2
  import '../chunk-3HVASHBF.js';
3
3
  import '../chunk-NU4E7C5I.js';
4
4
  import '../chunk-UKJMQZV6.js';
@@ -1,4 +1,4 @@
1
- import { softDepName } from './chunk-ZGTM4BOU.js';
1
+ import { softDepName } from './chunk-CO4DNTIL.js';
2
2
  import pc from 'picocolors';
3
3
 
4
4
  var S = {
@@ -94,5 +94,5 @@ function formatRelativeTime(date) {
94
94
  }
95
95
 
96
96
  export { KIND_COL_WIDTH, S, bareSoftDepName, colorRef, fitWidth, formatRelativeTime, formatSoftDepAddPreview, formatSoftDepHint, formatSoftDepRemovePreview, formatSoftDepSection, kindColor, kindColorPad, logErr, logInfo, logOk, logWarn };
97
- //# sourceMappingURL=chunk-VBMY3VCW.js.map
98
- //# sourceMappingURL=chunk-VBMY3VCW.js.map
97
+ //# sourceMappingURL=chunk-3IGXVTOS.js.map
98
+ //# sourceMappingURL=chunk-3IGXVTOS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/helpers.ts"],"names":[],"mappings":";;;AAQO,IAAM,CAAA,GAAI;AAAA,EACf,KAAA,EAAO,CAAC,CAAA,KAAc,EAAA,CAAG,KAAK,EAAA,CAAG,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,EACxC,GAAA,EAAK,CAAC,CAAA,KAAc,EAAA,CAAG,KAAK,CAAC,CAAA;AAAA,EAC7B,OAAA,EAAS,CAAC,CAAA,KAAc,EAAA,CAAG,KAAK,CAAC,CAAA;AAAA,EACjC,IAAA,EAAM,CAAC,CAAA,KAAc,EAAA,CAAG,KAAK,CAAC,CAAA;AAAA,EAC9B,GAAA,EAAK,CAAC,CAAA,KAAc,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,EAC5B,EAAA,EAAI,CAAC,CAAA,KAAc,EAAA,CAAG,MAAM,CAAC,CAAA;AAAA,EAC7B,IAAA,EAAM,CAAC,CAAA,KAAc,EAAA,CAAG,OAAO,CAAC,CAAA;AAAA,EAChC,GAAA,EAAK,CAAC,CAAA,KAAc,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,EAC5B,KAAA,EAAO,CAAC,CAAA,KAAc,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,EAC9B,IAAA,EAAM,CAAC,CAAA,GAAI,EAAA,KAAO,GAAG,GAAA,CAAI,QAAA,CAAI,MAAA,CAAO,CAAC,CAAC;AACxC;AAEA,IAAM,WAAA,GAAqD;AAAA,EACzD,OAAO,EAAA,CAAG,IAAA;AAAA,EACV,OAAO,EAAA,CAAG,OAAA;AAAA,EACV,QAAQ,EAAA,CAAG,MAAA;AAAA,EACX,MAAM,EAAA,CAAG,KAAA;AAAA,EACT,QAAQ,EAAA,CAAG,IAAA;AAAA,EACX,UAAU,EAAA,CAAG,GAAA;AAAA,EACb,WAAW,EAAA,CAAG,KAAA;AAAA,EACd,OAAO,EAAA,CAAG,KAAA;AAAA,EACV,WAAW,EAAA,CAAG,IAAA;AAAA,EACd,SAAS,EAAA,CAAG,IAAA;AAAA,EACZ,SAAS,EAAA,CAAG,IAAA;AAAA,EACZ,cAAc,EAAA,CAAG;AACnB,CAAA;AAQO,SAAS,QAAA,CAAS,GAAW,KAAA,EAAuB;AACzD,EAAA,IAAI,KAAA,IAAS,GAAG,OAAO,EAAA;AACvB,EAAA,IAAI,CAAA,CAAE,MAAA,GAAS,KAAA,EAAO,OAAO,KAAA,KAAU,CAAA,GAAI,QAAA,GAAM,CAAA,EAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,KAAA,GAAQ,CAAC,CAAC,CAAA,MAAA,CAAA;AACzE,EAAA,OAAO,CAAA,CAAE,OAAO,KAAK,CAAA;AACvB;AAGO,SAAS,UAAU,IAAA,EAAsB;AAC9C,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,IAAI,CAAA,IAAK,EAAA,CAAG,GAAA;AACxC,EAAA,OAAO,QAAQ,IAAI,CAAA;AACrB;AAMO,IAAM,cAAA,GAAiB;AAOvB,SAAS,YAAA,CAAa,IAAA,EAAc,KAAA,GAAQ,cAAA,EAAwB;AACzE,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,IAAI,CAAA,IAAK,EAAA,CAAG,GAAA;AACxC,EAAA,OAAO,OAAA,CAAQ,QAAA,CAAS,IAAA,EAAM,KAAK,CAAC,CAAA;AACtC;AAGO,SAAS,SAAS,GAAA,EAAqB;AAC5C,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAChC,EAAA,IAAI,QAAA,KAAa,IAAI,OAAO,GAAA;AAC5B,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA;AAC/B,EAAA,OAAO,CAAA,EAAG,SAAA,CAAU,IAAI,CAAC,GAAG,IAAI,CAAA,CAAA;AAClC;AAQO,SAAS,MAAM,GAAA,EAAa;AACjC,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,EAAA,CAAG,KAAA,CAAM,QAAG,CAAC,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AACzC;AAOO,SAAS,OAAO,GAAA,EAAa;AAClC,EAAA,OAAA,CAAQ,KAAA,CAAM,KAAK,EAAA,CAAG,GAAA,CAAI,QAAG,CAAC,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AACzC;AAOO,SAAS,QAAQ,GAAA,EAAa;AACnC,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,EAAA,CAAG,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AAC1C;AAOO,SAAS,QAAQ,GAAA,EAAa;AACnC,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,EAAA,CAAG,GAAA,CAAI,MAAG,CAAC,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AACvC;AAOO,SAAS,gBAAgB,GAAA,EAAqB;AACnD,EAAA,OAAO,YAAY,GAAG,CAAA;AACxB;AAOO,SAAS,kBAAkB,IAAA,EAA4D;AAC5F,EAAA,MAAM,SAAS,IAAA,CAAK,KAAA,GAAQ,CAAA,yBAAA,EAAuB,IAAA,CAAK,KAAK,CAAA,iBAAA,CAAA,GAAsB,EAAA;AACnF,EAAA,OAAO,CAAA,EAAG,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAC,CAAA,YAAA,EAAe,eAAA,CAAgB,IAAA,CAAK,EAAE,CAAC,CAAA,kBAAA,EAAqB,MAAM,CAAA,CAAA;AACxG;AAQO,SAAS,qBAAqB,OAAA,EAA6B;AAChE,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAClC,EAAA,MAAM,MAAM,CAAC,CAAA,EAAA,EAAK,EAAE,KAAA,CAAM,8BAA8B,CAAC,CAAA,CAAA,CAAG,CAAA;AAC5D,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS,GAAA,CAAI,IAAA,CAAK,CAAA,IAAA,EAAO,EAAE,GAAA,CAAI,MAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAA,CAAE,CAAA;AAC9E,EAAA,OAAO,GAAA;AACT;AAOO,SAAS,uBAAA,CAAwB,MAAc,OAAA,EAA2B;AAC/E,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACjC,EAAA,OAAO,CAAA,CAAE,IAAI,CAAA,EAAG,IAAI,eAAe,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,2BAAA,CAA6B,CAAA;AACpF;AAOO,SAAS,0BAAA,CAA2B,MAAc,OAAA,EAA2B;AAClF,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACjC,EAAA,OAAO,CAAA,CAAE,IAAI,CAAA,SAAA,EAAY,IAAI,iBAAiB,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,WAAA,CAAa,CAAA;AAC/E;AAGO,SAAS,mBAAmB,IAAA,EAAoB;AACrD,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,EAAI,GAAI,KAAK,OAAA,EAAQ;AACvC,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,GAAI,CAAA;AACtC,EAAA,IAAI,OAAA,GAAU,IAAI,OAAO,UAAA;AACzB,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA;AACvC,EAAA,IAAI,OAAA,GAAU,EAAA,EAAI,OAAO,CAAA,EAAG,OAAO,CAAA,KAAA,CAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA;AACrC,EAAA,IAAI,KAAA,GAAQ,EAAA,EAAI,OAAO,CAAA,EAAG,KAAK,CAAA,KAAA,CAAA;AAC/B,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,EAAE,CAAA;AAClC,EAAA,OAAO,GAAG,IAAI,CAAA,KAAA,CAAA;AAChB","file":"chunk-VBMY3VCW.js","sourcesContent":["import { softDepName } from \"@skaile/workspaces/asset-manager\";\nimport pc from \"picocolors\";\n\n/**\n * Terminal styling helpers used across all CLI commands.\n *\n * @docLink cli/dev-guide#helpers\n */\nexport const S = {\n brand: (s: string) => pc.cyan(pc.bold(s)),\n cmd: (s: string) => pc.cyan(s),\n heading: (s: string) => pc.bold(s),\n bold: (s: string) => pc.bold(s),\n dim: (s: string) => pc.dim(s),\n ok: (s: string) => pc.green(s),\n warn: (s: string) => pc.yellow(s),\n err: (s: string) => pc.red(s),\n label: (s: string) => pc.dim(s),\n rule: (w = 40) => pc.dim(\"─\".repeat(w)),\n};\n\nconst KIND_COLORS: Record<string, (s: string) => string> = {\n skill: pc.blue,\n agent: pc.magenta,\n prompt: pc.yellow,\n flow: pc.green,\n bundle: pc.cyan,\n contract: pc.red,\n connector: pc.green,\n mount: pc.green,\n knowledge: pc.cyan,\n persona: pc.cyan,\n ruleset: pc.cyan,\n \"mcp-server\": pc.magenta,\n};\n\n/**\n * Fit a plain (ANSI-free) string into exactly `width` columns: truncate with a\n * trailing `…` when too long, pad with spaces when too short. Keeps fixed-width\n * columns from overflowing the row when a value (e.g. an asset name) is wider\n * than its budget.\n */\nexport function fitWidth(s: string, width: number): string {\n if (width <= 0) return \"\";\n if (s.length > width) return width === 1 ? \"…\" : `${s.slice(0, width - 1)}…`;\n return s.padEnd(width);\n}\n\n/** Color-code an asset kind string. */\nexport function kindColor(kind: string): string {\n const colorFn = KIND_COLORS[kind] ?? pc.dim;\n return colorFn(kind);\n}\n\n/**\n * Widest kind label (`mcp-server`); the default kind-column width fits it so\n * long kinds don't shove the rest of the row out of alignment.\n */\nexport const KIND_COL_WIDTH = 10;\n\n/**\n * Color-code an asset kind string fit to exactly `width` columns — pads short\n * kinds, truncates over-long ones (via {@link fitWidth}) so the column can never\n * overflow and break row alignment.\n */\nexport function kindColorPad(kind: string, width = KIND_COL_WIDTH): string {\n const colorFn = KIND_COLORS[kind] ?? pc.dim;\n return colorFn(fitWidth(kind, width));\n}\n\n/** Color-code a \"kind:name\" reference string. */\nexport function colorRef(ref: string): string {\n const colonIdx = ref.indexOf(\":\");\n if (colonIdx === -1) return ref;\n const kind = ref.slice(0, colonIdx);\n const rest = ref.slice(colonIdx);\n return `${kindColor(kind)}${rest}`;\n}\n\n/**\n * Print a success line prefixed with a green check mark.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logOk(msg: string) {\n console.log(` ${pc.green(\"✓\")} ${msg}`);\n}\n/**\n * Print an error line prefixed with a red cross to stderr.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logErr(msg: string) {\n console.error(` ${pc.red(\"✗\")} ${msg}`);\n}\n/**\n * Print a warning line prefixed with a yellow exclamation mark.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logWarn(msg: string) {\n console.log(` ${pc.yellow(\"!\")} ${msg}`);\n}\n/**\n * Print an info line prefixed with a dim dot.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logInfo(msg: string) {\n console.log(` ${pc.dim(\"·\")} ${msg}`);\n}\n\n/**\n * Bare asset name of a canonical soft-dep target ref (`@pub/name@ver` → `name`),\n * for compact rendering in `info` / `tree` / `why`. Delegates to the asset-manager\n * parser (the soft-dep read surface) so the two never diverge.\n */\nexport function bareSoftDepName(ref: string): string {\n return softDepName(ref);\n}\n\n/**\n * The report-only soft-dep hint line shared by the `add` no-TTY branch and the\n * `install` surface, so the two user-facing wordings can't drift. Appends a\n * runnable `skaile add` suffix only when the target has a parseable ref.\n */\nexport function formatSoftDepHint(edge: { from: string; to: string; toRef?: string }): string {\n const suffix = edge.toRef ? ` — run \\`skaile add ${edge.toRef}\\` to include it.` : \"\";\n return `${bareSoftDepName(edge.from)} references ${bareSoftDepName(edge.to)} (soft dependency)${suffix}`;\n}\n\n/**\n * Build the \"Soft dependencies (inferred)\" section for `skaile info`. Returns one\n * header line plus one line per soft-dep target, each marked `(soft)` to read\n * differently from a typed `requires`. Empty array when there are no soft deps,\n * so the caller prints nothing.\n */\nexport function formatSoftDepSection(targets: string[]): string[] {\n if (targets.length === 0) return [];\n const out = [` ${S.label(\"Soft dependencies (inferred)\")}:`];\n for (const t of targets) out.push(` ${S.dim(\"·\")} ${t} ${S.dim(\"(soft)\")}`);\n return out;\n}\n\n/**\n * Footer line previewing the soft deps that *adding* the highlighted asset would\n * reference (manage TUI). \"references\" + \"not auto-added\" makes clear the pull is\n * report-only. Empty string when there are no targets, so the caller clears.\n */\nexport function formatSoftDepAddPreview(name: string, targets: string[]): string {\n if (targets.length === 0) return \"\";\n return S.dim(`${name} references ${targets.join(\", \")} (soft dep, not auto-added)`);\n}\n\n/**\n * Footer line previewing the soft deps that *removing* the highlighted asset\n * would orphan (manage TUI) — symmetric with {@link formatSoftDepAddPreview}.\n * Empty string when nothing would be orphaned, so the caller clears.\n */\nexport function formatSoftDepRemovePreview(name: string, targets: string[]): string {\n if (targets.length === 0) return \"\";\n return S.dim(`removing ${name} would orphan ${targets.join(\", \")} (soft dep)`);\n}\n\n/** Format a Date as a human-readable relative time string (e.g. \"5m ago\"). */\nexport function formatRelativeTime(date: Date): string {\n const diff = Date.now() - date.getTime();\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return \"just now\";\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n"]}
1
+ {"version":3,"sources":["../cli/src/helpers.ts"],"names":[],"mappings":"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","file":"chunk-3IGXVTOS.js","sourcesContent":["import { softDepName } from \"@skaile/workspaces/asset-manager\";\nimport pc from \"picocolors\";\n\n/**\n * Terminal styling helpers used across all CLI commands.\n *\n * @docLink cli/dev-guide#helpers\n */\nexport const S = {\n brand: (s: string) => pc.cyan(pc.bold(s)),\n cmd: (s: string) => pc.cyan(s),\n heading: (s: string) => pc.bold(s),\n bold: (s: string) => pc.bold(s),\n dim: (s: string) => pc.dim(s),\n ok: (s: string) => pc.green(s),\n warn: (s: string) => pc.yellow(s),\n err: (s: string) => pc.red(s),\n label: (s: string) => pc.dim(s),\n rule: (w = 40) => pc.dim(\"─\".repeat(w)),\n};\n\nconst KIND_COLORS: Record<string, (s: string) => string> = {\n skill: pc.blue,\n agent: pc.magenta,\n prompt: pc.yellow,\n flow: pc.green,\n bundle: pc.cyan,\n contract: pc.red,\n connector: pc.green,\n mount: pc.green,\n knowledge: pc.cyan,\n persona: pc.cyan,\n ruleset: pc.cyan,\n \"mcp-server\": pc.magenta,\n};\n\n/**\n * Fit a plain (ANSI-free) string into exactly `width` columns: truncate with a\n * trailing `…` when too long, pad with spaces when too short. Keeps fixed-width\n * columns from overflowing the row when a value (e.g. an asset name) is wider\n * than its budget.\n */\nexport function fitWidth(s: string, width: number): string {\n if (width <= 0) return \"\";\n if (s.length > width) return width === 1 ? \"…\" : `${s.slice(0, width - 1)}…`;\n return s.padEnd(width);\n}\n\n/** Color-code an asset kind string. */\nexport function kindColor(kind: string): string {\n const colorFn = KIND_COLORS[kind] ?? pc.dim;\n return colorFn(kind);\n}\n\n/**\n * Widest kind label (`mcp-server`); the default kind-column width fits it so\n * long kinds don't shove the rest of the row out of alignment.\n */\nexport const KIND_COL_WIDTH = 10;\n\n/**\n * Color-code an asset kind string fit to exactly `width` columns — pads short\n * kinds, truncates over-long ones (via {@link fitWidth}) so the column can never\n * overflow and break row alignment.\n */\nexport function kindColorPad(kind: string, width = KIND_COL_WIDTH): string {\n const colorFn = KIND_COLORS[kind] ?? pc.dim;\n return colorFn(fitWidth(kind, width));\n}\n\n/** Color-code a \"kind:name\" reference string. */\nexport function colorRef(ref: string): string {\n const colonIdx = ref.indexOf(\":\");\n if (colonIdx === -1) return ref;\n const kind = ref.slice(0, colonIdx);\n const rest = ref.slice(colonIdx);\n return `${kindColor(kind)}${rest}`;\n}\n\n/**\n * Print a success line prefixed with a green check mark.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logOk(msg: string) {\n console.log(` ${pc.green(\"✓\")} ${msg}`);\n}\n/**\n * Print an error line prefixed with a red cross to stderr.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logErr(msg: string) {\n console.error(` ${pc.red(\"✗\")} ${msg}`);\n}\n/**\n * Print a warning line prefixed with a yellow exclamation mark.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logWarn(msg: string) {\n console.log(` ${pc.yellow(\"!\")} ${msg}`);\n}\n/**\n * Print an info line prefixed with a dim dot.\n *\n * @param msg - Message to display.\n * @docLink cli/dev-guide#helpers\n */\nexport function logInfo(msg: string) {\n console.log(` ${pc.dim(\"·\")} ${msg}`);\n}\n\n/**\n * Bare asset name of a canonical soft-dep target ref (`@pub/name@ver` → `name`),\n * for compact rendering in `info` / `tree` / `why`. Delegates to the asset-manager\n * parser (the soft-dep read surface) so the two never diverge.\n */\nexport function bareSoftDepName(ref: string): string {\n return softDepName(ref);\n}\n\n/**\n * The report-only soft-dep hint line shared by the `add` no-TTY branch and the\n * `install` surface, so the two user-facing wordings can't drift. Appends a\n * runnable `skaile add` suffix only when the target has a parseable ref.\n */\nexport function formatSoftDepHint(edge: { from: string; to: string; toRef?: string }): string {\n const suffix = edge.toRef ? ` — run \\`skaile add ${edge.toRef}\\` to include it.` : \"\";\n return `${bareSoftDepName(edge.from)} references ${bareSoftDepName(edge.to)} (soft dependency)${suffix}`;\n}\n\n/**\n * Build the \"Soft dependencies (inferred)\" section for `skaile info`. Returns one\n * header line plus one line per soft-dep target, each marked `(soft)` to read\n * differently from a typed `requires`. Empty array when there are no soft deps,\n * so the caller prints nothing.\n */\nexport function formatSoftDepSection(targets: string[]): string[] {\n if (targets.length === 0) return [];\n const out = [` ${S.label(\"Soft dependencies (inferred)\")}:`];\n for (const t of targets) out.push(` ${S.dim(\"·\")} ${t} ${S.dim(\"(soft)\")}`);\n return out;\n}\n\n/**\n * Footer line previewing the soft deps that *adding* the highlighted asset would\n * reference (manage TUI). \"references\" + \"not auto-added\" makes clear the pull is\n * report-only. Empty string when there are no targets, so the caller clears.\n */\nexport function formatSoftDepAddPreview(name: string, targets: string[]): string {\n if (targets.length === 0) return \"\";\n return S.dim(`${name} references ${targets.join(\", \")} (soft dep, not auto-added)`);\n}\n\n/**\n * Footer line previewing the soft deps that *removing* the highlighted asset\n * would orphan (manage TUI) — symmetric with {@link formatSoftDepAddPreview}.\n * Empty string when nothing would be orphaned, so the caller clears.\n */\nexport function formatSoftDepRemovePreview(name: string, targets: string[]): string {\n if (targets.length === 0) return \"\";\n return S.dim(`removing ${name} would orphan ${targets.join(\", \")} (soft dep)`);\n}\n\n/** Format a Date as a human-readable relative time string (e.g. \"5m ago\"). */\nexport function formatRelativeTime(date: Date): string {\n const diff = Date.now() - date.getTime();\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return \"just now\";\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n"]}
@@ -22,15 +22,19 @@ var FlowExpressionError = class extends Error {
22
22
  name = "FlowExpressionError";
23
23
  /** Runtime path that was grammar-valid but absent from the supplied context. */
24
24
  missingPath;
25
- constructor(message, missingPath) {
25
+ /** Producer node whose `skipped` status is why {@link missingPath} is absent. */
26
+ skippedProducer;
27
+ constructor(message, missingPath, skippedProducer) {
26
28
  super(message);
27
29
  this.missingPath = missingPath;
30
+ this.skippedProducer = skippedProducer;
28
31
  }
29
32
  };
30
- function expressionError(message, position, missingPath) {
33
+ function expressionError(message, position, missingPath, skippedProducer) {
31
34
  return new FlowExpressionError(
32
35
  position === void 0 ? `Flow expression ${message}` : `Flow expression ${message} at ${position}`,
33
- missingPath
36
+ missingPath,
37
+ skippedProducer
34
38
  );
35
39
  }
36
40
  function parseString(source, start) {
@@ -159,6 +163,7 @@ function resolvePath(raw, context, position, traverse) {
159
163
  if (forbidden) throw expressionError(`contains forbidden path segment ${forbidden}`, position);
160
164
  let value;
161
165
  let remaining;
166
+ let skippedProducer;
162
167
  if (segments[0] === "flow" && segments[1] === "input") {
163
168
  if (segments.length === 2) {
164
169
  throw expressionError("path requires a JSON property suffix", position);
@@ -175,12 +180,23 @@ function resolvePath(raw, context, position, traverse) {
175
180
  if (segments.length === 4) {
176
181
  throw expressionError("path requires a JSON property suffix", position);
177
182
  }
183
+ if (Object.hasOwn(context.nodes, segments[1]) && context.nodes[segments[1]].status === "skipped") {
184
+ skippedProducer = segments[1];
185
+ }
178
186
  value = context.nodes;
179
187
  remaining = segments.slice(1);
180
188
  } else {
181
189
  throw expressionError(`contains unsupported path ${raw}`, position);
182
190
  }
183
191
  if (!traverse) return void 0;
192
+ if (skippedProducer !== void 0) {
193
+ throw expressionError(
194
+ `cannot read ${raw}: producer node ${skippedProducer} was skipped and produced no output`,
195
+ position,
196
+ raw,
197
+ skippedProducer
198
+ );
199
+ }
184
200
  for (const segment of remaining) value = ownValue(value, segment, position, raw);
185
201
  return value;
186
202
  }
@@ -1252,5 +1268,5 @@ function parseFlowDefinition(data) {
1252
1268
  }
1253
1269
 
1254
1270
  export { FLOW_EXPRESSION_LIMITS, FlowDefinitionSchema, FlowExpressionError, collectFlowBindingExpressions, evaluate, evaluateFlowExpression, inspectFlowExpression, normalizeV1, parseFlowDefinition, resolveFlowBindings, stableFlowJson };
1255
- //# sourceMappingURL=chunk-5QGMEPUX.js.map
1256
- //# sourceMappingURL=chunk-5QGMEPUX.js.map
1271
+ //# sourceMappingURL=chunk-B4J6TV4G.js.map
1272
+ //# sourceMappingURL=chunk-B4J6TV4G.js.map