@knpkv/confluence-to-markdown 0.6.0 → 0.7.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 (357) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +22 -13
  4. package/dist/AdfPlaceholders.d.ts +42 -0
  5. package/dist/AdfPlaceholders.d.ts.map +1 -0
  6. package/dist/AdfPlaceholders.js +547 -0
  7. package/dist/AdfPlaceholders.js.map +1 -0
  8. package/dist/AdfSchemaValidator.d.ts +37 -0
  9. package/dist/AdfSchemaValidator.d.ts.map +1 -0
  10. package/dist/AdfSchemaValidator.js +37 -0
  11. package/dist/AdfSchemaValidator.js.map +1 -0
  12. package/dist/AdfWalker.d.ts +39 -0
  13. package/dist/AdfWalker.d.ts.map +1 -0
  14. package/dist/AdfWalker.js +527 -0
  15. package/dist/AdfWalker.js.map +1 -0
  16. package/dist/AtlaskitTransformers.d.ts +35 -0
  17. package/dist/AtlaskitTransformers.d.ts.map +1 -0
  18. package/dist/AtlaskitTransformers.js +48 -0
  19. package/dist/AtlaskitTransformers.js.map +1 -0
  20. package/dist/Brand.d.ts +6 -6
  21. package/dist/Brand.d.ts.map +1 -1
  22. package/dist/Brand.js +8 -6
  23. package/dist/Brand.js.map +1 -1
  24. package/dist/ConfluenceAuth.d.ts +4 -4
  25. package/dist/ConfluenceAuth.d.ts.map +1 -1
  26. package/dist/ConfluenceAuth.js +37 -39
  27. package/dist/ConfluenceAuth.js.map +1 -1
  28. package/dist/ConfluenceClient.d.ts +7 -17
  29. package/dist/ConfluenceClient.d.ts.map +1 -1
  30. package/dist/ConfluenceClient.js +81 -38
  31. package/dist/ConfluenceClient.js.map +1 -1
  32. package/dist/ConfluenceConfig.d.ts +3 -3
  33. package/dist/ConfluenceConfig.d.ts.map +1 -1
  34. package/dist/ConfluenceConfig.js +13 -11
  35. package/dist/ConfluenceConfig.js.map +1 -1
  36. package/dist/ConfluenceError.d.ts +68 -16
  37. package/dist/ConfluenceError.d.ts.map +1 -1
  38. package/dist/ConfluenceError.js +30 -1
  39. package/dist/ConfluenceError.js.map +1 -1
  40. package/dist/GitError.d.ts +5 -5
  41. package/dist/GitService.d.ts +11 -3
  42. package/dist/GitService.d.ts.map +1 -1
  43. package/dist/GitService.js +22 -27
  44. package/dist/GitService.js.map +1 -1
  45. package/dist/LocalFileSystem.d.ts +3 -3
  46. package/dist/LocalFileSystem.d.ts.map +1 -1
  47. package/dist/LocalFileSystem.js +6 -6
  48. package/dist/LocalFileSystem.js.map +1 -1
  49. package/dist/MarkdownConverter.d.ts +16 -65
  50. package/dist/MarkdownConverter.d.ts.map +1 -1
  51. package/dist/MarkdownConverter.js +64 -85
  52. package/dist/MarkdownConverter.js.map +1 -1
  53. package/dist/Schemas.d.ts +128 -141
  54. package/dist/Schemas.d.ts.map +1 -1
  55. package/dist/Schemas.js +21 -23
  56. package/dist/Schemas.js.map +1 -1
  57. package/dist/SyncEngine.d.ts +8 -5
  58. package/dist/SyncEngine.d.ts.map +1 -1
  59. package/dist/SyncEngine.js +189 -113
  60. package/dist/SyncEngine.js.map +1 -1
  61. package/dist/bin.js +23 -35
  62. package/dist/bin.js.map +1 -1
  63. package/dist/commands/auth.d.ts +2 -14
  64. package/dist/commands/auth.d.ts.map +1 -1
  65. package/dist/commands/auth.js +11 -16
  66. package/dist/commands/auth.js.map +1 -1
  67. package/dist/commands/clone.d.ts +4 -6
  68. package/dist/commands/clone.d.ts.map +1 -1
  69. package/dist/commands/clone.js +34 -32
  70. package/dist/commands/clone.js.map +1 -1
  71. package/dist/commands/delete.d.ts +2 -10
  72. package/dist/commands/delete.d.ts.map +1 -1
  73. package/dist/commands/delete.js +5 -4
  74. package/dist/commands/delete.js.map +1 -1
  75. package/dist/commands/errorHandler.d.ts +2 -1
  76. package/dist/commands/errorHandler.d.ts.map +1 -1
  77. package/dist/commands/errorHandler.js +22 -15
  78. package/dist/commands/errorHandler.js.map +1 -1
  79. package/dist/commands/fetch.d.ts +27 -0
  80. package/dist/commands/fetch.d.ts.map +1 -0
  81. package/dist/commands/fetch.js +48 -0
  82. package/dist/commands/fetch.js.map +1 -0
  83. package/dist/commands/git.d.ts +7 -10
  84. package/dist/commands/git.d.ts.map +1 -1
  85. package/dist/commands/git.js +6 -6
  86. package/dist/commands/git.js.map +1 -1
  87. package/dist/commands/index.d.ts +1 -0
  88. package/dist/commands/index.d.ts.map +1 -1
  89. package/dist/commands/index.js +1 -0
  90. package/dist/commands/index.js.map +1 -1
  91. package/dist/commands/layers.d.ts +10 -9
  92. package/dist/commands/layers.d.ts.map +1 -1
  93. package/dist/commands/layers.js +41 -30
  94. package/dist/commands/layers.js.map +1 -1
  95. package/dist/commands/new.d.ts +2 -6
  96. package/dist/commands/new.d.ts.map +1 -1
  97. package/dist/commands/new.js +5 -4
  98. package/dist/commands/new.js.map +1 -1
  99. package/dist/commands/pageInput.d.ts +19 -0
  100. package/dist/commands/pageInput.d.ts.map +1 -0
  101. package/dist/commands/pageInput.js +68 -0
  102. package/dist/commands/pageInput.js.map +1 -0
  103. package/dist/commands/root.d.ts +8 -0
  104. package/dist/commands/root.d.ts.map +1 -0
  105. package/dist/commands/root.js +29 -0
  106. package/dist/commands/root.js.map +1 -0
  107. package/dist/commands/sync.d.ts +6 -9
  108. package/dist/commands/sync.d.ts.map +1 -1
  109. package/dist/commands/sync.js +5 -6
  110. package/dist/commands/sync.js.map +1 -1
  111. package/dist/index.d.ts +3 -8
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -13
  114. package/dist/index.js.map +1 -1
  115. package/dist/internal/NodeLayers.d.ts.map +1 -1
  116. package/dist/internal/NodeLayers.js +1 -2
  117. package/dist/internal/NodeLayers.js.map +1 -1
  118. package/dist/internal/adfMetadata.d.ts +30 -0
  119. package/dist/internal/adfMetadata.d.ts.map +1 -0
  120. package/dist/internal/adfMetadata.js +126 -0
  121. package/dist/internal/adfMetadata.js.map +1 -0
  122. package/dist/internal/cleanMarkdown.d.ts +5 -0
  123. package/dist/internal/cleanMarkdown.d.ts.map +1 -0
  124. package/dist/internal/cleanMarkdown.js +13 -0
  125. package/dist/internal/cleanMarkdown.js.map +1 -0
  126. package/dist/internal/frontmatter.d.ts.map +1 -1
  127. package/dist/internal/frontmatter.js +41 -8
  128. package/dist/internal/frontmatter.js.map +1 -1
  129. package/dist/internal/gitCommands.d.ts +9 -3
  130. package/dist/internal/gitCommands.d.ts.map +1 -1
  131. package/dist/internal/gitCommands.js +18 -9
  132. package/dist/internal/gitCommands.js.map +1 -1
  133. package/dist/internal/hashUtils.d.ts +1 -1
  134. package/dist/internal/hashUtils.d.ts.map +1 -1
  135. package/dist/internal/hashUtils.js +1 -1
  136. package/dist/internal/hashUtils.js.map +1 -1
  137. package/dist/internal/oauthServer.d.ts +10 -5
  138. package/dist/internal/oauthServer.d.ts.map +1 -1
  139. package/dist/internal/oauthServer.js +19 -40
  140. package/dist/internal/oauthServer.js.map +1 -1
  141. package/dist/internal/pathUtils.d.ts +1 -1
  142. package/dist/internal/pathUtils.d.ts.map +1 -1
  143. package/dist/internal/pathUtils.js +1 -1
  144. package/dist/internal/pathUtils.js.map +1 -1
  145. package/dist/internal/process.d.ts +15 -0
  146. package/dist/internal/process.d.ts.map +1 -0
  147. package/dist/internal/process.js +10 -0
  148. package/dist/internal/process.js.map +1 -0
  149. package/dist/internal/stdio.d.ts +6 -0
  150. package/dist/internal/stdio.d.ts.map +1 -0
  151. package/dist/internal/stdio.js +15 -0
  152. package/dist/internal/stdio.js.map +1 -0
  153. package/dist/internal/tokenStorage.d.ts +3 -13
  154. package/dist/internal/tokenStorage.d.ts.map +1 -1
  155. package/dist/internal/tokenStorage.js +26 -24
  156. package/dist/internal/tokenStorage.js.map +1 -1
  157. package/dist/internal/userCache.d.ts +1 -1
  158. package/dist/internal/userCache.d.ts.map +1 -1
  159. package/dist/internal/userCache.js +1 -1
  160. package/dist/internal/userCache.js.map +1 -1
  161. package/package.json +30 -30
  162. package/skills/confluence/SKILL.md +143 -0
  163. package/skills/confluence/agents/openai.yaml +4 -0
  164. package/src/AdfPlaceholders.ts +310 -13
  165. package/src/AdfSchemaValidator.ts +2 -4
  166. package/src/AdfWalker.ts +122 -42
  167. package/src/AtlaskitTransformers.ts +2 -4
  168. package/src/Brand.ts +11 -16
  169. package/src/ConfluenceAuth.ts +22 -30
  170. package/src/ConfluenceClient.ts +24 -20
  171. package/src/ConfluenceConfig.ts +14 -14
  172. package/src/GitService.ts +39 -49
  173. package/src/LocalFileSystem.ts +7 -9
  174. package/src/MarkdownConverter.ts +2 -4
  175. package/src/Schemas.ts +13 -12
  176. package/src/SyncEngine.ts +151 -53
  177. package/src/bin.ts +30 -56
  178. package/src/commands/auth.ts +21 -18
  179. package/src/commands/clone.ts +38 -37
  180. package/src/commands/delete.ts +5 -4
  181. package/src/commands/errorHandler.ts +25 -18
  182. package/src/commands/fetch.ts +90 -0
  183. package/src/commands/git.ts +6 -6
  184. package/src/commands/index.ts +1 -0
  185. package/src/commands/layers.ts +53 -33
  186. package/src/commands/new.ts +5 -4
  187. package/src/commands/pageInput.ts +103 -0
  188. package/src/commands/root.ts +59 -0
  189. package/src/commands/sync.ts +7 -6
  190. package/src/internal/NodeLayers.ts +1 -2
  191. package/src/internal/adfMetadata.ts +145 -0
  192. package/src/internal/cleanMarkdown.ts +15 -0
  193. package/src/internal/frontmatter.ts +45 -8
  194. package/src/internal/gitCommands.ts +23 -17
  195. package/src/internal/hashUtils.ts +2 -2
  196. package/src/internal/oauthServer.ts +84 -105
  197. package/src/internal/pathUtils.ts +1 -1
  198. package/src/internal/process.ts +15 -0
  199. package/src/internal/stdio.ts +22 -0
  200. package/src/internal/tokenStorage.ts +39 -29
  201. package/src/internal/userCache.ts +2 -2
  202. package/test/AdfPlaceholders.test.ts +213 -0
  203. package/test/AdfSchemaValidator.test.ts +6 -6
  204. package/test/AdfWalker.test.ts +167 -21
  205. package/test/AtlaskitTransformers.test.ts +4 -4
  206. package/test/Brand.test.ts +11 -11
  207. package/test/GitService.test.ts +6 -2
  208. package/test/MarkdownConverter.test.ts +12 -11
  209. package/test/RoundTrip.test.ts +258 -3
  210. package/test/Schemas.test.ts +40 -40
  211. package/test/adfMetadata.test.ts +110 -0
  212. package/test/cleanMarkdown.test.ts +36 -0
  213. package/test/commandHarness.test.ts +79 -0
  214. package/test/commandHarness.ts +147 -0
  215. package/test/fetch.test.ts +61 -0
  216. package/test/frontmatter.test.ts +41 -0
  217. package/test/integration.test.ts +569 -156
  218. package/test/layers.test.ts +12 -0
  219. package/test/oauthServer.test.ts +4 -5
  220. package/test/pageInput.test.ts +83 -0
  221. package/test/tokenStorage.test.ts +17 -17
  222. package/dist/SchemaConverterError.d.ts +0 -108
  223. package/dist/SchemaConverterError.d.ts.map +0 -1
  224. package/dist/SchemaConverterError.js +0 -84
  225. package/dist/SchemaConverterError.js.map +0 -1
  226. package/dist/ast/BlockNode.d.ts +0 -468
  227. package/dist/ast/BlockNode.d.ts.map +0 -1
  228. package/dist/ast/BlockNode.js +0 -319
  229. package/dist/ast/BlockNode.js.map +0 -1
  230. package/dist/ast/Document.d.ts +0 -244
  231. package/dist/ast/Document.d.ts.map +0 -1
  232. package/dist/ast/Document.js +0 -69
  233. package/dist/ast/Document.js.map +0 -1
  234. package/dist/ast/InlineNode.d.ts +0 -477
  235. package/dist/ast/InlineNode.d.ts.map +0 -1
  236. package/dist/ast/InlineNode.js +0 -263
  237. package/dist/ast/InlineNode.js.map +0 -1
  238. package/dist/ast/MacroNode.d.ts +0 -267
  239. package/dist/ast/MacroNode.d.ts.map +0 -1
  240. package/dist/ast/MacroNode.js +0 -164
  241. package/dist/ast/MacroNode.js.map +0 -1
  242. package/dist/ast/index.d.ts +0 -10
  243. package/dist/ast/index.d.ts.map +0 -1
  244. package/dist/ast/index.js +0 -14
  245. package/dist/ast/index.js.map +0 -1
  246. package/dist/parsers/ConfluenceParser.d.ts +0 -26
  247. package/dist/parsers/ConfluenceParser.d.ts.map +0 -1
  248. package/dist/parsers/ConfluenceParser.js +0 -792
  249. package/dist/parsers/ConfluenceParser.js.map +0 -1
  250. package/dist/parsers/MarkdownParser.d.ts +0 -26
  251. package/dist/parsers/MarkdownParser.d.ts.map +0 -1
  252. package/dist/parsers/MarkdownParser.js +0 -873
  253. package/dist/parsers/MarkdownParser.js.map +0 -1
  254. package/dist/parsers/index.d.ts +0 -8
  255. package/dist/parsers/index.d.ts.map +0 -1
  256. package/dist/parsers/index.js +0 -8
  257. package/dist/parsers/index.js.map +0 -1
  258. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +0 -23
  259. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +0 -1
  260. package/dist/parsers/preprocessing/ConfluencePreprocessing.js +0 -323
  261. package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +0 -1
  262. package/dist/parsers/preprocessing/index.d.ts +0 -7
  263. package/dist/parsers/preprocessing/index.d.ts.map +0 -1
  264. package/dist/parsers/preprocessing/index.js +0 -7
  265. package/dist/parsers/preprocessing/index.js.map +0 -1
  266. package/dist/schemas/ConfluenceSchema.d.ts +0 -21
  267. package/dist/schemas/ConfluenceSchema.d.ts.map +0 -1
  268. package/dist/schemas/ConfluenceSchema.js +0 -38
  269. package/dist/schemas/ConfluenceSchema.js.map +0 -1
  270. package/dist/schemas/ConversionSchema.d.ts +0 -35
  271. package/dist/schemas/ConversionSchema.d.ts.map +0 -1
  272. package/dist/schemas/ConversionSchema.js +0 -208
  273. package/dist/schemas/ConversionSchema.js.map +0 -1
  274. package/dist/schemas/MarkdownSchema.d.ts +0 -21
  275. package/dist/schemas/MarkdownSchema.d.ts.map +0 -1
  276. package/dist/schemas/MarkdownSchema.js +0 -38
  277. package/dist/schemas/MarkdownSchema.js.map +0 -1
  278. package/dist/schemas/hast/HastFromHtml.d.ts +0 -27
  279. package/dist/schemas/hast/HastFromHtml.d.ts.map +0 -1
  280. package/dist/schemas/hast/HastFromHtml.js +0 -107
  281. package/dist/schemas/hast/HastFromHtml.js.map +0 -1
  282. package/dist/schemas/hast/HastSchema.d.ts +0 -195
  283. package/dist/schemas/hast/HastSchema.d.ts.map +0 -1
  284. package/dist/schemas/hast/HastSchema.js +0 -183
  285. package/dist/schemas/hast/HastSchema.js.map +0 -1
  286. package/dist/schemas/hast/index.d.ts +0 -9
  287. package/dist/schemas/hast/index.d.ts.map +0 -1
  288. package/dist/schemas/hast/index.js +0 -3
  289. package/dist/schemas/hast/index.js.map +0 -1
  290. package/dist/schemas/index.d.ts +0 -14
  291. package/dist/schemas/index.d.ts.map +0 -1
  292. package/dist/schemas/index.js +0 -16
  293. package/dist/schemas/index.js.map +0 -1
  294. package/dist/schemas/mdast/MdastFromMarkdown.d.ts +0 -30
  295. package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +0 -1
  296. package/dist/schemas/mdast/MdastFromMarkdown.js +0 -79
  297. package/dist/schemas/mdast/MdastFromMarkdown.js.map +0 -1
  298. package/dist/schemas/mdast/MdastSchema.d.ts +0 -385
  299. package/dist/schemas/mdast/MdastSchema.d.ts.map +0 -1
  300. package/dist/schemas/mdast/MdastSchema.js +0 -266
  301. package/dist/schemas/mdast/MdastSchema.js.map +0 -1
  302. package/dist/schemas/mdast/index.d.ts +0 -10
  303. package/dist/schemas/mdast/index.d.ts.map +0 -1
  304. package/dist/schemas/mdast/index.js +0 -4
  305. package/dist/schemas/mdast/index.js.map +0 -1
  306. package/dist/schemas/mdast/mdastToString.d.ts +0 -13
  307. package/dist/schemas/mdast/mdastToString.d.ts.map +0 -1
  308. package/dist/schemas/mdast/mdastToString.js +0 -85
  309. package/dist/schemas/mdast/mdastToString.js.map +0 -1
  310. package/dist/schemas/nodes/block/BlockSchema.d.ts +0 -43
  311. package/dist/schemas/nodes/block/BlockSchema.d.ts.map +0 -1
  312. package/dist/schemas/nodes/block/BlockSchema.js +0 -634
  313. package/dist/schemas/nodes/block/BlockSchema.js.map +0 -1
  314. package/dist/schemas/nodes/block/index.d.ts +0 -7
  315. package/dist/schemas/nodes/block/index.d.ts.map +0 -1
  316. package/dist/schemas/nodes/block/index.js +0 -7
  317. package/dist/schemas/nodes/block/index.js.map +0 -1
  318. package/dist/schemas/nodes/index.d.ts +0 -9
  319. package/dist/schemas/nodes/index.d.ts.map +0 -1
  320. package/dist/schemas/nodes/index.js +0 -12
  321. package/dist/schemas/nodes/index.js.map +0 -1
  322. package/dist/schemas/nodes/inline/InlineSchema.d.ts +0 -48
  323. package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +0 -1
  324. package/dist/schemas/nodes/inline/InlineSchema.js +0 -436
  325. package/dist/schemas/nodes/inline/InlineSchema.js.map +0 -1
  326. package/dist/schemas/nodes/inline/index.d.ts +0 -7
  327. package/dist/schemas/nodes/inline/index.d.ts.map +0 -1
  328. package/dist/schemas/nodes/inline/index.js +0 -7
  329. package/dist/schemas/nodes/inline/index.js.map +0 -1
  330. package/dist/schemas/nodes/macro/MacroSchema.d.ts +0 -27
  331. package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +0 -1
  332. package/dist/schemas/nodes/macro/MacroSchema.js +0 -162
  333. package/dist/schemas/nodes/macro/MacroSchema.js.map +0 -1
  334. package/dist/schemas/nodes/macro/index.d.ts +0 -7
  335. package/dist/schemas/nodes/macro/index.d.ts.map +0 -1
  336. package/dist/schemas/nodes/macro/index.js +0 -7
  337. package/dist/schemas/nodes/macro/index.js.map +0 -1
  338. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +0 -53
  339. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +0 -1
  340. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +0 -349
  341. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +0 -1
  342. package/dist/schemas/preprocessing/index.d.ts +0 -8
  343. package/dist/schemas/preprocessing/index.d.ts.map +0 -1
  344. package/dist/schemas/preprocessing/index.js +0 -2
  345. package/dist/schemas/preprocessing/index.js.map +0 -1
  346. package/dist/serializers/ConfluenceSerializer.d.ts +0 -30
  347. package/dist/serializers/ConfluenceSerializer.d.ts.map +0 -1
  348. package/dist/serializers/ConfluenceSerializer.js +0 -551
  349. package/dist/serializers/ConfluenceSerializer.js.map +0 -1
  350. package/dist/serializers/MarkdownSerializer.d.ts +0 -34
  351. package/dist/serializers/MarkdownSerializer.d.ts.map +0 -1
  352. package/dist/serializers/MarkdownSerializer.js +0 -355
  353. package/dist/serializers/MarkdownSerializer.js.map +0 -1
  354. package/dist/serializers/index.d.ts +0 -8
  355. package/dist/serializers/index.d.ts.map +0 -1
  356. package/dist/serializers/index.js +0 -8
  357. package/dist/serializers/index.js.map +0 -1
