kiriya 0.1.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 (282) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/dist/src/config/clean-rules.js +39 -0
  5. package/dist/src/config/config-keys.js +24 -0
  6. package/dist/src/config/dependency-directories.js +16 -0
  7. package/dist/src/config/modules.js +38 -0
  8. package/dist/src/config/protected-paths.js +32 -0
  9. package/dist/src/config/rebuildable-directories.js +20 -0
  10. package/dist/src/core/application/command-registry.js +57 -0
  11. package/dist/src/core/application/config-values.js +33 -0
  12. package/dist/src/core/application/path-guard.js +69 -0
  13. package/dist/src/core/application/paths.js +77 -0
  14. package/dist/src/core/application/plugin-loader.js +100 -0
  15. package/dist/src/core/application/process-ending.js +28 -0
  16. package/dist/src/core/application/root-scope.js +123 -0
  17. package/dist/src/core/application/safety.js +26 -0
  18. package/dist/src/core/application/text-input.js +44 -0
  19. package/dist/src/core/application/walk.js +34 -0
  20. package/dist/src/core/domain/command.js +8 -0
  21. package/dist/src/core/domain/encodings.js +70 -0
  22. package/dist/src/core/domain/errors.js +49 -0
  23. package/dist/src/core/domain/glob.js +57 -0
  24. package/dist/src/core/domain/global-options.js +10 -0
  25. package/dist/src/core/domain/input-schema.js +54 -0
  26. package/dist/src/core/domain/message.js +7 -0
  27. package/dist/src/core/domain/module.js +2 -0
  28. package/dist/src/core/domain/names.js +22 -0
  29. package/dist/src/core/domain/ports/clipboard.js +2 -0
  30. package/dist/src/core/domain/ports/clock.js +2 -0
  31. package/dist/src/core/domain/ports/command-catalog.js +2 -0
  32. package/dist/src/core/domain/ports/compression.js +2 -0
  33. package/dist/src/core/domain/ports/config-store.js +2 -0
  34. package/dist/src/core/domain/ports/confirmation.js +2 -0
  35. package/dist/src/core/domain/ports/environment.js +2 -0
  36. package/dist/src/core/domain/ports/file-content.js +2 -0
  37. package/dist/src/core/domain/ports/file-system.js +2 -0
  38. package/dist/src/core/domain/ports/hasher.js +2 -0
  39. package/dist/src/core/domain/ports/network.js +2 -0
  40. package/dist/src/core/domain/ports/opener.js +2 -0
  41. package/dist/src/core/domain/ports/passthrough.js +2 -0
  42. package/dist/src/core/domain/ports/plugin-inventory.js +2 -0
  43. package/dist/src/core/domain/ports/plugin-source.js +2 -0
  44. package/dist/src/core/domain/ports/port-table.js +2 -0
  45. package/dist/src/core/domain/ports/process-runner.js +2 -0
  46. package/dist/src/core/domain/ports/process-table.js +2 -0
  47. package/dist/src/core/domain/ports/protected-paths.js +2 -0
  48. package/dist/src/core/domain/ports/random-source.js +2 -0
  49. package/dist/src/core/domain/ports/standard-input.js +2 -0
  50. package/dist/src/core/domain/ports/system-info.js +2 -0
  51. package/dist/src/core/domain/ports/trash.js +2 -0
  52. package/dist/src/core/domain/secrets.js +59 -0
  53. package/dist/src/core/domain/targets.js +41 -0
  54. package/dist/src/core/domain/text-case.js +39 -0
  55. package/dist/src/core/domain/values/bytes.js +13 -0
  56. package/dist/src/core/domain/view.js +2 -0
  57. package/dist/src/core/infrastructure/node/can-listen.js +10 -0
  58. package/dist/src/core/infrastructure/node/closed-standard-input.adapter.js +12 -0
  59. package/dist/src/core/infrastructure/node/config-location.js +27 -0
  60. package/dist/src/core/infrastructure/node/fs-errors.js +22 -0
  61. package/dist/src/core/infrastructure/node/json-config-store.adapter.js +69 -0
  62. package/dist/src/core/infrastructure/node/node-compression.adapter.js +82 -0
  63. package/dist/src/core/infrastructure/node/node-environment.adapter.js +21 -0
  64. package/dist/src/core/infrastructure/node/node-file-content.adapter.js +99 -0
  65. package/dist/src/core/infrastructure/node/node-file-system.adapter.js +169 -0
  66. package/dist/src/core/infrastructure/node/node-hasher.adapter.js +32 -0
  67. package/dist/src/core/infrastructure/node/node-network.adapter.js +168 -0
  68. package/dist/src/core/infrastructure/node/node-plugin-source.adapter.js +89 -0
  69. package/dist/src/core/infrastructure/node/node-process-runner.adapter.js +48 -0
  70. package/dist/src/core/infrastructure/node/node-random-source.adapter.js +7 -0
  71. package/dist/src/core/infrastructure/node/node-standard-input.adapter.js +24 -0
  72. package/dist/src/core/infrastructure/node/node-system-info.adapter.js +63 -0
  73. package/dist/src/core/infrastructure/node/process-signals.js +51 -0
  74. package/dist/src/core/infrastructure/node/run-program.js +65 -0
  75. package/dist/src/core/infrastructure/node/system-clock.adapter.js +22 -0
  76. package/dist/src/core/infrastructure/node/typed-programs.js +45 -0
  77. package/dist/src/core/infrastructure/platform/linux/freedesktop-trash.adapter.js +115 -0
  78. package/dist/src/core/infrastructure/platform/linux/linux-clipboard.adapter.js +70 -0
  79. package/dist/src/core/infrastructure/platform/linux/linux-opener.adapter.js +21 -0
  80. package/dist/src/core/infrastructure/platform/linux/linux-port-table.adapter.js +97 -0
  81. package/dist/src/core/infrastructure/platform/linux/linux-process-table.adapter.js +61 -0
  82. package/dist/src/core/infrastructure/platform/macos/macos-clipboard.adapter.js +29 -0
  83. package/dist/src/core/infrastructure/platform/macos/macos-opener.adapter.js +13 -0
  84. package/dist/src/core/infrastructure/platform/macos/macos-port-table.adapter.js +86 -0
  85. package/dist/src/core/infrastructure/platform/macos/macos-process-table.adapter.js +62 -0
  86. package/dist/src/core/infrastructure/platform/macos/macos-trash.adapter.js +64 -0
  87. package/dist/src/core/infrastructure/platform/windows/system32.js +6 -0
  88. package/dist/src/core/infrastructure/platform/windows/windows-clipboard.adapter.js +50 -0
  89. package/dist/src/core/infrastructure/platform/windows/windows-opener.adapter.js +11 -0
  90. package/dist/src/core/infrastructure/platform/windows/windows-port-table.adapter.js +38 -0
  91. package/dist/src/core/infrastructure/platform/windows/windows-process-table.adapter.js +134 -0
  92. package/dist/src/core/infrastructure/platform/windows/windows-trash.adapter.js +86 -0
  93. package/dist/src/core/presentation/cli/argv.js +83 -0
  94. package/dist/src/core/presentation/cli/cli-application.js +201 -0
  95. package/dist/src/core/presentation/cli/cli-view-format.js +46 -0
  96. package/dist/src/core/presentation/cli/help.js +202 -0
  97. package/dist/src/core/presentation/cli/json-output.js +28 -0
  98. package/dist/src/core/presentation/cli/style.js +21 -0
  99. package/dist/src/core/presentation/cli/suggest.js +25 -0
  100. package/dist/src/core/presentation/cli/terminal-confirmation.js +63 -0
  101. package/dist/src/core/presentation/ended-processes.js +18 -0
  102. package/dist/src/core/presentation/i18n/translator.js +23 -0
  103. package/dist/src/core/presentation/list-preview.js +10 -0
  104. package/dist/src/core/presentation/mcp/elicitation.js +55 -0
  105. package/dist/src/core/presentation/mcp/mcp-application.js +60 -0
  106. package/dist/src/core/presentation/mcp/mcp-confirmation.js +59 -0
  107. package/dist/src/core/presentation/mcp/mcp-server.js +355 -0
  108. package/dist/src/core/presentation/mcp/protocol.js +32 -0
  109. package/dist/src/core/presentation/mcp/request-state.js +66 -0
  110. package/dist/src/core/presentation/mcp/tool-definitions.js +149 -0
  111. package/dist/src/core/presentation/mcp/tool-results.js +56 -0
  112. package/dist/src/i18n/locales/en.js +827 -0
  113. package/dist/src/main.js +154 -0
  114. package/dist/src/modules/archive/application/archive-sources.js +55 -0
  115. package/dist/src/modules/archive/application/create-tar.use-case.js +123 -0
  116. package/dist/src/modules/archive/application/create-zip.use-case.js +106 -0
  117. package/dist/src/modules/archive/application/extract-tar.use-case.js +240 -0
  118. package/dist/src/modules/archive/application/extract-zip.use-case.js +181 -0
  119. package/dist/src/modules/archive/application/tar-reader.js +45 -0
  120. package/dist/src/modules/archive/application/zip-reader.js +57 -0
  121. package/dist/src/modules/archive/archive.module.js +27 -0
  122. package/dist/src/modules/archive/domain/crc32.js +18 -0
  123. package/dist/src/modules/archive/domain/tar-format.js +97 -0
  124. package/dist/src/modules/archive/domain/tar-stream.js +223 -0
  125. package/dist/src/modules/archive/domain/zip-format.js +158 -0
  126. package/dist/src/modules/archive/presentation/tar.view.js +17 -0
  127. package/dist/src/modules/archive/presentation/untar.view.js +30 -0
  128. package/dist/src/modules/archive/presentation/unzip.view.js +24 -0
  129. package/dist/src/modules/archive/presentation/zip.view.js +17 -0
  130. package/dist/src/modules/clip/application/copy-text.use-case.js +43 -0
  131. package/dist/src/modules/clip/application/paste-text.use-case.js +25 -0
  132. package/dist/src/modules/clip/clip.module.js +16 -0
  133. package/dist/src/modules/clip/presentation/clip.views.js +7 -0
  134. package/dist/src/modules/completion/application/print-script.use-case.js +38 -0
  135. package/dist/src/modules/completion/application/suggest-words.use-case.js +37 -0
  136. package/dist/src/modules/completion/completion.module.js +14 -0
  137. package/dist/src/modules/completion/domain/scripts.js +91 -0
  138. package/dist/src/modules/completion/domain/suggest.js +134 -0
  139. package/dist/src/modules/completion/presentation/completion.views.js +6 -0
  140. package/dist/src/modules/config/application/config-commands.js +14 -0
  141. package/dist/src/modules/config/application/get-setting.use-case.js +25 -0
  142. package/dist/src/modules/config/application/list-config-keys.use-case.js +26 -0
  143. package/dist/src/modules/config/application/list-settings.use-case.js +28 -0
  144. package/dist/src/modules/config/application/set-setting.use-case.js +53 -0
  145. package/dist/src/modules/config/application/show-config-path.use-case.js +21 -0
  146. package/dist/src/modules/config/application/unset-setting.use-case.js +28 -0
  147. package/dist/src/modules/config/config.module.js +28 -0
  148. package/dist/src/modules/config/presentation/config.views.js +33 -0
  149. package/dist/src/modules/convert/application/convert-case.use-case.js +38 -0
  150. package/dist/src/modules/convert/application/convert-codecs.use-case.js +92 -0
  151. package/dist/src/modules/convert/application/convert-input.js +16 -0
  152. package/dist/src/modules/convert/application/convert-json.use-case.js +51 -0
  153. package/dist/src/modules/convert/application/convert-time.use-case.js +39 -0
  154. package/dist/src/modules/convert/application/decode-jwt.use-case.js +52 -0
  155. package/dist/src/modules/convert/convert.module.js +29 -0
  156. package/dist/src/modules/convert/domain/json-syntax.js +135 -0
  157. package/dist/src/modules/convert/domain/jwt.js +29 -0
  158. package/dist/src/modules/convert/domain/moments.js +20 -0
  159. package/dist/src/modules/convert/presentation/convert.views.js +44 -0
  160. package/dist/src/modules/docker/application/clean-engine.use-case.js +77 -0
  161. package/dist/src/modules/docker/application/compose-project.js +45 -0
  162. package/dist/src/modules/docker/application/docker-engine.js +40 -0
  163. package/dist/src/modules/docker/application/list-containers.use-case.js +69 -0
  164. package/dist/src/modules/docker/application/run-compose.use-case.js +83 -0
  165. package/dist/src/modules/docker/application/show-logs.use-case.js +51 -0
  166. package/dist/src/modules/docker/application/stop-project.use-case.js +56 -0
  167. package/dist/src/modules/docker/docker.module.js +23 -0
  168. package/dist/src/modules/docker/domain/compose-file.js +105 -0
  169. package/dist/src/modules/docker/domain/engine-output.js +65 -0
  170. package/dist/src/modules/docker/presentation/docker.views.js +63 -0
  171. package/dist/src/modules/doctor/application/run-checks.use-case.js +128 -0
  172. package/dist/src/modules/doctor/doctor.module.js +13 -0
  173. package/dist/src/modules/doctor/domain/versions.js +22 -0
  174. package/dist/src/modules/doctor/presentation/doctor.view.js +30 -0
  175. package/dist/src/modules/env/application/check-dotenv.use-case.js +116 -0
  176. package/dist/src/modules/env/application/inspect-path.use-case.js +71 -0
  177. package/dist/src/modules/env/application/path-entries.js +28 -0
  178. package/dist/src/modules/env/application/show-variables.use-case.js +47 -0
  179. package/dist/src/modules/env/domain/dotenv.js +62 -0
  180. package/dist/src/modules/env/env.module.js +17 -0
  181. package/dist/src/modules/env/presentation/env.views.js +43 -0
  182. package/dist/src/modules/files/application/clean-builds.use-case.js +149 -0
  183. package/dist/src/modules/files/application/compare-paths.use-case.js +127 -0
  184. package/dist/src/modules/files/application/copy-paths.use-case.js +101 -0
  185. package/dist/src/modules/files/application/create-paths.use-case.js +85 -0
  186. package/dist/src/modules/files/application/delete-paths.use-case.js +127 -0
  187. package/dist/src/modules/files/application/find-duplicates.use-case.js +105 -0
  188. package/dist/src/modules/files/application/find-files.use-case.js +131 -0
  189. package/dist/src/modules/files/application/hash-files.use-case.js +73 -0
  190. package/dist/src/modules/files/application/list-entries.use-case.js +87 -0
  191. package/dist/src/modules/files/application/measure-folders.use-case.js +102 -0
  192. package/dist/src/modules/files/application/move-entry.js +19 -0
  193. package/dist/src/modules/files/application/move-paths.use-case.js +82 -0
  194. package/dist/src/modules/files/application/read-text.use-case.js +92 -0
  195. package/dist/src/modules/files/application/rename-paths.use-case.js +184 -0
  196. package/dist/src/modules/files/application/replace-text.use-case.js +146 -0
  197. package/dist/src/modules/files/application/search-text.use-case.js +115 -0
  198. package/dist/src/modules/files/application/show-info.use-case.js +80 -0
  199. package/dist/src/modules/files/application/show-tree.use-case.js +75 -0
  200. package/dist/src/modules/files/application/sync-folders.use-case.js +162 -0
  201. package/dist/src/modules/files/application/text-files.js +46 -0
  202. package/dist/src/modules/files/application/transfers.js +35 -0
  203. package/dist/src/modules/files/domain/case-style.js +18 -0
  204. package/dist/src/modules/files/domain/filters.js +9 -0
  205. package/dist/src/modules/files/domain/sync-plan.js +35 -0
  206. package/dist/src/modules/files/domain/text-encoding.js +75 -0
  207. package/dist/src/modules/files/domain/values.js +28 -0
  208. package/dist/src/modules/files/files.module.js +70 -0
  209. package/dist/src/modules/files/presentation/clean.view.js +15 -0
  210. package/dist/src/modules/files/presentation/compare.view.js +50 -0
  211. package/dist/src/modules/files/presentation/copy.view.js +17 -0
  212. package/dist/src/modules/files/presentation/delete.view.js +16 -0
  213. package/dist/src/modules/files/presentation/dupes.view.js +20 -0
  214. package/dist/src/modules/files/presentation/find.view.js +16 -0
  215. package/dist/src/modules/files/presentation/grep.view.js +27 -0
  216. package/dist/src/modules/files/presentation/hash.view.js +15 -0
  217. package/dist/src/modules/files/presentation/info.view.js +36 -0
  218. package/dist/src/modules/files/presentation/list.view.js +17 -0
  219. package/dist/src/modules/files/presentation/move.view.js +14 -0
  220. package/dist/src/modules/files/presentation/new.view.js +8 -0
  221. package/dist/src/modules/files/presentation/read.view.js +6 -0
  222. package/dist/src/modules/files/presentation/rename.view.js +16 -0
  223. package/dist/src/modules/files/presentation/replace.view.js +26 -0
  224. package/dist/src/modules/files/presentation/size.view.js +24 -0
  225. package/dist/src/modules/files/presentation/sync.view.js +37 -0
  226. package/dist/src/modules/files/presentation/tree.view.js +22 -0
  227. package/dist/src/modules/gen/application/gen-options.js +18 -0
  228. package/dist/src/modules/gen/application/generate-passwords.use-case.js +39 -0
  229. package/dist/src/modules/gen/application/generate-tokens.use-case.js +38 -0
  230. package/dist/src/modules/gen/application/generate-ulids.use-case.js +40 -0
  231. package/dist/src/modules/gen/application/generate-uuids.use-case.js +32 -0
  232. package/dist/src/modules/gen/domain/identifiers.js +65 -0
  233. package/dist/src/modules/gen/domain/secrets.js +43 -0
  234. package/dist/src/modules/gen/gen.module.js +20 -0
  235. package/dist/src/modules/git/application/fetch-repositories.use-case.js +50 -0
  236. package/dist/src/modules/git/application/git-client.js +62 -0
  237. package/dist/src/modules/git/application/pull-repositories.use-case.js +67 -0
  238. package/dist/src/modules/git/application/repositories.js +67 -0
  239. package/dist/src/modules/git/application/show-status.use-case.js +49 -0
  240. package/dist/src/modules/git/application/switch-branch.use-case.js +118 -0
  241. package/dist/src/modules/git/domain/repository-status.js +37 -0
  242. package/dist/src/modules/git/git.module.js +25 -0
  243. package/dist/src/modules/git/presentation/git.views.js +100 -0
  244. package/dist/src/modules/net/application/check-connection.use-case.js +71 -0
  245. package/dist/src/modules/net/application/list-addresses.use-case.js +29 -0
  246. package/dist/src/modules/net/application/lookup-name.use-case.js +55 -0
  247. package/dist/src/modules/net/net.module.js +17 -0
  248. package/dist/src/modules/net/presentation/net.views.js +39 -0
  249. package/dist/src/modules/open/application/open-target.use-case.js +53 -0
  250. package/dist/src/modules/open/domain/targets.js +45 -0
  251. package/dist/src/modules/open/open.module.js +12 -0
  252. package/dist/src/modules/open/presentation/open.view.js +6 -0
  253. package/dist/src/modules/port/application/end-port-owners.use-case.js +68 -0
  254. package/dist/src/modules/port/application/find-free-port.use-case.js +40 -0
  255. package/dist/src/modules/port/application/find-listeners.use-case.js +34 -0
  256. package/dist/src/modules/port/application/port-owners.js +25 -0
  257. package/dist/src/modules/port/domain/port-number.js +8 -0
  258. package/dist/src/modules/port/port.module.js +17 -0
  259. package/dist/src/modules/port/presentation/port.views.js +27 -0
  260. package/dist/src/modules/proc/application/end-processes.use-case.js +77 -0
  261. package/dist/src/modules/proc/application/find-processes.use-case.js +50 -0
  262. package/dist/src/modules/proc/application/list-processes.use-case.js +41 -0
  263. package/dist/src/modules/proc/application/show-tree.use-case.js +40 -0
  264. package/dist/src/modules/proc/domain/process-tree.js +55 -0
  265. package/dist/src/modules/proc/presentation/proc.views.js +28 -0
  266. package/dist/src/modules/proc/proc.module.js +19 -0
  267. package/dist/src/modules/sys/application/create-report.use-case.js +30 -0
  268. package/dist/src/modules/sys/application/find-tools.use-case.js +20 -0
  269. package/dist/src/modules/sys/application/machine-facts.js +44 -0
  270. package/dist/src/modules/sys/application/show-info.use-case.js +29 -0
  271. package/dist/src/modules/sys/domain/tools.js +17 -0
  272. package/dist/src/modules/sys/presentation/sys.views.js +67 -0
  273. package/dist/src/modules/sys/sys.module.js +17 -0
  274. package/dist/src/modules/wait/application/poll.js +26 -0
  275. package/dist/src/modules/wait/application/wait-file.use-case.js +55 -0
  276. package/dist/src/modules/wait/application/wait-port.use-case.js +76 -0
  277. package/dist/src/modules/wait/application/wait-url.use-case.js +71 -0
  278. package/dist/src/modules/wait/domain/reasons.js +14 -0
  279. package/dist/src/modules/wait/domain/wait-input.js +50 -0
  280. package/dist/src/modules/wait/presentation/wait.views.js +31 -0
  281. package/dist/src/modules/wait/wait.module.js +21 -0
  282. package/package.json +57 -0
