@open-mercato/cezar 0.1.5-pr506.357

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 (264) hide show
  1. package/README.md +573 -0
  2. package/dist/config.d.ts +144 -0
  3. package/dist/config.js +106 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/core/agent-runner.d.ts +169 -0
  6. package/dist/core/agent-runner.js +23 -0
  7. package/dist/core/agent-runner.js.map +1 -0
  8. package/dist/core/backend-detect.d.ts +16 -0
  9. package/dist/core/backend-detect.js +118 -0
  10. package/dist/core/backend-detect.js.map +1 -0
  11. package/dist/core/claude-cli-runner.d.ts +56 -0
  12. package/dist/core/claude-cli-runner.js +416 -0
  13. package/dist/core/claude-cli-runner.js.map +1 -0
  14. package/dist/core/claude-ui-mapper.d.ts +71 -0
  15. package/dist/core/claude-ui-mapper.js +567 -0
  16. package/dist/core/claude-ui-mapper.js.map +1 -0
  17. package/dist/core/codex-app-server-runner.d.ts +29 -0
  18. package/dist/core/codex-app-server-runner.js +489 -0
  19. package/dist/core/codex-app-server-runner.js.map +1 -0
  20. package/dist/core/codex-ui-mapper.d.ts +63 -0
  21. package/dist/core/codex-ui-mapper.js +491 -0
  22. package/dist/core/codex-ui-mapper.js.map +1 -0
  23. package/dist/core/ndjson.d.ts +6 -0
  24. package/dist/core/ndjson.js +23 -0
  25. package/dist/core/ndjson.js.map +1 -0
  26. package/dist/core/opencode-server-runner.d.ts +30 -0
  27. package/dist/core/opencode-server-runner.js +488 -0
  28. package/dist/core/opencode-server-runner.js.map +1 -0
  29. package/dist/core/opencode-ui-mapper.d.ts +105 -0
  30. package/dist/core/opencode-ui-mapper.js +588 -0
  31. package/dist/core/opencode-ui-mapper.js.map +1 -0
  32. package/dist/core/process-usage.d.ts +63 -0
  33. package/dist/core/process-usage.js +181 -0
  34. package/dist/core/process-usage.js.map +1 -0
  35. package/dist/core/runner-factory.d.ts +8 -0
  36. package/dist/core/runner-factory.js +22 -0
  37. package/dist/core/runner-factory.js.map +1 -0
  38. package/dist/core/tool-display.d.ts +30 -0
  39. package/dist/core/tool-display.js +156 -0
  40. package/dist/core/tool-display.js.map +1 -0
  41. package/dist/core/ui-events.d.ts +308 -0
  42. package/dist/core/ui-events.js +25 -0
  43. package/dist/core/ui-events.js.map +1 -0
  44. package/dist/core/usage.d.ts +11 -0
  45. package/dist/core/usage.js +15 -0
  46. package/dist/core/usage.js.map +1 -0
  47. package/dist/git-worktree.d.ts +88 -0
  48. package/dist/git-worktree.js +297 -0
  49. package/dist/git-worktree.js.map +1 -0
  50. package/dist/handoff.d.ts +62 -0
  51. package/dist/handoff.js +147 -0
  52. package/dist/handoff.js.map +1 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.js +466 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/install-as-command.d.ts +39 -0
  57. package/dist/install-as-command.js +51 -0
  58. package/dist/install-as-command.js.map +1 -0
  59. package/dist/pack-check.d.ts +17 -0
  60. package/dist/pack-check.js +27 -0
  61. package/dist/pack-check.js.map +1 -0
  62. package/dist/paths.d.ts +23 -0
  63. package/dist/paths.js +34 -0
  64. package/dist/paths.js.map +1 -0
  65. package/dist/planner.d.ts +17 -0
  66. package/dist/planner.js +248 -0
  67. package/dist/planner.js.map +1 -0
  68. package/dist/release/snapshot.d.ts +64 -0
  69. package/dist/release/snapshot.js +75 -0
  70. package/dist/release/snapshot.js.map +1 -0
  71. package/dist/release/stable.d.ts +46 -0
  72. package/dist/release/stable.js +57 -0
  73. package/dist/release/stable.js.map +1 -0
  74. package/dist/runs/auto-name.d.ts +75 -0
  75. package/dist/runs/auto-name.js +168 -0
  76. package/dist/runs/auto-name.js.map +1 -0
  77. package/dist/runs/retention.d.ts +60 -0
  78. package/dist/runs/retention.js +86 -0
  79. package/dist/runs/retention.js.map +1 -0
  80. package/dist/runs/review-gate.d.ts +18 -0
  81. package/dist/runs/review-gate.js +21 -0
  82. package/dist/runs/review-gate.js.map +1 -0
  83. package/dist/runs/store.d.ts +408 -0
  84. package/dist/runs/store.js +526 -0
  85. package/dist/runs/store.js.map +1 -0
  86. package/dist/runs/task-refs.d.ts +24 -0
  87. package/dist/runs/task-refs.js +74 -0
  88. package/dist/runs/task-refs.js.map +1 -0
  89. package/dist/runs/ui-event-sink.d.ts +81 -0
  90. package/dist/runs/ui-event-sink.js +210 -0
  91. package/dist/runs/ui-event-sink.js.map +1 -0
  92. package/dist/server/capabilities.d.ts +33 -0
  93. package/dist/server/capabilities.js +43 -0
  94. package/dist/server/capabilities.js.map +1 -0
  95. package/dist/server/forge/github.d.ts +70 -0
  96. package/dist/server/forge/github.js +375 -0
  97. package/dist/server/forge/github.js.map +1 -0
  98. package/dist/server/forge/index.d.ts +24 -0
  99. package/dist/server/forge/index.js +45 -0
  100. package/dist/server/forge/index.js.map +1 -0
  101. package/dist/server/forge/types.d.ts +80 -0
  102. package/dist/server/forge/types.js +2 -0
  103. package/dist/server/forge/types.js.map +1 -0
  104. package/dist/server/git-changes.d.ts +195 -0
  105. package/dist/server/git-changes.js +497 -0
  106. package/dist/server/git-changes.js.map +1 -0
  107. package/dist/server/git.d.ts +26 -0
  108. package/dist/server/git.js +103 -0
  109. package/dist/server/git.js.map +1 -0
  110. package/dist/server/github.d.ts +8 -0
  111. package/dist/server/github.js +8 -0
  112. package/dist/server/github.js.map +1 -0
  113. package/dist/server/launch-key.d.ts +7 -0
  114. package/dist/server/launch-key.js +33 -0
  115. package/dist/server/launch-key.js.map +1 -0
  116. package/dist/server/open-in-app.d.ts +51 -0
  117. package/dist/server/open-in-app.js +189 -0
  118. package/dist/server/open-in-app.js.map +1 -0
  119. package/dist/server/open-in-terminal.d.ts +25 -0
  120. package/dist/server/open-in-terminal.js +120 -0
  121. package/dist/server/open-in-terminal.js.map +1 -0
  122. package/dist/server/pr.d.ts +7 -0
  123. package/dist/server/pr.js +7 -0
  124. package/dist/server/pr.js.map +1 -0
  125. package/dist/server/server.d.ts +41 -0
  126. package/dist/server/server.js +1390 -0
  127. package/dist/server/server.js.map +1 -0
  128. package/dist/server/static-ui.d.ts +49 -0
  129. package/dist/server/static-ui.js +92 -0
  130. package/dist/server/static-ui.js.map +1 -0
  131. package/dist/server/wsl.d.ts +25 -0
  132. package/dist/server/wsl.js +92 -0
  133. package/dist/server/wsl.js.map +1 -0
  134. package/dist/server-install/engine.d.ts +33 -0
  135. package/dist/server-install/engine.js +314 -0
  136. package/dist/server-install/engine.js.map +1 -0
  137. package/dist/server-install/platforms/macosx-ngrok.d.ts +6 -0
  138. package/dist/server-install/platforms/macosx-ngrok.js +345 -0
  139. package/dist/server-install/platforms/macosx-ngrok.js.map +1 -0
  140. package/dist/server-install/platforms/ubuntu-vps.d.ts +39 -0
  141. package/dist/server-install/platforms/ubuntu-vps.js +746 -0
  142. package/dist/server-install/platforms/ubuntu-vps.js.map +1 -0
  143. package/dist/server-install/state.d.ts +26 -0
  144. package/dist/server-install/state.js +125 -0
  145. package/dist/server-install/state.js.map +1 -0
  146. package/dist/server-install/steps.d.ts +109 -0
  147. package/dist/server-install/steps.js +330 -0
  148. package/dist/server-install/steps.js.map +1 -0
  149. package/dist/server-install/strategies.d.ts +4 -0
  150. package/dist/server-install/strategies.js +20 -0
  151. package/dist/server-install/strategies.js.map +1 -0
  152. package/dist/server-install/types.d.ts +597 -0
  153. package/dist/server-install/types.js +98 -0
  154. package/dist/server-install/types.js.map +1 -0
  155. package/dist/server-install/ui.d.ts +42 -0
  156. package/dist/server-install/ui.js +130 -0
  157. package/dist/server-install/ui.js.map +1 -0
  158. package/dist/skills-banner.d.ts +21 -0
  159. package/dist/skills-banner.js +40 -0
  160. package/dist/skills-banner.js.map +1 -0
  161. package/dist/skills-remote.d.ts +35 -0
  162. package/dist/skills-remote.js +266 -0
  163. package/dist/skills-remote.js.map +1 -0
  164. package/dist/skills.d.ts +45 -0
  165. package/dist/skills.js +182 -0
  166. package/dist/skills.js.map +1 -0
  167. package/dist/todos.d.ts +63 -0
  168. package/dist/todos.js +168 -0
  169. package/dist/todos.js.map +1 -0
  170. package/dist/ui-state.d.ts +12 -0
  171. package/dist/ui-state.js +25 -0
  172. package/dist/ui-state.js.map +1 -0
  173. package/dist/update-check.d.ts +12 -0
  174. package/dist/update-check.js +41 -0
  175. package/dist/update-check.js.map +1 -0
  176. package/dist/workflows/load.d.ts +15 -0
  177. package/dist/workflows/load.js +56 -0
  178. package/dist/workflows/load.js.map +1 -0
  179. package/dist/workflows/run.d.ts +281 -0
  180. package/dist/workflows/run.js +1511 -0
  181. package/dist/workflows/run.js.map +1 -0
  182. package/dist/workflows/types.d.ts +307 -0
  183. package/dist/workflows/types.js +171 -0
  184. package/dist/workflows/types.js.map +1 -0
  185. package/package.json +90 -0
  186. package/scripts/check-pack.mjs +46 -0
  187. package/scripts/dev.mjs +74 -0
  188. package/scripts/install-as-command.mjs +120 -0
  189. package/scripts/mock-claude.mjs +310 -0
  190. package/scripts/release-snapshot.mjs +130 -0
  191. package/scripts/release.mjs +119 -0
  192. package/scripts/test-process-usage.mjs +47 -0
  193. package/web/dist/assets/arrow-left-BE957rJs.js +1 -0
  194. package/web/dist/assets/bundle-mjs-DLPpRFyl.js +1 -0
  195. package/web/dist/assets/centered-state-BddhEEtO.js +43 -0
  196. package/web/dist/assets/chunk-BO2N2NFS-f5igTtpB.js +131 -0
  197. package/web/dist/assets/command-CjhFbABo.js +1 -0
  198. package/web/dist/assets/compare-variants-Cs52IAEG.js +1 -0
  199. package/web/dist/assets/core-BuMqAF_1.js +12 -0
  200. package/web/dist/assets/css-BsVw1vtW.js +1 -0
  201. package/web/dist/assets/dialog-DFvXTkFv.js +1 -0
  202. package/web/dist/assets/diff-view-C9b8AITq.js +4 -0
  203. package/web/dist/assets/diff-woXpYk--.js +1 -0
  204. package/web/dist/assets/dist-FBs4wmIY.js +1 -0
  205. package/web/dist/assets/dist-o_kVMXjg.js +1 -0
  206. package/web/dist/assets/engine-javascript-FQvzRocB.js +141 -0
  207. package/web/dist/assets/file-BVuztAOd.js +1 -0
  208. package/web/dist/assets/folder-0pJ1NZ_S.js +1 -0
  209. package/web/dist/assets/git-pull-request-DJkAMT0f.js +1 -0
  210. package/web/dist/assets/git-toolbar-CwhQdx3J.js +1 -0
  211. package/web/dist/assets/github-D0rAX4-r.js +1 -0
  212. package/web/dist/assets/go-rLFTqkRN.js +1 -0
  213. package/web/dist/assets/highlighted-body-OFNGDK62-DGHHie_q.js +1 -0
  214. package/web/dist/assets/highlighter-BKVEsACy.js +3 -0
  215. package/web/dist/assets/html-BY76lXLH.js +1 -0
  216. package/web/dist/assets/index-CEkI7QxN.css +2 -0
  217. package/web/dist/assets/index-CO2lH2et.js +11 -0
  218. package/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  219. package/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  220. package/web/dist/assets/javascript-BgS3c2Ky.js +1 -0
  221. package/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  222. package/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  223. package/web/dist/assets/json-qhed-kSA.js +1 -0
  224. package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
  225. package/web/dist/assets/jsx-Bz0zcwM4.js +1 -0
  226. package/web/dist/assets/lib-CNGa3Dig.js +1 -0
  227. package/web/dist/assets/loader-circle-BV3DO9mE.js +1 -0
  228. package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
  229. package/web/dist/assets/markdown-D4-ADgH9.js +1 -0
  230. package/web/dist/assets/mermaid-GHXKKRXX-DM2VsGBG.js +1 -0
  231. package/web/dist/assets/open-mercato-toBr6SOa.svg +11 -0
  232. package/web/dist/assets/prompt-templates-C7ptKvDd.js +15 -0
  233. package/web/dist/assets/python-gzcpVVnB.js +1 -0
  234. package/web/dist/assets/react-dom-Ddtik4qM.js +1 -0
  235. package/web/dist/assets/refresh-cw-DCa-Rlwz.js +1 -0
  236. package/web/dist/assets/repo-git-DIhryGDx.js +1 -0
  237. package/web/dist/assets/run-diff-Dyow8B1q.js +3 -0
  238. package/web/dist/assets/run-header-jx9-18b8.js +1 -0
  239. package/web/dist/assets/rust-Cfkwpbl8.js +1 -0
  240. package/web/dist/assets/search-x-CoCyl7zT.js +1 -0
  241. package/web/dist/assets/shellscript-CLZ0U2zV.js +1 -0
  242. package/web/dist/assets/skill-detail-DB7x_FQp.js +1 -0
  243. package/web/dist/assets/skills-98vCroU8.js +1 -0
  244. package/web/dist/assets/skills-BKIoqD6K.js +1 -0
  245. package/web/dist/assets/sql-BsFa4tDR.js +1 -0
  246. package/web/dist/assets/square-terminal-B6PZX4Qp.js +1 -0
  247. package/web/dist/assets/tab-link-C5--YMQd.js +1 -0
  248. package/web/dist/assets/task-changes-ejxPGVJw.js +1 -0
  249. package/web/dist/assets/task-commits-C6GO5qT-.js +1 -0
  250. package/web/dist/assets/task-files-CatpCBXa.js +2 -0
  251. package/web/dist/assets/task-thread-BMbXDM3d.js +5 -0
  252. package/web/dist/assets/tasks-table-dgezlCcg.js +1 -0
  253. package/web/dist/assets/trash-2-CDtxmmc8.js +1 -0
  254. package/web/dist/assets/triangle-alert-6su4Js5O.js +1 -0
  255. package/web/dist/assets/tsx-udAQXfEw.js +1 -0
  256. package/web/dist/assets/typescript-bsJCZSQ-.js +1 -0
  257. package/web/dist/assets/upload-Bp7hQxll.js +1 -0
  258. package/web/dist/assets/use-desktop-CvTwojOJ.js +3 -0
  259. package/web/dist/assets/utils-rQGbOrwc.js +1 -0
  260. package/web/dist/assets/workflows-Dy2c6790.js +11 -0
  261. package/web/dist/assets/x-Br_jIDBw.js +1 -0
  262. package/web/dist/assets/yaml-rwi0_p6S.js +1 -0
  263. package/web/dist/index.html +70 -0
  264. package/web/open-mercato.svg +11 -0