package/src/bin.ts CHANGED
@@ -2,69 +2,43 @@
2
2
  /**
3
3
  * CLI entry point for confluence-to-markdown.
4
4
  */
5
- import { Command } from "@effect/cli"
5
+ import { NodeRuntime, NodeStdio, NodeTerminal } from "@effect/platform-node"
6
6
  import * as Effect from "effect/Effect"
7
- import * as Logger from "effect/Logger"
8
- import * as LogLevel from "effect/LogLevel"
7
+ import * as Stdio from "effect/Stdio"
8
+ import { Command } from "effect/unstable/cli"
9
9
  import pkg from "../package.json" with { type: "json" }
10
10
  import { handleError } from "./commands/errorHandler.js"
11
- import {
12
- authCommand,
13
- cloneCommand,
14
- commitCommand,
15
- deleteCommand,
16
- diffCommand,
17
- logCommand,
18
- newCommand,
19
- pullCommand,
20
- pushCommand,
21
- statusCommand
22
- } from "./commands/index.js"
23
- import { AppLayer, AuthOnlyLayer, CloneLayer, getLayerType, MinimalLayer } from "./commands/layers.js"
24
-
25
- // === Main command ===
26
- const confluence = Command.make("confluence").pipe(
27
- Command.withDescription("Sync Confluence pages to local markdown"),
28
- Command.withSubcommands([
29
- cloneCommand,
30
- authCommand,
31
- pullCommand,
32
- pushCommand,
33
- statusCommand,
34
- commitCommand,
35
- logCommand,
36
- diffCommand,
37
- newCommand,
38
- deleteCommand
39
- ])
40
- )
11
+ import { AppLayer, AuthOnlyLayer, CloneLayer, FetchLayer, getLayerType, MinimalLayer } from "./commands/layers.js"
12
+ import { confluenceCommand } from "./commands/root.js"
41
13
 