@@ -0,0 +1,201 @@
1
+ import { InterruptedError, KiriyaError, UsageError } from "../../domain/errors.js";
2
+ import { message } from "../../domain/message.js";
3
+ import { Translator } from "../i18n/translator.js";
4
+ import { parseCommandArguments, splitGlobalFlags } from "./argv.js";
5
+ import { CliViewFormat } from "./cli-view-format.js";
6
+ import { MCP_INPUT } from "../mcp/mcp-application.js";
7
+ import { commandHelp, mainHelp, mcpHelp, moduleHelp } from "./help.js";
8
+ import { errorJson, resultJson } from "./json-output.js";
9
+ import { createStyle } from "./style.js";
10
+ import { closest } from "./suggest.js";
11
+ import { TerminalConfirmation } from "./terminal-confirmation.js";
12
+ const EXIT_CODES = {
13
+ usage: 2,
14
+ "not-found": 1,
15
+ conflict: 1,
16
+ refused: 1,
17
+ "capability-unavailable": 1,
18
+ failed: 1,
19
+ interrupted: 130,
20
+ };
21
+ const verbsOf = (module) => [...module.commands.keys()].filter((verb) => verb !== "");
22
+ /** The command line: finds the command, runs it, prints its result, and maps errors to exit codes once. */
23
+ export class CliApplication {
24
+ deps;
25
+ translator;
26
+ constructor(deps) {
27
+ this.deps = deps;
28
+ this.translator = new Translator(deps.catalog);
29
+ }
30
+ async run(argv, cwd) {
31
+ const { flags, rest } = splitGlobalFlags(argv);
32
+ const colour = !flags.noColor &&
33
+ !this.deps.environment.variable("KIRIYA_NO_COLOR") &&
34
+ this.deps.environment.variable("TERM") !== "dumb";
35
+ const out = createStyle(colour, this.deps.stdout);
36
+ const err = createStyle(colour, this.deps.stderr);
37
+ // Help wraps to the terminal's width, and shows the wordmark, only when writing to one.
38
+ const terminal = this.deps.stdout.isTTY === true;
39
+ const columns = this.deps.stdout.columns;
40
+ const help = {
41
+ translator: this.translator,
42
+ style: out,
43
+ version: this.deps.version,
44
+ ...(terminal && typeof columns === "number" && columns > 0 ? { width: columns } : {}),
45
+ logo: terminal && colour,
46
+ };
47
+ try {
48
+ if (flags.version)
49
+ return this.print(this.deps.stdout, [this.deps.version]);
50
+ const [first, ...words] = rest;
51
+ if (first === undefined)
52
+ return this.print(this.deps.stdout, mainHelp(this.deps.registry.list(), help));
53
+ if (first === "help")
54
+ return this.help(words[0], words[1], help);
55
+ if (first === "mcp")
56
+ return await this.mcp(words, cwd, flags.help, help);
57
+ const module = this.findModule(first);
58
+ const { entry, args } = this.resolveCommand(module, words);
59
+ if (entry === undefined)
60
+ return this.print(this.deps.stdout, moduleHelp(module, help));
61
+ if (flags.help)
62
+ return this.print(this.deps.stdout, commandHelp(module, entry, help));
63
+ const { spec } = entry.command;
64
+ const input = spec.input.parse(parseCommandArguments(spec.input, args));
65
+ const controller = new AbortController();
66
+ const interrupt = () => controller.abort();
67
+ process.once("SIGINT", interrupt);
68
+ let result;
69
+ try {
70
+ result = await entry.command.execute(input, {
71
+ cwd,
72
+ signal: controller.signal,
73
+ confirmation: new TerminalConfirmation({
74
+ input: this.deps.stdin,
75
+ output: this.deps.stderr,
76
+ interactive: this.deps.stdin.isTTY === true && !flags.noInput,
77
+ translator: this.translator,
78
+ style: err,
79
+ }),
80
+ passthrough: {
81
+ write: (text, stream) => {
82
+ (flags.json || stream === "stderr" ? this.deps.stderr : this.deps.stdout).write(text);
83
+ },
84
+ },
85
+ });
86
+ }
87
+ finally {
88
+ process.removeListener("SIGINT", interrupt);
89
+ }
90
+ // The first Ctrl+C asks the command to stop; a second one ends the process as usual.
91
+ if (controller.signal.aborted)
92
+ throw new InterruptedError("core.error.interrupted");
93
+ return this.report(spec.id, result, flags, cwd, out, err, entry.view);
94
+ }
95
+ catch (error) {
96
+ return this.fail(error, flags, err);
97
+ }
98
+ }
99
+ /**
100
+ * A verb picks a command. A module that is one command takes every word after its
101
+ * name as arguments. Without either, the module's help is shown.
102
+ */
103
+ resolveCommand(module, words) {
104
+ const [verb, ...rest] = words;
105
+ const named = verb === undefined || verb === "" ? undefined : module.commands.get(verb);
106
+ if (named !== undefined)
107
+ return { entry: named, args: rest };
108
+ const own = module.commands.get("");
109
+ if (own !== undefined)
110
+ return { entry: own, args: words };
111
+ if (verb === undefined)
112
+ return { entry: undefined, args: [] };
113
+ throw this.unknown("core.usage.unknown-command", { module: module.id, name: verb }, verb, verbsOf(module));
114
+ }
115
+ /** `kiriya mcp` serves every exposed command to an AI agent until the agent closes stdin. */
116
+ async mcp(words, cwd, showHelp, context) {
117
+ if (showHelp)
118
+ return this.print(this.deps.stdout, mcpHelp(MCP_INPUT, context));
119
+ return this.deps.serveMcp(MCP_INPUT.parse(parseCommandArguments(MCP_INPUT, words)), cwd);
120
+ }
121
+ help(moduleName, verb, context) {
122
+ if (moduleName === undefined)
123
+ return this.print(this.deps.stdout, mainHelp(this.deps.registry.list(), context));
124
+ if (moduleName === "mcp")
125
+ return this.print(this.deps.stdout, mcpHelp(MCP_INPUT, context));
126
+ const module = this.findModule(moduleName);
127
+ if (verb === undefined) {
128
+ const own = module.commands.get("");
129
+ const lines = own === undefined ? moduleHelp(module, context) : commandHelp(module, own, context);
130
+ return this.print(this.deps.stdout, lines);
131
+ }
132
+ const entry = verb === "" ? undefined : module.commands.get(verb);
133
+ if (entry === undefined) {
134
+ throw this.unknown("core.usage.unknown-command", { module: module.id, name: verb }, verb, verbsOf(module));
135
+ }
136
+ return this.print(this.deps.stdout, commandHelp(module, entry, context));
137
+ }
138
+ findModule(name) {
139
+ const module = this.deps.registry.module(name);
140
+ if (module !== undefined)
141
+ return module;
142
+ throw this.unknown("core.usage.unknown-module", { name }, name, this.deps.registry.list().map((item) => item.id));
143
+ }
144
+ unknown(key, params, input, candidates) {
145
+ const suggestion = closest(input, candidates);
146
+ return new UsageError(key, suggestion === undefined ? params : { ...params, hint: message("core.usage.did-you-mean", { suggestion }) });
147
+ }
148
+ report(commandId, result, flags, cwd, out, err, view) {
149
+ const failures = result.kind === "done" ? result.failures : [];
150
+ if (flags.json) {
151
+ this.print(this.deps.stdout, [resultJson(commandId, result, this.translator)]);
152
+ }
153
+ else {
154
+ this.print(this.deps.stdout, view(result.data, new CliViewFormat(this.translator, out, cwd)));
155
+ for (const warning of result.warnings)
156
+ this.print(this.deps.stderr, [err.yellow(this.translator.text(warning))]);
157
+ for (const failure of failures)
158
+ this.print(this.deps.stderr, [err.red(this.translator.text(failure))]);
159
+ if (result.kind === "preview") {
160
+ const note = result.applyFlag === ""
161
+ ? message("core.result.dry-run")
162
+ : message("core.result.preview", { flag: result.applyFlag });
163
+ this.print(this.deps.stderr, [err.dim(this.translator.text(note))]);
164
+ }
165
+ }
166
+ return failures.length > 0 ? 1 : 0;
167
+ }
168
+ fail(error, flags, err) {
169
+ if (flags.json)
170
+ this.print(this.deps.stdout, [errorJson(error, this.translator)]);
171
+ if (error instanceof KiriyaError) {
172
+ if (!flags.json) {
173
+ const hint = error.detail.params["hint"];
174
+ const lines = [
175
+ `${err.red(this.translator.text(message("core.error.label")))}: ${this.translator.text(error.detail)}`,
176
+ ];
177
+ if (hint !== undefined && typeof hint === "object")
178
+ lines.push(this.translator.text(hint));
179
+ this.print(this.deps.stderr, lines);
180
+ }
181
+ if (flags.debug && error.cause !== undefined) {
182
+ this.print(this.deps.stderr, [String(error.cause.stack ?? error.cause)]);
183
+ }
184
+ return EXIT_CODES[error.kind];
185
+ }
186
+ if (!flags.json) {
187
+ const detail = error instanceof Error ? error.message : String(error);
188
+ this.print(this.deps.stderr, [err.red(this.translator.text(message("core.error.unexpected", { detail })))]);
189
+ if (flags.debug && error instanceof Error && error.stack !== undefined) {
190
+ this.print(this.deps.stderr, [error.stack]);
191
+ }
192
+ }
193
+ return 1;
194
+ }
195
+ print(stream, lines) {
196
+ if (lines.length > 0)
197
+ stream.write(`${lines.join("\n")}\n`);
198
+ return 0;
199
+ }
200
+ }
201
+ //# sourceMappingURL=cli-application.js.map
@@ -0,0 +1,46 @@
1
+ import path from "node:path";
2
+ import { formatBytes } from "../../domain/values/bytes.js";
3
+ export class CliViewFormat {
4
+ translator;
5
+ style;
6
+ cwd;
7
+ constructor(translator, style, cwd) {
8
+ this.translator = translator;
9
+ this.style = style;
10
+ this.cwd = cwd;
11
+ }
12
+ text(value) {
13
+ return this.translator.text(value);
14
+ }
15
+ bold(text) {
16
+ return this.style.bold(text);
17
+ }
18
+ dim(text) {
19
+ return this.style.dim(text);
20
+ }
21
+ red(text) {
22
+ return this.style.red(text);
23
+ }
24
+ green(text) {
25
+ return this.style.green(text);
26
+ }
27
+ yellow(text) {
28
+ return this.style.yellow(text);
29
+ }
30
+ bytes(count) {
31
+ return formatBytes(count);
32
+ }
33
+ /** Local time as YYYY-MM-DD HH:MM. */
34
+ time(epochMs) {
35
+ const date = new Date(epochMs);
36
+ const pad = (value) => String(value).padStart(2, "0");
37
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
38
+ }
39
+ path(absolutePath) {
40
+ const relative = path.relative(this.cwd, absolutePath);
41
+ if (relative === "")
42
+ return ".";
43
+ return relative.startsWith("..") || path.isAbsolute(relative) ? absolutePath : relative;
44
+ }
45
+ }
46
+ //# sourceMappingURL=cli-view-format.js.map
@@ -0,0 +1,202 @@
1
+ import { GLOBAL_OPTIONS } from "../../domain/global-options.js";
2
+ import { message } from "../../domain/message.js";
3
+ /** The list of modules and their guides, for main help to point to. */
4
+ const GUIDES = "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/README.md";
5
+ /** Prose reads best in a column this wide, even in a much wider terminal. */
6
+ const PARAGRAPH_WIDTH = 80;
7
+ /** The width prose wraps to: the terminal when it is narrower than a comfortable column, else 80. */
8
+ function proseWidth(context) {
9
+ return context.width === undefined ? PARAGRAPH_WIDTH : Math.min(context.width, PARAGRAPH_WIDTH);
10
+ }
11
+ function globalLabel(option) {
12
+ return option.short === null ? `--${option.name}` : `-${option.short}, --${option.name}`;
13
+ }
14
+ /**
15
+ * A `--flag` in cyan and a `<value>` in yellow, so an option and its argument stand out in a
16
+ * usage line. Every other character, brackets and spaces included, is left exactly as it was,
17
+ * so with colour off the string is unchanged and stays safe to measure and to pad.
18
+ */
19
+ function colorize(text, style) {
20
+ return text.replace(/(--?[A-Za-z][\w-]*)|(<[^>]*>)/g, (_match, flag, value) => flag !== undefined ? style.cyan(flag) : style.yellow(value ?? ""));
21
+ }
22
+ function table(rows, options = {}) {
23
+ const nameWidth = Math.max(0, ...rows.map(([left]) => left.length));
24
+ const start = 2 + nameWidth + 2;
25
+ const lines = [];
26
+ for (const [left, right] of rows) {
27
+ const name = options.paint === undefined ? left : options.paint(left);
28
+ const padding = " ".repeat(nameWidth - left.length);
29
+ const wrapped = options.width === undefined ? [right] : wrap(right, Math.max(20, options.width - start));
30
+ lines.push(` ${name}${padding} ${wrapped[0] ?? ""}`);
31
+ for (const line of wrapped.slice(1))
32
+ lines.push(`${" ".repeat(start)}${line}`);
33
+ }
34
+ return lines;
35
+ }
36
+ /** Text broken between words into lines of at most `width` characters, unless one word is longer. */
37
+ function wrap(text, width) {
38
+ const lines = [];
39
+ let line = "";
40
+ for (const word of text.split(/\s+/)) {
41
+ if (word === "")
42
+ continue;
43
+ if (line === "") {
44
+ line = word;
45
+ }
46
+ else if (line.length + 1 + word.length > width) {
47
+ lines.push(line);
48
+ line = word;
49
+ }
50
+ else {
51
+ line = `${line} ${word}`;
52
+ }
53
+ }
54
+ return line === "" ? lines : [...lines, line];
55
+ }
56
+ /** A paragraph of prose, always wrapped for readability (the module `about` text). */
57
+ function paragraph(text, context) {
58
+ return wrap(text, proseWidth(context));
59
+ }
60
+ /**
61
+ * A line of prose that was one line before: wrapped only to a known terminal width, and left
62
+ * whole for piped output, so a script or a doc reads the summary and each hint on one line.
63
+ */
64
+ function flow(text, context) {
65
+ return context.width === undefined ? [text] : wrap(text, proseWidth(context));
66
+ }
67
+ /** A small wordmark for a terminal: the name in a rounded box, then the version. */
68
+ function wordmark(context) {
69
+ const { style } = context;
70
+ const inner = " kiriya ";
71
+ const bar = "─".repeat(inner.length);
72
+ return [
73
+ style.cyan(`╭${bar}╮`),
74
+ `${style.cyan("│ ")}${style.bold(style.cyan("kiriya"))}${style.cyan(" │")}`,
75
+ style.cyan(`╰${bar}╯`),
76
+ style.dim(`v${context.version}`),
77
+ ];
78
+ }
79
+ /** `-i, --ignore-case` or `--to <file>`, as help and the generated docs show an option. */
80
+ export function optionLabel(name, spec) {
81
+ const value = spec.type === "string" ? ` ${spec.valueName ?? "<value>"}` : "";
82
+ const short = spec.short === undefined ? "" : `-${spec.short}, `;
83
+ return `${short}--${name}${value}`;
84
+ }
85
+ /** `kiriya files list`, or `kiriya doctor` for a module that is one command. */
86
+ function commandName(module, entry) {
87
+ return ["kiriya", module.id, entry.verb].filter((part) => part !== "").join(" ");
88
+ }
89
+ /** How a command is typed, such as `kiriya files delete <paths...> [options]`. */
90
+ export function usageLine(module, entry) {
91
+ const { input } = entry.command.spec;
92
+ const positionals = input.positionals.map((positional) => {
93
+ const label = positional.variadic ? `${positional.name}...` : positional.name;
94
+ return positional.required ? `<${label}>` : `[${label}]`;
95
+ });
96
+ const options = Object.keys(input.options).length > 0 ? ["[options]"] : [];
97
+ return [commandName(module, entry), ...positionals, ...options].join(" ");
98
+ }
99
+ function aboutLines(module, context) {
100
+ return module.about === null ? [] : ["", ...paragraph(context.translator.text(message(module.about)), context)];
101
+ }
102
+ function exampleLines(examples, context) {
103
+ if (examples.length === 0)
104
+ return [];
105
+ const heading = context.style.bold(context.translator.text(message("core.help.examples")));
106
+ return ["", heading, ...examples.map((example) => ` ${colorize(example, context.style)}`)];
107
+ }
108
+ function guideLines(module, context) {
109
+ return module.guide === null
110
+ ? []
111
+ : flow(context.translator.text(message("core.help.guide", { address: module.guide })), context);
112
+ }
113
+ /** An options table, its labels coloured and its descriptions wrapped to the terminal. */
114
+ function optionsTable(options, context) {
115
+ return table(Object.entries(options).map(([name, spec]) => [optionLabel(name, spec), context.translator.text(message(spec.description))]), { width: context.width, paint: (label) => colorize(label, context.style) });
116
+ }
117
+ function argumentsTable(input, context) {
118
+ return table(input.positionals.map((positional) => [positional.name, context.translator.text(message(positional.description))]), { width: context.width, paint: (name) => context.style.yellow(name) });
119
+ }
120
+ export function mainHelp(modules, context) {
121
+ const { translator, style } = context;
122
+ const heading = context.logo === true ? wordmark(context) : [`${style.bold("kiriya")} ${context.version}`];
123
+ return [
124
+ ...heading,
125
+ ...flow(translator.text(message("core.app.summary")), context),
126
+ "",
127
+ style.bold(translator.text(message("core.help.usage"))),
128
+ ` ${colorize("kiriya <module> <command> [arguments] [options]", style)}`,
129
+ "",
130
+ style.bold(translator.text(message("core.help.modules"))),
131
+ ...table(modules.map((module) => [module.id, translator.text(message(module.summary))]), { width: context.width, paint: (id) => style.green(id) }),
132
+ "",
133
+ style.bold(translator.text(message("core.help.global-options"))),
134
+ ...table(GLOBAL_OPTIONS.map((option) => [globalLabel(option), translator.text(message(option.description))]), { width: context.width, paint: (label) => colorize(label, style) }),
135
+ "",
136
+ ...flow(translator.text(message("core.help.module-hint", { command: "kiriya help <module>" })), context),
137
+ ...flow(translator.text(message("core.help.mcp-hint", { command: "kiriya mcp --help" })), context),
138
+ ...flow(translator.text(message("core.help.guides", { address: GUIDES })), context),
139
+ ];
140
+ }
141
+ /** `kiriya mcp`, which serves the commands to AI agents instead of being one of them. */
142
+ export function mcpHelp(input, context) {
143
+ const { translator, style } = context;
144
+ return [
145
+ `${style.bold("kiriya mcp")} — ${translator.text(message("core.mcp.summary"))}`,
146
+ "",
147
+ style.bold(translator.text(message("core.help.usage"))),
148
+ ` ${colorize("kiriya mcp [folders...] [--root <folder>]...", style)}`,
149
+ "",
150
+ style.bold(translator.text(message("core.help.arguments"))),
151
+ ...argumentsTable(input, context),
152
+ "",
153
+ style.bold(translator.text(message("core.help.options"))),
154
+ ...optionsTable(input.options, context),
155
+ "",
156
+ ...flow(translator.text(message("core.mcp.help-details")), context),
157
+ ];
158
+ }
159
+ export function moduleHelp(module, context) {
160
+ const { translator, style } = context;
161
+ const commands = [...module.commands.values()]
162
+ .filter((entry) => entry.verb !== "")
163
+ .sort((a, b) => (a.verb < b.verb ? -1 : a.verb > b.verb ? 1 : 0));
164
+ return [
165
+ `${style.bold(`kiriya ${module.id}`)} — ${translator.text(message(module.summary))}`,
166
+ ...aboutLines(module, context),
167
+ "",
168
+ style.bold(translator.text(message("core.help.commands"))),
169
+ ...table(commands.map((entry) => [entry.verb, translator.text(message(entry.command.spec.summary))]), { width: context.width, paint: (verb) => style.green(verb) }),
170
+ ...exampleLines(module.examples, context),
171
+ "",
172
+ ...guideLines(module, context),
173
+ ...flow(translator.text(message("core.help.command-hint", { command: `kiriya help ${module.id} <command>` })), context),
174
+ ];
175
+ }
176
+ export function commandHelp(module, entry, context) {
177
+ const { translator, style } = context;
178
+ const { spec } = entry.command;
179
+ // A module that is one command has no help of its own, so its command's help also explains the module.
180
+ const own = entry.verb === "";
181
+ const lines = [
182
+ `${style.bold(commandName(module, entry))} — ${translator.text(message(spec.summary))}`,
183
+ ...(own ? aboutLines(module, context) : []),
184
+ "",
185
+ style.bold(translator.text(message("core.help.usage"))),
186
+ ` ${colorize(usageLine(module, entry), style)}`,
187
+ ];
188
+ if (spec.input.positionals.length > 0) {
189
+ lines.push("", style.bold(translator.text(message("core.help.arguments"))));
190
+ lines.push(...argumentsTable(spec.input, context));
191
+ }
192
+ if (Object.keys(spec.input.options).length > 0) {
193
+ lines.push("", style.bold(translator.text(message("core.help.options"))));
194
+ lines.push(...optionsTable(spec.input.options, context));
195
+ }
196
+ lines.push(...exampleLines(spec.examples, context));
197
+ const guide = own ? guideLines(module, context) : [];
198
+ if (guide.length > 0)
199
+ lines.push("", ...guide);
200
+ return lines;
201
+ }
202
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1,28 @@
1
+ import { KiriyaError } from "../../domain/errors.js";
2
+ import { isMessage } from "../../domain/message.js";
3
+ /**
4
+ * JSON keeps every message's key and parameters, so scripts never depend on wording,
5
+ * and adds its text. Nested messages in parameters get the same treatment on the way down.
6
+ */
7
+ function stringify(value, translator) {
8
+ return JSON.stringify(value, (_key, item) => isMessage(item) && !("text" in item) ? { key: item.key, params: item.params, text: translator.text(item) } : item, 2);
9
+ }
10
+ export function resultJson(commandId, result, translator) {
11
+ const failures = result.kind === "done" ? result.failures : [];
12
+ return stringify({
13
+ ok: failures.length === 0,
14
+ command: commandId,
15
+ kind: result.kind,
16
+ ...(result.kind === "preview" ? { applyFlag: result.applyFlag } : {}),
17
+ data: result.data,
18
+ warnings: result.warnings,
19
+ failures,
20
+ }, translator);
21
+ }
22
+ export function errorJson(error, translator) {
23
+ if (error instanceof KiriyaError) {
24
+ return stringify({ ok: false, error: { kind: error.kind, message: error.detail } }, translator);
25
+ }
26
+ return stringify({ ok: false, error: { kind: "unexpected", message: String(error) } }, translator);
27
+ }
28
+ //# sourceMappingURL=json-output.js.map
@@ -0,0 +1,21 @@
1
+ import { styleText } from "node:util";
2
+ const plain = (text) => text;
3
+ /**
4
+ * Colour through util.styleText, which already turns it off for a stream that is
5
+ * not a terminal, for NO_COLOR and NODE_DISABLE_COLORS, and on for FORCE_COLOR.
6
+ * `enabled` adds kiriya's own switches: --no-color, KIRIYA_NO_COLOR and TERM=dumb.
7
+ */
8
+ export function createStyle(enabled, stream) {
9
+ if (!enabled)
10
+ return { bold: plain, dim: plain, red: plain, green: plain, yellow: plain, cyan: plain };
11
+ const paint = (format) => (text) => styleText(format, text, { stream });
12
+ return {
13
+ bold: paint("bold"),
14
+ dim: paint("dim"),
15
+ red: paint("red"),
16
+ green: paint("green"),
17
+ yellow: paint("yellow"),
18
+ cyan: paint("cyan"),
19
+ };
20
+ }
21
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1,25 @@
1
+ function distance(a, b) {
2
+ const previous = Array.from({ length: b.length + 1 }, (_, index) => index);
3
+ for (let i = 1; i <= a.length; i += 1) {
4
+ let diagonal = previous[0] ?? 0;
5
+ previous[0] = i;
6
+ for (let j = 1; j <= b.length; j += 1) {
7
+ const above = previous[j] ?? 0;
8
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
9
+ previous[j] = Math.min(above + 1, (previous[j - 1] ?? 0) + 1, diagonal + cost);
10
+ diagonal = above;
11
+ }
12
+ }
13
+ return previous[b.length] ?? 0;
14
+ }
15
+ /** The closest known name within two edits, for "did you mean". */
16
+ export function closest(input, candidates) {
17
+ let best;
18
+ for (const candidate of candidates) {
19
+ const score = distance(input.toLowerCase(), candidate.toLowerCase());
20
+ if (score <= 2 && (best === undefined || score < best.score))
21
+ best = { name: candidate, score };
22
+ }
23
+ return best?.name;
24
+ }
25
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1,63 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { InterruptedError } from "../../domain/errors.js";
3
+ import { message } from "../../domain/message.js";
4
+ export class TerminalConfirmation {
5
+ options;
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ async approve(question, assumeYes) {
10
+ if (assumeYes)
11
+ return true;
12
+ if (!this.options.interactive) {
13
+ this.write(this.text(message("core.confirm.no-terminal-yes")));
14
+ return false;
15
+ }
16
+ const answer = await this.ask(`${this.text(question)} ${this.text(message("core.confirm.yes-no"))} `);
17
+ return answer.trim().toLowerCase() === "y";
18
+ }
19
+ async typed(warning, expected, provided) {
20
+ if (provided !== undefined)
21
+ return provided === expected;
22
+ if (!this.options.interactive) {
23
+ this.write(this.text(message("core.confirm.no-terminal-typed", { flag: `--confirm=${expected}` })));
24
+ return false;
25
+ }
26
+ this.write(`${this.options.style.red(this.text(message("core.confirm.cannot-undo")))} ${this.text(warning)}`);
27
+ const answer = await this.ask(`${this.text(message("core.confirm.type", { expected: this.options.style.bold(expected) }))} `);
28
+ return answer.trim() === expected;
29
+ }
30
+ text(value) {
31
+ return this.options.translator.text(value);
32
+ }
33
+ write(line) {
34
+ this.options.output.write(`${line}\n`);
35
+ }
36
+ /** Ctrl+C at the prompt interrupts the command; input that ends without an answer declines. */
37
+ async ask(prompt) {
38
+ const readline = createInterface({ input: this.options.input, output: this.options.output });
39
+ const controller = new AbortController();
40
+ let interrupted = false;
41
+ readline.once("SIGINT", () => {
42
+ interrupted = true;
43
+ controller.abort();
44
+ });
45
+ readline.once("close", () => controller.abort());
46
+ try {
47
+ return await readline.question(prompt, { signal: controller.signal });
48
+ }
49
+ catch (error) {
50
+ if (interrupted) {
51
+ this.options.output.write("\n");
52
+ throw new InterruptedError("core.error.interrupted");
53
+ }
54
+ if (controller.signal.aborted)
55
+ return "";
56
+ throw error;
57
+ }
58
+ finally {
59
+ readline.close();
60
+ }
61
+ }
62
+ }
63
+ //# sourceMappingURL=terminal-confirmation.js.map
@@ -0,0 +1,18 @@
1
+ import { message } from "../domain/message.js";
2
+ /** One line per process that ended; the others arrive as failures and warnings. */
3
+ export function endedLines(processes, format) {
4
+ return processes
5
+ .filter((entry) => entry.outcome === "ended")
6
+ .map((entry) => format.green(format.text(message("core.end.ended", { name: entry.name ?? "?", pid: entry.pid }))));
7
+ }
8
+ /** Rows as aligned columns two spaces apart; the last column is not padded, so long text can run on. */
9
+ export function columns(rows) {
10
+ const widths = [];
11
+ for (const row of rows) {
12
+ row.forEach((cell, index) => {
13
+ widths[index] = Math.max(widths[index] ?? 0, cell.length);
14
+ });
15
+ }
16
+ return rows.map((row) => ` ${row.map((cell, index) => (index === row.length - 1 ? cell : cell.padEnd(widths[index] ?? 0))).join(" ")}`.trimEnd());
17
+ }
18
+ //# sourceMappingURL=ended-processes.js.map
@@ -0,0 +1,23 @@
1
+ import { isMessage } from "../../domain/message.js";
2
+ const PLACEHOLDER = /\{([A-Za-z0-9]+)\}/g;
3
+ /**
4
+ * Turns messages into text; nested messages in parameters are translated too. The
5
+ * catalog is kiriya's own plus the messages of loaded plugins, so a key missing from
6
+ * it is shown as the key rather than failing.
7
+ */
8
+ export class Translator {
9
+ catalog;
10
+ constructor(catalog) {
11
+ this.catalog = catalog;
12
+ }
13
+ text(value) {
14
+ const template = this.catalog[value.key] ?? value.key;
15
+ return template.replace(PLACEHOLDER, (whole, name) => {
16
+ const param = value.params[name];
17
+ if (param === undefined)
18
+ return whole;
19
+ return isMessage(param) ? this.text(param) : String(param);
20
+ });
21
+ }
22
+ }
23
+ //# sourceMappingURL=translator.js.map
@@ -0,0 +1,10 @@
1
+ import { message } from "../domain/message.js";
2
+ /** The first `limit` items as lines, then one line saying how many more there are. */
3
+ export function previewLines(items, limit, render, format) {
4
+ const lines = items.slice(0, limit).map(render);
5
+ if (items.length > limit) {
6
+ lines.push(format.dim(format.text(message("core.list.more", { count: items.length - limit }))));
7
+ }
8
+ return lines;
9
+ }
10
+ //# sourceMappingURL=list-preview.js.map