@@ -0,0 +1,98 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Contracts for the server installer (spec 2026-07-16-server-installer).
4
+ * The engine closes over `InstallStep` / `PlatformStrategy` and never learns
5
+ * what a step *does* — only `check` / `run` / `undo`. That seam is what makes
6
+ * "use ubuntu as a selector and run it that way" a registry lookup, and what
7
+ * makes the interactive helpers reusable across every future platform.
8
+ */
9
+ /** The platforms the registry knows. Extend here + add a strategy file. */
10
+ export const PLATFORM_IDS = ['ubuntu-vps', 'macosx-ngrok'];
11
+ /** Per-step lifecycle. `failed` resumes identically to `pending`. */
12
+ export const STEP_STATUSES = ['pending', 'done', 'skipped', 'failed'];
13
+ /**
14
+ * One thing a step created, tagged by who owns its removal:
15
+ * - `owned` — cezar authored it and nothing else uses it → uninstall removes it.
16
+ * - `shared` — a system tool the operator may now depend on (gh, agent CLIs,
17
+ * certbot + its renewal timer, the cert itself) → uninstall lists it with a
18
+ * manual removal hint instead of yanking it.
19
+ */
20
+ export const stepArtifactSchema = z.object({
21
+ kind: z.enum(['owned', 'shared']),
22
+ /** file | package | service | cert | htpasswd | config | note */
23
+ type: z.string().min(1),
24
+ /** Filesystem path for `owned` files (vhost, unit, htpasswd). */
25
+ path: z.string().optional(),
26
+ /** Human name: package id, unit name, cert domain, auth user. */
27
+ name: z.string().optional(),
28
+ /** `system` | `user` for services. */
29
+ scope: z.string().optional(),
30
+ /** For `shared` artifacts: the exact command the operator can run to remove it. */
31
+ removeHint: z.string().optional(),
32
+ });
33
+ /** What a step returns from `run()`; `undo()` receives it back verbatim. */
34
+ export const stepCreatedSchema = z
35
+ .object({ artifacts: z.array(stepArtifactSchema).default([]) })
36
+ .nullable();
37
+ /**
38
+ * Persisted per-step outcome — the `steps` map in `server.json`. A status this
39
+ * version doesn't know (written by a newer cezar) degrades to `failed`, which
40
+ * keeps the record AND keeps it on uninstall's undo path — never to a parse
41
+ * failure that would discard the whole ledger.
42
+ */
43
+ export const stepOutcomeSchema = z.object({
44
+ status: z.enum(STEP_STATUSES).catch('failed'),
45
+ created: stepCreatedSchema.optional().catch(null),
46
+ });
47
+ /**
48
+ * `~/.cezar/server.json` — host-level, install-once. Additive-safe: every new
49
+ * field is optional / defaulted so an older cezar still parses a newer file
50
+ * (BACKWARD_COMPATIBILITY cross-version-state rule). No secrets live here.
51
+ */
52
+ export const serverStateSchema = z
53
+ .object({
54
+ schema: z.literal(1).catch(1),
55
+ /**
56
+ * Free string, not an enum: a `server.json` written by a newer cezar with a
57
+ * platform this version doesn't ship must still parse — the registry lookup
58
+ * is where "unknown platform" is decided, with the ledger intact.
59
+ */
60
+ platform: z.string().min(1).optional().catch(undefined),
61
+ /** Flips true only when every required step is `done`. */
62
+ installed: z.boolean().default(false).catch(false),
63
+ /** True when this record was written by a CEZ_DRY_RUN preview — a real
64
+ * install/uninstall treats it as no record at all (self-healing). */
65
+ dryRun: z.boolean().optional().catch(undefined),
66
+ /** ISO stamp set by the caller (Date.now is unavailable in some contexts). */
67
+ createdAt: z.string().optional().catch(undefined),
68
+ updatedAt: z.string().optional().catch(undefined),
69
+ primaryPort: z.number().int().positive().default(4321).catch(4321),
70
+ /** Public URL / identity user surfaced at the end — display only. */
71
+ publicUrl: z.string().optional().catch(undefined),
72
+ /** macOS+ngrok free tier: the tunnel URL changes across restarts. */
73
+ ephemeral: z.boolean().optional().catch(undefined),
74
+ // A single malformed entry degrades to a `failed` record (undo still runs
75
+ // from constants), never to losing every other step's ledger.
76
+ steps: z
77
+ .record(z.string(), stepOutcomeSchema.catch({ status: 'failed', created: null }))
78
+ .default({})
79
+ .catch({}),
80
+ })
81
+ // Unknown top-level fields written by a newer cezar survive a load+save
82
+ // round-trip — the file's own additive-safe contract.
83
+ .passthrough();
84
+ /** Freshly-initialized state (no install yet). */
85
+ export function freshServerState() {
86
+ return serverStateSchema.parse({});
87
+ }
88
+ /**
89
+ * The interactive surface a step talks to. Implemented by `ui.ts` over
90
+ * `@clack/prompts`; declared here as a pure interface so `types.ts` (and the
91
+ * engine) never import the TUI library. Prompt methods resolve to the
92
+ * `CANCEL` sentinel instead of throwing when the user aborts.
93
+ */
94
+ export const CANCEL = Symbol('cezar.ui.cancel');
95
+ /** Thrown by `preflight` to stop with a clean, user-facing reason (no stack). */
96
+ export class PreflightError extends Error {
97
+ }
98
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server-install/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import * as clack from '@clack/prompts';
2
+ import { type Ui } from './types.js';
3
+ /**
4
+ * The interactive surface, implemented over `@clack/prompts` — the one place
5
+ * the TUI library is imported. `types.ts`, the engine and the steps talk to
6
+ * the `Ui` interface only, so `@clack/prompts` never enters the server/runtime
7
+ * import graph (AGENTS.md keeps that stack tiny).
8
+ *
9
+ * Every prompt maps clack's cancel symbol to the `CANCEL` sentinel instead of
10
+ * throwing, so a Ctrl-C mid-install is a value the engine can persist-and-exit
11
+ * on, not an exception.
12
+ */
13
+ /** The subset of `@clack/prompts` the UI uses — injectable for unit tests. */
14
+ export interface PromptBackend {
15
+ intro: typeof clack.intro;
16
+ outro: typeof clack.outro;
17
+ note: typeof clack.note;
18
+ log: typeof clack.log;
19
+ select: typeof clack.select;
20
+ multiselect: typeof clack.multiselect;
21
+ confirm: typeof clack.confirm;
22
+ text: typeof clack.text;
23
+ password: typeof clack.password;
24
+ spinner: typeof clack.spinner;
25
+ isCancel: typeof clack.isCancel;
26
+ }
27
+ /** The real interactive UI. */
28
+ export declare function createClackUi(backend?: PromptBackend): Ui;
29
+ /**
30
+ * Non-interactive UI for `--yes`, `CEZ_DRY_RUN`, and unit tests. Prompts resolve
31
+ * to deterministic safe defaults (initial value, or the first option, or ""),
32
+ * logs go to the console. It never touches stdin, so it can drive the engine
33
+ * headless. Optional `answers` override defaults per-prompt-message.
34
+ */
35
+ export declare function createAutoUi(answers?: Record<string, unknown>, sink?: (m: string) => void, opts?: {
36
+ /**
37
+ * Enforce each prompt's `validate` on auto-answers (real `--yes` runs must
38
+ * fail closed on an unanswerable prompt). Off for CEZ_DRY_RUN previews,
39
+ * which must walk every step with placeholder-grade values.
40
+ */
41
+ strictValidate?: boolean;
42
+ }): Ui;
@@ -0,0 +1,130 @@
1
+ import * as clack from '@clack/prompts';
2
+ import { CANCEL, PreflightError } from './types.js';
3
+ import { StepAborted } from './steps.js';
4
+ const realBackend = {
5
+ intro: clack.intro,
6
+ outro: clack.outro,
7
+ note: clack.note,
8
+ log: clack.log,
9
+ select: clack.select,
10
+ multiselect: clack.multiselect,
11
+ confirm: clack.confirm,
12
+ text: clack.text,
13
+ password: clack.password,
14
+ spinner: clack.spinner,
15
+ isCancel: clack.isCancel,
16
+ };
17
+ /** Map a clack result to `T | CANCEL`, never a thrown cancel. */
18
+ function unwrap(value, isCancel) {
19
+ return isCancel(value) ? CANCEL : value;
20
+ }
21
+ /** The real interactive UI. */
22
+ export function createClackUi(backend = realBackend) {
23
+ // clack renders a prompt at stdin-EOF and never resolves — a piped/ssh'd
24
+ // invocation would hang forever while holding the single-writer install
25
+ // lock. Refuse up front with the way out. (Injected fake backends are for
26
+ // tests, which have no TTY.)
27
+ if (backend === realBackend && (!process.stdin.isTTY || !process.stdout.isTTY)) {
28
+ throw new PreflightError('this terminal is not interactive — re-run from a TTY, or pass --yes (or CEZ_DRY_RUN=1) for non-interactive mode');
29
+ }
30
+ const wrapValidate = (validate) => validate ? (v) => validate(v ?? '') : undefined;
31
+ return {
32
+ intro: (m) => backend.intro(m),
33
+ outro: (m) => backend.outro(m),
34
+ note: (m, title) => backend.note(m, title),
35
+ // Raw, un-boxed: clack's note() draws a bordered box that breaks when a long
36
+ // command wraps. Write straight to stdout so the command stays selectable.
37
+ message: (m) => process.stdout.write(`\n${m}\n`),
38
+ info: (m) => backend.log.info(m),
39
+ success: (m) => backend.log.success(m),
40
+ warn: (m) => backend.log.warn(m),
41
+ error: (m) => backend.log.error(m),
42
+ async select(opts) {
43
+ // clack's `Option<Value>` is a conditional type that can't resolve against
44
+ // an unconstrained generic; cast the options and keep the explicit T.
45
+ const res = await backend.select({
46
+ message: opts.message,
47
+ options: opts.options,
48
+ initialValue: opts.initialValue,
49
+ });
50
+ return unwrap(res, backend.isCancel);
51
+ },
52
+ async multiselect(opts) {
53
+ const res = await backend.multiselect({
54
+ message: opts.message,
55
+ options: opts.options,
56
+ required: opts.required ?? false,
57
+ });
58
+ return unwrap(res, backend.isCancel);
59
+ },
60
+ async confirm(opts) {
61
+ return unwrap(await backend.confirm(opts), backend.isCancel);
62
+ },
63
+ async text(opts) {
64
+ return unwrap(await backend.text({ ...opts, validate: wrapValidate(opts.validate) }), backend.isCancel);
65
+ },
66
+ async password(opts) {
67
+ return unwrap(await backend.password({ ...opts, validate: wrapValidate(opts.validate) }), backend.isCancel);
68
+ },
69
+ spinner() {
70
+ const s = backend.spinner();
71
+ return {
72
+ start: (m) => s.start(m),
73
+ stop: (m) => s.stop(m),
74
+ message: (m) => s.message(m),
75
+ };
76
+ },
77
+ };
78
+ }
79
+ /**
80
+ * Non-interactive UI for `--yes`, `CEZ_DRY_RUN`, and unit tests. Prompts resolve
81
+ * to deterministic safe defaults (initial value, or the first option, or ""),
82
+ * logs go to the console. It never touches stdin, so it can drive the engine
83
+ * headless. Optional `answers` override defaults per-prompt-message.
84
+ */
85
+ export function createAutoUi(answers = {}, sink = () => { }, opts = {}) {
86
+ const answer = (message, fallback) => (message in answers ? answers[message] : fallback);
87
+ const checkValid = (message, v, validate) => {
88
+ if (!opts.strictValidate)
89
+ return;
90
+ const invalid = validate?.(v);
91
+ if (invalid !== undefined) {
92
+ throw new StepAborted(`cannot auto-answer "${message}" (${invalid}) — run without --yes to answer it`);
93
+ }
94
+ };
95
+ return {
96
+ intro: sink,
97
+ outro: sink,
98
+ note: (m) => sink(m),
99
+ message: sink,
100
+ info: sink,
101
+ success: sink,
102
+ warn: sink,
103
+ error: sink,
104
+ async select(opts) {
105
+ return answer(opts.message, opts.initialValue ?? opts.options[0]?.value);
106
+ },
107
+ async multiselect(opts) {
108
+ return answer(opts.message, []);
109
+ },
110
+ async confirm(opts) {
111
+ return answer(opts.message, opts.initialValue ?? true);
112
+ },
113
+ // A placeholder is a HINT, never an answer — auto-adopting it turned the
114
+ // example domain `cezar.ngrok.app` into real input under `--yes`.
115
+ async text(o) {
116
+ const v = String(answer(o.message, o.initialValue ?? ''));
117
+ checkValid(o.message, v, o.validate);
118
+ return v;
119
+ },
120
+ async password(o) {
121
+ const v = String(answer(o.message, ''));
122
+ checkValid(o.message, v, o.validate);
123
+ return v;
124
+ },
125
+ spinner() {
126
+ return { start: (m) => m && sink(m), stop: (m) => m && sink(m), message: (m) => sink(m) };
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/server-install/ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Promotes `open-mercato/skills` (#391) — cezar's built-in default skills source
3
+ * (`DEFAULT_SKILLS_REPOS` in `src/config.ts`) that today only surfaces in `--help`. Split out of
4
+ * `src/index.ts` so the CLI banner has one source of truth the web cockpit copy
5
+ * (`web/app/src/components/skills-banner.tsx`) can be kept in step with, and so it is testable
6
+ * without importing `index.ts` (which runs `main()` on import).
7
+ *
8
+ * Printed on `serve` start — a few lines in an already-chatty startup block. It has two off
9
+ * switches, because a promo that cannot be silenced is a nag:
10
+ * - `CEZ_NO_BANNER=1` in the environment, and
11
+ * - `dismissedSkillsBanner` in `.ai/cezar/ui-state.json` — the SAME flag the cockpit banner
12
+ * persists, so dismissing it there also silences the terminal and the two surfaces tell one
13
+ * story.
14
+ * Reading that flag follows the `src/config.ts` rule: a missing/unreadable/malformed state file
15
+ * degrades to showing the banner, never to a crash or a blocked startup.
16
+ */
17
+ export declare const SKILLS_BANNER_LINES: readonly string[];
18
+ /** Both off switches in one place. Never throws. */
19
+ export declare function shouldShowSkillsBanner(repoRoot: string): Promise<boolean>;
20
+ /** The `serve` startup banner. `log` is injectable so the wiring is testable. */
21
+ export declare function printSkillsBanner(repoRoot: string, log?: (line?: string) => void): Promise<void>;
@@ -0,0 +1,40 @@
1
+ import { readUiState } from './ui-state.js';
2
+ /**
3
+ * Promotes `open-mercato/skills` (#391) — cezar's built-in default skills source
4
+ * (`DEFAULT_SKILLS_REPOS` in `src/config.ts`) that today only surfaces in `--help`. Split out of
5
+ * `src/index.ts` so the CLI banner has one source of truth the web cockpit copy
6
+ * (`web/app/src/components/skills-banner.tsx`) can be kept in step with, and so it is testable
7
+ * without importing `index.ts` (which runs `main()` on import).
8
+ *
9
+ * Printed on `serve` start — a few lines in an already-chatty startup block. It has two off
10
+ * switches, because a promo that cannot be silenced is a nag:
11
+ * - `CEZ_NO_BANNER=1` in the environment, and
12
+ * - `dismissedSkillsBanner` in `.ai/cezar/ui-state.json` — the SAME flag the cockpit banner
13
+ * persists, so dismissing it there also silences the terminal and the two surfaces tell one
14
+ * story.
15
+ * Reading that flag follows the `src/config.ts` rule: a missing/unreadable/malformed state file
16
+ * degrades to showing the banner, never to a crash or a blocked startup.
17
+ */
18
+ export const SKILLS_BANNER_LINES = [
19
+ ' 🤖 Make the most of parallel coding with our AI skills',
20
+ ' open-mercato/skills: reusable, technology-agnostic agent skills for PR',
21
+ ' creation, code review, CI stabilisation, spec writing & more.',
22
+ ' cezar already loads them for you — Skills → Refresh in the cockpit gets the latest.',
23
+ " To use them outside cezar: npx skills add open-mercato/skills --skill '*'",
24
+ ];
25
+ /** Both off switches in one place. Never throws. */
26
+ export async function shouldShowSkillsBanner(repoRoot) {
27
+ if (process.env.CEZ_NO_BANNER === '1')
28
+ return false;
29
+ const uiState = await readUiState(repoRoot);
30
+ return uiState.dismissedSkillsBanner !== true;
31
+ }
32
+ /** The `serve` startup banner. `log` is injectable so the wiring is testable. */
33
+ export async function printSkillsBanner(repoRoot, log = console.log) {
34
+ if (!(await shouldShowSkillsBanner(repoRoot)))
35
+ return;
36
+ for (const line of SKILLS_BANNER_LINES)
37
+ log(line);
38
+ log();
39
+ }
40
+ //# sourceMappingURL=skills-banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-banner.js","sourceRoot":"","sources":["../src/skills-banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,0DAA0D;IAC1D,0EAA0E;IAC1E,iEAAiE;IACjE,uFAAuF;IACvF,8EAA8E;CAC/E,CAAC;AAEF,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,QAAgB;IAC3D,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,qBAAqB,KAAK,IAAI,CAAC;AAChD,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,MAA+B,OAAO,CAAC,GAAG;IAE1C,IAAI,CAAC,CAAC,MAAM,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO;IACtD,KAAK,MAAM,IAAI,IAAI,mBAAmB;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAClD,GAAG,EAAE,CAAC;AACR,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type SkillsRepoSource } from './config.js';
2
+ import { type Skill } from './skills.js';
3
+ /** Stable cache directory name: the last two path segments, `owner__name`. */
4
+ export declare function bareDirFor(repo: string): string;
5
+ /** Clone the skills repo bare (no checkout) into the global cache, once. */
6
+ export declare function ensureBareClone(repo: string): Promise<{
7
+ bareDir: string;
8
+ created: boolean;
9
+ }>;
10
+ /** "Refresh" — update every branch head in the bare clone from origin. */
11
+ export declare function fetchAll(bareDir: string): Promise<void>;
12
+ /** Read one file from the bare clone at the source's ref. Null on any failure. */
13
+ export declare function readRemoteSkill(src: SkillsRepoSource, path: string): Promise<string | null>;
14
+ /**
15
+ * List every skill the repo defines at `src.ref`. Reads from the local bare
16
+ * clone only — no network. Empty list when the clone doesn't exist yet or
17
+ * the ref can't be resolved.
18
+ */
19
+ export declare function listRemoteSkills(src: SkillsRepoSource): Promise<Skill[]>;
20
+ /**
21
+ * Copy a directory skill (SKILL.md + references/…) out of the bare clone into
22
+ * `<repoRoot>/.claude/skills/<name>/` so claude sees the references on disk,
23
+ * and keep it out of the user's git via `.git/info/exclude`. Returns false
24
+ * when there is nothing to materialize (not a directory skill, no clone…).
25
+ */
26
+ export declare function materializeSkillDir(repoRoot: string, skill: Skill): Promise<boolean>;
27
+ /**
28
+ * The current team-skill list, straight from memory. The first call kicks off
29
+ * an async background load (clone + list) and returns immediately — the GUI
30
+ * refetches, so remote skills appear moments later instead of blocking the
31
+ * first `GET /api/skills`.
32
+ */
33
+ export declare function getTeamSkillsCached(repoRoot: string): Skill[];
34
+ /** Refresh: clone missing sources, `git fetch` existing ones, reload the list. */
35
+ export declare function refreshTeamSkills(repoRoot: string): Promise<Skill[]>;
@@ -0,0 +1,266 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { homedir } from 'node:os';
5
+ import { basename, dirname, join } from 'node:path';
6
+ import { loadConfig } from './config.js';
7
+ import { parseFrontmatter } from './skills.js';
8
+ /**
9
+ * Team skills from remote git repos (spec 005), janitor-style: a bare clone
10
+ * without a checkout, listed with `git ls-tree` and read with `git show`.
11
+ * The cache lives in `~/.cache/cez/skills/<owner>__<name>/` — global, so one
12
+ * fetch serves every project. Everything degrades: no network / no access to
13
+ * the skills repo means the team skills quietly disappear from the list while
14
+ * local skills keep working. Nothing here ever blocks startup.
15
+ */
16
+ const LIST_TIMEOUT_MS = 10_000; // ls-tree / show / rev-parse
17
+ const CLONE_TIMEOUT_MS = 60_000; // clone / fetch
18
+ function git(args, timeoutMs, cwd) {
19
+ return new Promise((resolve) => {
20
+ execFile('git', args, { cwd, timeout: timeoutMs, killSignal: 'SIGKILL', maxBuffer: 16 * 1024 * 1024, encoding: 'utf8' }, (err, stdout, stderr) => resolve({ ok: !err, stdout: stdout ?? '', stderr: stderr ?? '' }));
21
+ });
22
+ }
23
+ /** `owner/name` shorthand → GitHub HTTPS; anything else (URL, file://, local path) as-is. */
24
+ function remoteFor(repo) {
25
+ return /^[\w.-]+\/[\w.-]+$/.test(repo) ? `https://github.com/${repo}.git` : repo;
26
+ }
27
+ /** Stable cache directory name: the last two path segments, `owner__name`. */
28
+ export function bareDirFor(repo) {
29
+ const trimmed = repo
30
+ .replace(/\/+$/, '')
31
+ .replace(/\.git$/, '')
32
+ .replace(/^[a-z+]+:\/\//i, '')
33
+ .replace(/^~\//, '');
34
+ const segments = trimmed.split(/[/:]/).filter(Boolean).map(sanitizeSegment);
35
+ const key = segments.slice(-2).join('__') || 'skills';
36
+ return join(homedir(), '.cache', 'cez', 'skills', key);
37
+ }
38
+ function sanitizeSegment(s) {
39
+ return s.replace(/[^\w.-]/g, '-');
40
+ }
41
+ /** Clone the skills repo bare (no checkout) into the global cache, once. */
42
+ export async function ensureBareClone(repo) {
43
+ const bareDir = bareDirFor(repo);
44
+ if (existsSync(join(bareDir, 'HEAD')))
45
+ return { bareDir, created: false };
46
+ await mkdir(dirname(bareDir), { recursive: true });
47
+ const remote = remoteFor(repo);
48
+ const res = await git(['clone', '--bare', remote, bareDir], CLONE_TIMEOUT_MS);
49
+ if (!res.ok)
50
+ throw new Error(`git clone --bare ${remote} failed: ${res.stderr.trim()}`);
51
+ return { bareDir, created: true };
52
+ }
53
+ /** "Refresh" — update every branch head in the bare clone from origin. */
54
+ export async function fetchAll(bareDir) {
55
+ const res = await git(['fetch', 'origin', '--prune', '+refs/heads/*:refs/heads/*'], CLONE_TIMEOUT_MS, bareDir);
56
+ if (!res.ok)
57
+ throw new Error(`git fetch failed: ${res.stderr.trim() || res.stdout.trim()}`);
58
+ }
59
+ async function resolveRef(bareDir, ref) {
60
+ for (const candidate of [ref, `refs/heads/${ref}`, 'HEAD']) {
61
+ const probe = await git(['rev-parse', '--verify', '--quiet', candidate], LIST_TIMEOUT_MS, bareDir);
62
+ if (probe.ok)
63
+ return candidate;
64
+ }
65
+ return ref;
66
+ }
67
+ /**
68
+ * Match the janitor conventions plus our own:
69
+ * - `**\/SKILL.md` → skill named after the parent directory (with references/)
70
+ * - `**\/commands/<n>.md` → skill `<n>`
71
+ * - `.ai/skills/**\/*.md` or `.ai/cezar/skills/**\/*.md` → frontmatter/basename name
72
+ */
73
+ function matchSkillPath(line) {
74
+ if (line === 'SKILL.md' || line.endsWith('/SKILL.md')) {
75
+ const parts = line.split('/');
76
+ if (parts.length < 2)
77
+ return null;
78
+ const parent = parts[parts.length - 2];
79
+ return parent && parent !== '.' ? { name: parent, kind: 'skill' } : null;
80
+ }
81
+ const cmd = /(?:^|\/)commands\/([^/]+)\.md$/.exec(line);
82
+ if (cmd)
83
+ return { name: cmd[1], kind: 'command' };
84
+ if (/(?:^|\/)\.ai\/(?:cezar\/)?skills\/.+\.md$/.test(line))
85
+ return { name: null, kind: 'markdown' };
86
+ return null;
87
+ }
88
+ /** Read one file from the bare clone at the source's ref. Null on any failure. */
89
+ export async function readRemoteSkill(src, path) {
90
+ const bareDir = bareDirFor(src.repo);
91
+ if (!existsSync(join(bareDir, 'HEAD')))
92
+ return null;
93
+ const ref = await resolveRef(bareDir, src.ref);
94
+ const res = await git(['show', `${ref}:${path}`], LIST_TIMEOUT_MS, bareDir);
95
+ return res.ok ? res.stdout : null;
96
+ }
97
+ /**
98
+ * List every skill the repo defines at `src.ref`. Reads from the local bare
99
+ * clone only — no network. Empty list when the clone doesn't exist yet or
100
+ * the ref can't be resolved.
101
+ */
102
+ export async function listRemoteSkills(src) {
103
+ const bareDir = bareDirFor(src.repo);
104
+ if (!existsSync(join(bareDir, 'HEAD')))
105
+ return [];
106
+ const ref = await resolveRef(bareDir, src.ref);
107
+ const ls = await git(['ls-tree', '-r', '--name-only', ref], LIST_TIMEOUT_MS, bareDir);
108
+ if (!ls.ok)
109
+ return [];
110
+ const skills = [];
111
+ const seen = new Set();
112
+ for (const line of ls.stdout.split('\n')) {
113
+ if (!line)
114
+ continue;
115
+ const hit = matchSkillPath(line);
116
+ if (!hit)
117
+ continue;
118
+ const raw = await readRemoteSkill(src, line);
119
+ if (raw === null)
120
+ continue;
121
+ const { frontmatter, body } = parseFrontmatter(raw);
122
+ const name = hit.name ??
123
+ (typeof frontmatter.name === 'string' && frontmatter.name.trim()
124
+ ? frontmatter.name.trim()
125
+ : basename(line, '.md'));
126
+ if (seen.has(name))
127
+ continue;
128
+ seen.add(name);
129
+ const description = typeof frontmatter.description === 'string' && frontmatter.description.trim()
130
+ ? frontmatter.description.trim()
131
+ : undefined;
132
+ skills.push({
133
+ name,
134
+ description,
135
+ body,
136
+ path: `${src.repo}@${src.ref}:${line}`,
137
+ source: 'team',
138
+ team: { repo: src.repo, ref: src.ref, path: line, dir: hit.kind === 'skill' },
139
+ });
140
+ }
141
+ return skills;
142
+ }
143
+ // ---- materialization (directory skills) ---------------------------------------
144
+ /**
145
+ * Copy a directory skill (SKILL.md + references/…) out of the bare clone into
146
+ * `<repoRoot>/.claude/skills/<name>/` so claude sees the references on disk,
147
+ * and keep it out of the user's git via `.git/info/exclude`. Returns false
148
+ * when there is nothing to materialize (not a directory skill, no clone…).
149
+ */
150
+ export async function materializeSkillDir(repoRoot, skill) {
151
+ if (!skill.team?.dir || !skill.team.path.endsWith('SKILL.md'))
152
+ return false;
153
+ const bareDir = bareDirFor(skill.team.repo);
154
+ if (!existsSync(join(bareDir, 'HEAD')))
155
+ return false;
156
+ const ref = await resolveRef(bareDir, skill.team.ref);
157
+ const srcDir = skill.team.path.slice(0, -'/SKILL.md'.length);
158
+ const ls = await git(['ls-tree', '-r', '--name-only', ref, '--', srcDir], LIST_TIMEOUT_MS, bareDir);
159
+ if (!ls.ok)
160
+ return false;
161
+ const destDir = join(repoRoot, '.claude', 'skills', skill.name);
162
+ let wrote = 0;
163
+ for (const file of ls.stdout.split('\n').filter(Boolean)) {
164
+ const rel = file.slice(srcDir.length + 1);
165
+ // git paths are repo-relative and normalized, but never trust them blindly.
166
+ if (!rel || rel.split('/').includes('..'))
167
+ continue;
168
+ const show = await git(['show', `${ref}:${file}`], LIST_TIMEOUT_MS, bareDir);
169
+ if (!show.ok)
170
+ continue;
171
+ const target = join(destDir, rel);
172
+ await mkdir(dirname(target), { recursive: true });
173
+ await writeFile(target, show.stdout, 'utf8');
174
+ wrote++;
175
+ }
176
+ if (wrote === 0)
177
+ return false;
178
+ await excludeFromGit(repoRoot, `.claude/skills/${skill.name}/`);
179
+ return true;
180
+ }
181
+ /** Append a pattern to git's `info/exclude` (idempotent, non-fatal). */
182
+ async function excludeFromGit(repoRoot, pattern) {
183
+ try {
184
+ // Resolve the real exclude file: in a linked worktree (spec 006) `.git`
185
+ // is a file and `info/exclude` lives in the shared common dir — which
186
+ // also means one exclude entry covers every task worktree.
187
+ const probe = await git(['rev-parse', '--path-format=absolute', '--git-common-dir'], LIST_TIMEOUT_MS, repoRoot);
188
+ const gitDir = probe.ok && probe.stdout.trim() ? probe.stdout.trim() : join(repoRoot, '.git');
189
+ const excludePath = join(gitDir, 'info', 'exclude');
190
+ let prev = '';
191
+ try {
192
+ prev = await readFile(excludePath, 'utf8');
193
+ }
194
+ catch {
195
+ // no exclude file yet
196
+ }
197
+ if (prev.split('\n').includes(pattern))
198
+ return;
199
+ await mkdir(dirname(excludePath), { recursive: true });
200
+ await writeFile(excludePath, prev + (prev && !prev.endsWith('\n') ? '\n' : '') + pattern + '\n', 'utf8');
201
+ }
202
+ catch {
203
+ // non-fatal — `.git` might be a linked file (worktree) or absent entirely
204
+ }
205
+ }
206
+ // ---- in-process cache ----------------------------------------------------------
207
+ // Clone attempts are expensive when the network is down (git can hang on
208
+ // DNS/TCP), so each source gets one implicit attempt per process. "Refresh"
209
+ // always retries.
210
+ const cloneAttempted = new Set();
211
+ let teamSkills = [];
212
+ let firstLoadStarted = false;
213
+ /**
214
+ * The current team-skill list, straight from memory. The first call kicks off
215
+ * an async background load (clone + list) and returns immediately — the GUI
216
+ * refetches, so remote skills appear moments later instead of blocking the
217
+ * first `GET /api/skills`.
218
+ */
219
+ export function getTeamSkillsCached(repoRoot) {
220
+ if (!firstLoadStarted) {
221
+ firstLoadStarted = true;
222
+ void loadTeamSkills(repoRoot, false).catch(() => undefined);
223
+ }
224
+ return teamSkills;
225
+ }
226
+ /** Refresh: clone missing sources, `git fetch` existing ones, reload the list. */
227
+ export async function refreshTeamSkills(repoRoot) {
228
+ firstLoadStarted = true;
229
+ return loadTeamSkills(repoRoot, true);
230
+ }
231
+ async function loadTeamSkills(repoRoot, refresh) {
232
+ const config = await loadConfig(repoRoot);
233
+ const out = [];
234
+ const seen = new Set();
235
+ for (const src of config.skillsRepos) {
236
+ try {
237
+ if (refresh) {
238
+ const { bareDir, created } = await ensureBareClone(src.repo);
239
+ if (!created)
240
+ await fetchAll(bareDir);
241
+ cloneAttempted.add(src.repo);
242
+ }
243
+ else if (!cloneAttempted.has(src.repo)) {
244
+ cloneAttempted.add(src.repo);
245
+ await ensureBareClone(src.repo);
246
+ }
247
+ }
248
+ catch {
249
+ // offline / no access — list whatever an older clone has (or nothing)
250
+ }
251
+ try {
252
+ for (const skill of await listRemoteSkills(src)) {
253
+ if (seen.has(skill.name))
254
+ continue;
255
+ seen.add(skill.name);
256
+ out.push(skill);
257
+ }
258
+ }
259
+ catch {
260
+ // degrade: this source contributes nothing
261
+ }
262
+ }
263
+ teamSkills = out;
264
+ return out;
265
+ }
266
+ //# sourceMappingURL=skills-remote.js.map