42
14
  // === Run CLI ===
43
- const cli = Command.run(confluence, {
44
- name: pkg.name,
15
+ const cli = Command.runWith(confluenceCommand, {
45
16
  version: pkg.version
46
17
  })
47
18
 
48
- const layerType = getLayerType()
49
- const layer = layerType === "full"
50
- ? AppLayer
51
- : layerType === "auth"
52
- ? AuthOnlyLayer
53
- : layerType === "clone"
54
- ? CloneLayer
55
- : MinimalLayer
19
+ const layerForArgv = (argv: ReadonlyArray<string>) => {
20
+ const layerType = getLayerType(argv)
21
+ return layerType === "full"
22
+ ? AppLayer
23
+ : layerType === "auth"
24
+ ? AuthOnlyLayer
25
+ : layerType === "clone"
26
+ ? CloneLayer
27
+ : layerType === "fetch"
28
+ ? FetchLayer
29
+ : MinimalLayer
30
+ }
56
31
 
57
32
  // Suppress verbose Effect logs (e.g. token refresh messages)
58
- const SilentLogger = Logger.replace(Logger.defaultLogger, Logger.none)
59
-
60
- Effect.suspend(() => cli(process.argv)).pipe(
61
- Effect.provide(layer),
62
- Effect.provide(SilentLogger),
63
- Logger.withMinimumLogLevel(LogLevel.None),
64
- Effect.runPromiseExit
65
- ).then((exit) => {
66
- if (exit._tag === "Failure") {
67
- handleError(exit.cause)
68
- process.exit(1)
69
- }
70
- })
33
+ Effect.gen(function*() {
34
+ const stdio = yield* Stdio.Stdio
35
+ const args = yield* stdio.args
36
+ return yield* cli(args).pipe(
37
+ Effect.provide(layerForArgv(args))
38
+ )
39
+ }).pipe(
40
+ Effect.provide(NodeTerminal.layer),
41
+ Effect.provide(NodeStdio.layer),
42
+ Effect.catchCause((cause) => handleError(cause)),
43
+ NodeRuntime.runMain({ disableErrorReporting: true })
44
+ )
@@ -1,12 +1,28 @@
1
1
  /**
2
2
  * Authentication commands for Confluence CLI.
3
3
  */
4
- import { Command, Options, Prompt } from "@effect/cli"
5
4
  import * as Console from "effect/Console"
6
5
  import * as Effect from "effect/Effect"
7
6
  import * as Option from "effect/Option"
7
+ import { Command, Flag as Options, Prompt } from "effect/unstable/cli"
8
+ import { ChildProcessSpawner } from "effect/unstable/process"
9
+ import * as ChildProcess from "effect/unstable/process/ChildProcess"
8
10
  import { ConfluenceAuth } from "../ConfluenceAuth.js"
9
11
 
12
+ const openBrowser = (url: string) => {
13
+ const run = (command: ChildProcess.Command) =>
14
+ Effect.flatMap(ChildProcessSpawner.ChildProcessSpawner, (spawner) =>
15
+ spawner.exitCode(command).pipe(
16
+ Effect.flatMap((code) => code === 0 ? Effect.void : Effect.fail(code))
17
+ ))
18
+
19
+ return run(ChildProcess.make("open", [url])).pipe(
20
+ Effect.catchIf(() => true, () => run(ChildProcess.make("xdg-open", [url]))),
21
+ Effect.catchIf(() => true, () => run(ChildProcess.make("rundll32.exe", ["url.dll,FileProtocolHandler", url]))),
22
+ Effect.asVoid
23
+ )
24
+ }
25
+
10
26
  // === Auth create command ===
11
27
  const createCommand = Command.make("create", {}, () =>
12
28
  Effect.gen(function*() {
@@ -24,28 +40,15 @@ Creating OAuth app in Atlassian Developer Console...
24
40
  6. Run: confluence auth configure --client-id <ID> --client-secret <SECRET>
25
41
  `)
26
42
  const url = "https://developer.atlassian.com/console/myapps/create-3lo-app/"
27
- yield* Effect.promise(() =>
28
- import("node:child_process").then((cp) =>
29
- new Promise<void>((resolve, reject) => {
30
- const platform = process.platform
31
- if (platform === "darwin") {
32
- cp.execFile("open", [url], (err) => err ? reject(err) : resolve())
33
- } else if (platform === "win32") {
34
- cp.execFile("cmd", ["/c", "start", "", url], (err) => err ? reject(err) : resolve())
35
- } else {
36
- cp.execFile("xdg-open", [url], (err) => err ? reject(err) : resolve())
37
- }
38
- })
39
- )
40
- )
43
+ yield* openBrowser(url)
41
44
  })).pipe(Command.withDescription("Create OAuth app in Atlassian Developer Console"))
42
45
 
43
46
  // === Auth configure command ===
44
- const clientIdOption = Options.text("client-id").pipe(
47
+ const clientIdOption = Options.string("client-id").pipe(
45
48
  Options.withDescription("OAuth client ID from Atlassian Developer Console"),
46
49
  Options.optional
47
50
  )
48
- const clientSecretOption = Options.text("client-secret").pipe(
51
+ const clientSecretOption = Options.string("client-secret").pipe(
49
52
  Options.withDescription("OAuth client secret"),
50
53
  Options.optional
51
54
  )
@@ -70,7 +73,7 @@ const configureCommand = Command.make(
70
73
  ).pipe(Command.withDescription("Configure OAuth client credentials"))
71
74
 
72
75
  // === Auth login command ===
73
- const siteOption = Options.text("site").pipe(
76
+ const siteOption = Options.string("site").pipe(
74
77
  Options.withDescription("Confluence site URL to use (for accounts with multiple sites)"),
75
78
  Options.optional
76
79
  )
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Clone command for Confluence CLI.
3
3
  */
4
- import { Command, Options, Prompt } from "@effect/cli"
5
- import * as NodeContext from "@effect/platform-node/NodeContext"
6
4
  import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient"
5
+ import * as NodeServices from "@effect/platform-node/NodeServices"
7
6
  import * as Console from "effect/Console"
8
7
  import * as Effect from "effect/Effect"
9
8
  import * as Layer from "effect/Layer"
10
9
  import * as Option from "effect/Option"
10
+ import { Command, Flag as Options, Prompt } from "effect/unstable/cli"
11
11
  import { layer as AdfSchemaValidatorLayer } from "../AdfSchemaValidator.js"
12
12
  import { layer as AtlaskitTransformersLayer } from "../AtlaskitTransformers.js"
13
13
  import type { PageId } from "../Brand.js"
@@ -15,10 +15,12 @@ import { type ConfluenceClientConfig, layer as ConfluenceClientLayer } from "../
15
15
  import { createConfigFile, layerFromValues as ConfluenceConfigLayerFromValues } from "../ConfluenceConfig.js"
16
16
  import { ConfigError } from "../ConfluenceError.js"
17
17
  import { GitService, layer as GitServiceLayer } from "../GitService.js"
18
+ import { writeStdout } from "../internal/stdio.js"
18
19
  import { UserCacheLayer } from "../internal/userCache.js"
19
20
  import { layer as LocalFileSystemLayer } from "../LocalFileSystem.js"
20
21
  import { layer as MarkdownConverterLayer } from "../MarkdownConverter.js"
21
22
  import { layer as SyncEngineLayer, SyncEngine } from "../SyncEngine.js"
23
+ import { resolvePageInput } from "./pageInput.js"
22
24
  import { getAuth } from "./shared.js"
23
25
 
24
26
  const ConverterPipeline = MarkdownConverterLayer.pipe(
@@ -26,39 +28,34 @@ const ConverterPipeline = MarkdownConverterLayer.pipe(
26
28
  Layer.provide(AdfSchemaValidatorLayer)
27
29
  )
28
30
 
29
- const rootPageIdOption = Options.text("root-page-id").pipe(
31
+ const rootPageIdOption = Options.string("root-page-id").pipe(
30
32
  Options.withDescription("Confluence root page ID to sync from"),
31
33
  Options.optional
32
34
  )
33
35
 
34
- const baseUrlOption = Options.text("base-url").pipe(
36
+ const baseUrlOption = Options.string("base-url").pipe(
35
37
  Options.withDescription("Confluence Cloud base URL (e.g., https://yoursite.atlassian.net)"),
36
38
  Options.optional
37
39
  )
38
40
 
39
- /** Validate page ID format */
40
- const validatePageId = (input: string): Effect.Effect<string, ConfigError> =>
41
- input.trim().length > 0
42
- ? Effect.succeed(input.trim())
43
- : Effect.fail(new ConfigError({ message: "Page ID cannot be empty" }))
44
-
45
- /** Validate base URL format */
46
- const validateBaseUrl = (input: string): Effect.Effect<string, ConfigError> => {
47
- const pattern = /^https:\/\/[a-z0-9-]+\.atlassian\.net$/
48
- return pattern.test(input)
49
- ? Effect.succeed(input)
50
- : Effect.fail(
51
- new ConfigError({
52
- message: `Invalid Confluence URL: ${input}. Expected format: https://yoursite.atlassian.net`
53
- })
54
- )
55
- }
41
+ const urlOption = Options.string("url").pipe(
42
+ Options.withDescription("Confluence root page URL"),
43
+ Options.optional
44
+ )
56
45
 
57
46
  export const cloneCommand = Command.make(
58
47
  "clone",
59
- { rootPageId: rootPageIdOption, baseUrl: baseUrlOption },
60
- ({ baseUrl, rootPageId }) =>
48
+ { rootPageId: rootPageIdOption, baseUrl: baseUrlOption, url: urlOption },
49
+ ({ baseUrl, rootPageId, url: pageUrl }) =>
61
50
  Effect.gen(function*() {
51
+ const urlInput = Option.isSome(pageUrl)
52
+ ? yield* resolvePageInput({
53
+ url: pageUrl.value,
54
+ pageId: Option.isSome(rootPageId) ? rootPageId.value : undefined,
55
+ baseUrl: Option.isSome(baseUrl) ? baseUrl.value : undefined
56
+ })
57
+ : undefined
58
+
62
59
  const git = yield* GitService
63
60
 
64
61
  // Fail if .confluence already exists
@@ -78,15 +75,20 @@ export const cloneCommand = Command.make(
78
75
  )
79
76
  yield* Console.log(`Found git ${gitVersion}`)
80
77
 
81
- const rawPageId = Option.isSome(rootPageId)
82
- ? rootPageId.value
83
- : yield* Prompt.text({ message: "Enter Confluence root page ID:" })
84
- const rawUrl = Option.isSome(baseUrl)
85
- ? baseUrl.value
86
- : yield* Prompt.text({ message: "Enter Confluence base URL (e.g., https://yoursite.atlassian.net):" })
78
+ const input = urlInput ??
79
+ (yield* Effect.gen(function*() {
80
+ const rawPageId = Option.isSome(rootPageId)
81
+ ? rootPageId.value
82
+ : yield* Prompt.text({ message: "Enter Confluence root page ID:" })
83
+ const rawUrl = Option.isSome(baseUrl)
84
+ ? baseUrl.value
85
+ : yield* Prompt.text({ message: "Enter Confluence base URL (e.g., https://yoursite.atlassian.net):" })
86
+
87
+ return yield* resolvePageInput({ pageId: rawPageId, baseUrl: rawUrl })
88
+ }))
87
89
 
88
- const pageId = yield* validatePageId(rawPageId)
89
- const url = yield* validateBaseUrl(rawUrl)
90
+ const pageId = input.pageId
91
+ const url = input.baseUrl
90
92
 
91
93
  const path = yield* createConfigFile(pageId, url)
92
94
  yield* Console.log(`Created configuration file: ${path}`)
@@ -115,7 +117,7 @@ export const cloneCommand = Command.make(
115
117
  })
116
118
 
117
119
  const clientLayer = ConfluenceClientLayer(clientConfig).pipe(
118
- Layer.provide(NodeHttpClient.layer)
120
+ Layer.provide(NodeHttpClient.layerFetch)
119
121
  )
120
122
 
121
123
  const cloneLayer = SyncEngineLayer.pipe(
@@ -125,7 +127,7 @@ export const cloneCommand = Command.make(
125
127
  Layer.provideMerge(ConverterPipeline),
126
128
  Layer.provideMerge(LocalFileSystemLayer),
127
129
  Layer.provideMerge(configLayer),
128
- Layer.provideMerge(NodeContext.layer)
130
+ Layer.provideMerge(NodeServices.layer)
129
131
  )
130
132
 
131
133
  const result = yield* Effect.gen(function*() {
@@ -134,9 +136,8 @@ export const cloneCommand = Command.make(
134
136
  const pullResult = yield* engine.pull({
135
137
  force: true,
136
138
  replayHistory: true,
137
- onProgress: (current, total, message) => {
138
- process.stdout.write(`\r Replaying history: ${current}/${total} - ${message}`)
139
- }
139
+ onProgress: (current, total, message) =>
140
+ writeStdout(`\r Replaying history: ${current}/${total} - ${message}`)
140
141
  })
141
142
 
142
143
  // Create origin/confluence branch at HEAD to track remote state
@@ -146,7 +147,7 @@ export const cloneCommand = Command.make(
146
147
  }).pipe(Effect.provide(cloneLayer))
147
148
 
148
149
  // Clear progress line and print final result
149
- process.stdout.write("\r" + " ".repeat(80) + "\r")
150
+ yield* writeStdout("\r" + " ".repeat(80) + "\r")
150
151
  yield* Console.log(`Cloned ${result.pulled} pages with ${result.commits} commits`)
151
152
  })
152
153
  ).pipe(Command.withDescription("Clone Confluence pages with full version history"))
@@ -4,11 +4,11 @@
4
4
  * Interactive mode: select page from tree, delete local file only.
5
5
  * Push to actually delete from Confluence.
6
6
  */
7
- import { Command, Prompt } from "@effect/cli"
8
- import * as FileSystem from "@effect/platform/FileSystem"
9
- import * as Path from "@effect/platform/Path"
10
7
  import * as Console from "effect/Console"
11
8
  import * as Effect from "effect/Effect"
9
+ import * as FileSystem from "effect/FileSystem"
10
+ import * as Path from "effect/Path"
11
+ import { Command, Prompt } from "effect/unstable/cli"
12
12
  import { ConfluenceConfig } from "../ConfluenceConfig.js"
13
13
  import { LocalFileSystem } from "../LocalFileSystem.js"
14
14
  import { flattenPageTree } from "./pageTree.js"
@@ -20,7 +20,8 @@ export const deleteCommand = Command.make("delete", {}, () =>
20
20
  const pathService = yield* Path.Path
21
21
  const fs = yield* FileSystem.FileSystem
22
22
 
23
- const docsPath = pathService.join(process.cwd(), config.docsPath)
23
+ const cwd = pathService.resolve(".")
24
+ const docsPath = pathService.join(cwd, config.docsPath)
24
25
 
25
26
  // Interactive mode - show page tree, delete local file
26
27
  yield* Console.log("Scanning page structure...")
@@ -7,26 +7,33 @@
7
7
  * - NodeRuntime.runMain error reporting shows full cause structure
8
8
  */
9
9
  import * as Cause from "effect/Cause"
10
+ import * as Effect from "effect/Effect"
11
+ import { writeStderr } from "../internal/stdio.js"
10
12
 
11
13
  /**
12
14
  * Print errors to stderr without stack traces.
13
15
  */
14
- export const handleError = <E>(cause: Cause.Cause<E>): void => {
15
- if (Cause.isEmpty(cause)) return
16
-
17
- for (const error of Cause.failures(cause)) {
18
- if (error && typeof error === "object" && "message" in error) {
19
- process.stderr.write(`${(error as { message: string }).message}\n`)
20
- } else {
21
- process.stderr.write(`${String(error)}\n`)
22
- }
23
- }
24
-
25
- for (const defect of Cause.defects(cause)) {
26
- if (defect instanceof Error) {
27
- process.stderr.write(`Error: ${defect.message}\n`)
28
- } else {
29
- process.stderr.write(`Error: ${String(defect)}\n`)
16
+ export const handleError = <E>(
17
+ cause: Cause.Cause<E>
18
+ ): Effect.Effect<void> =>
19
+ Effect.gen(function*() {
20
+ for (const reason of cause.reasons) {
21
+ if (Cause.isFailReason(reason)) {
22
+ const error = reason.error
23
+ if (error && typeof error === "object" && "message" in error) {
24
+ yield* writeStderr(`${(error as { message: string }).message}\n`)
25
+ } else {
26
+ yield* writeStderr(`${String(error)}\n`)
27
+ }
28
+ } else if (Cause.isDieReason(reason)) {
29
+ const defect = reason.defect
30
+ if (defect instanceof Error) {
31
+ yield* writeStderr(`Error: ${defect.message}\n`)
32
+ } else {
33
+ yield* writeStderr(`Error: ${String(defect)}\n`)
34
+ }
35
+ } else {
36
+ yield* writeStderr("Interrupted\n")
37
+ }
30
38
  }
31
- }
32
- }
39
+ })
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Fetch command for Confluence CLI.
3
+ */
4
+ import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient"
5
+ import * as Effect from "effect/Effect"
6
+ import * as Layer from "effect/Layer"
7
+ import * as Option from "effect/Option"
8
+ import { Command, Flag as Options } from "effect/unstable/cli"
9
+ import type { PageId } from "../Brand.js"
10
+ import { ConfluenceClient, type ConfluenceClientConfig, layer as ConfluenceClientLayer } from "../ConfluenceClient.js"
11
+ import { ConfigError } from "../ConfluenceError.js"
12
+ import { cleanMarkdown as removeRoundTripMetadata } from "../internal/cleanMarkdown.js"
13
+ import { writeStdout } from "../internal/stdio.js"
14
+ import { MarkdownConverter } from "../MarkdownConverter.js"
15
+ import { resolvePageInput } from "./pageInput.js"
16
+ import { getAuth } from "./shared.js"
17
+
18
+ const urlOption = Options.string("url").pipe(
19
+ Options.withDescription("Confluence page URL"),
20
+ Options.optional
21
+ )
22
+
23
+ const pageIdOption = Options.string("page-id").pipe(
24
+ Options.withDescription("Confluence page ID"),
25
+ Options.optional
26
+ )
27
+
28
+ const baseUrlOption = Options.string("base-url").pipe(
29
+ Options.withDescription("Confluence Cloud base URL (e.g., https://yoursite.atlassian.net)"),
30
+ Options.optional
31
+ )
32
+
33
+ const cleanMarkdownOption = Options.boolean("clean-markdown").pipe(
34
+ Options.withDescription("Print readable markdown without Confluence round-trip metadata")
35
+ )
36
+
37
+ export const fetchPageMarkdown = (
38
+ pageId: PageId,
39
+ options: { readonly cleanMarkdown: boolean }
40
+ ) =>
41
+ Effect.gen(function*() {
42
+ const client = yield* ConfluenceClient
43
+ const converter = yield* MarkdownConverter
44
+ const page = yield* client.getPage(pageId)
45
+ const adfJson = page.body?.atlas_doc_format?.value
46
+
47
+ if (!adfJson) {
48
+ return yield* Effect.fail(new ConfigError({ message: `Page ${pageId} did not include ADF content.` }))
49
+ }
50
+
51
+ const markdown = yield* converter.adfToMarkdown(adfJson)
52
+ return options.cleanMarkdown ? removeRoundTripMetadata(markdown) : markdown
53
+ })
54
+
55
+ const optionValue = (option: Option.Option<string>): string | undefined =>
56
+ Option.isSome(option) ? option.value : undefined
57
+
58
+ export interface FetchCommandOptions {
59
+ readonly makeClientLayer?: (config: ConfluenceClientConfig) => Layer.Layer<ConfluenceClient>
60
+ }
61
+
62
+ export const makeFetchCommand = (options: FetchCommandOptions = {}) => {
63
+ const makeClientLayer = options.makeClientLayer ??
64
+ ((clientConfig: ConfluenceClientConfig) =>
65
+ ConfluenceClientLayer(clientConfig).pipe(
66
+ Layer.provide(NodeHttpClient.layerFetch)
67
+ ))
68
+
69
+ return Command.make(
70
+ "fetch",
71
+ { url: urlOption, pageId: pageIdOption, baseUrl: baseUrlOption, cleanMarkdown: cleanMarkdownOption },
72
+ ({ baseUrl, cleanMarkdown, pageId, url }) =>
73
+ Effect.gen(function*() {
74
+ const input = yield* resolvePageInput({
75
+ url: optionValue(url),
76
+ pageId: optionValue(pageId),
77
+ baseUrl: optionValue(baseUrl)
78
+ })
79
+ const auth = yield* getAuth()
80
+ const clientConfig: ConfluenceClientConfig = { baseUrl: input.baseUrl, auth }
81
+ const markdown = yield* fetchPageMarkdown(input.pageId as PageId, { cleanMarkdown }).pipe(
82
+ Effect.provide(makeClientLayer(clientConfig))
83
+ )
84
+
85
+ yield* writeStdout(markdown.endsWith("\n") ? markdown : `${markdown}\n`)
86
+ })
87
+ ).pipe(Command.withDescription("Fetch the latest Confluence page markdown without creating a git workspace"))
88
+ }
89
+
90
+ export const fetchCommand = makeFetchCommand()
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Git commands (commit, log, diff) for Confluence CLI.
3
3
  */
4
- import { Args, Command, Options } from "@effect/cli"
5
4
  import * as Console from "effect/Console"
6
5
  import * as Effect from "effect/Effect"
7
6
  import * as Option from "effect/Option"
7
+ import { Argument as Args, Command, Flag as Options } from "effect/unstable/cli"
8
8
  import { ConfluenceConfig } from "../ConfluenceConfig.js"
9
9
  import { GitService } from "../GitService.js"
10
10
 
11
11
  // === Commit command ===
12
- const commitMessageOption = Options.text("message").pipe(
12
+ const commitMessageOption = Options.string("message").pipe(
13
13
  Options.withAlias("m"),
14
14
  Options.withDescription("Commit message"),
15
15
  Options.optional
@@ -44,12 +44,12 @@ const logOnelineOption = Options.boolean("oneline").pipe(
44
44
  Options.withDescription("Show compact one-line format")
45
45
  )
46
46
 
47
- const logSinceOption = Options.text("since").pipe(
47
+ const logSinceOption = Options.string("since").pipe(
48
48
  Options.withDescription("Show commits since date (e.g., '2024-01-01')"),
49
49
  Options.optional
50
50
  )
51
51
 
52
- const logFileArg = Args.text({ name: "file" }).pipe(Args.optional) as Args.Args<Option.Option<string>>
52
+ const logFileArg = Args.string("file").pipe(Args.optional)
53
53
 
54
54
  export const logCommand = Command.make(
55
55
  "log",
@@ -86,12 +86,12 @@ const diffStagedOption = Options.boolean("staged").pipe(
86
86
  Options.withDescription("Show staged changes")
87
87
  )
88
88
 
89
- const diffCommitOption = Options.text("commit").pipe(
89
+ const diffCommitOption = Options.string("commit").pipe(
90
90
  Options.withDescription("Compare with specific commit"),
91
91
  Options.optional
92
92
  )
93
93
 
94
- const diffFileArg = Args.text({ name: "file" }).pipe(Args.optional) as Args.Args<Option.Option<string>>
94
+ const diffFileArg = Args.string("file").pipe(Args.optional)
95
95
 
96
96
  export const diffCommand = Command.make(
97
97
  "diff",
@@ -4,6 +4,7 @@
4
4
  export { authCommand } from "./auth.js"
5
5
  export { cloneCommand } from "./clone.js"
6
6
  export { deleteCommand } from "./delete.js"
7
+ export { fetchCommand } from "./fetch.js"
7
8
  export { commitCommand, diffCommand, logCommand } from "./git.js"
8
9
  export { newCommand } from "./new.js"
9
10
  export { getAuth } from "./shared.js"