@specforge/cli 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 (380) hide show
  1. package/README.md +781 -0
  2. package/bin/specforge +21 -0
  3. package/dist/channel.d.ts +27 -0
  4. package/dist/channel.d.ts.map +1 -0
  5. package/dist/channel.js +22 -0
  6. package/dist/channel.js.map +1 -0
  7. package/dist/cli/commands/configure.d.ts +16 -0
  8. package/dist/cli/commands/configure.d.ts.map +1 -0
  9. package/dist/cli/commands/configure.js +186 -0
  10. package/dist/cli/commands/configure.js.map +1 -0
  11. package/dist/cli/commands/configure.types.d.ts +110 -0
  12. package/dist/cli/commands/configure.types.d.ts.map +1 -0
  13. package/dist/cli/commands/configure.types.js +82 -0
  14. package/dist/cli/commands/configure.types.js.map +1 -0
  15. package/dist/cli/commands/debug/call.d.ts +14 -0
  16. package/dist/cli/commands/debug/call.d.ts.map +1 -0
  17. package/dist/cli/commands/debug/call.js +96 -0
  18. package/dist/cli/commands/debug/call.js.map +1 -0
  19. package/dist/cli/commands/debug/index.d.ts +12 -0
  20. package/dist/cli/commands/debug/index.d.ts.map +1 -0
  21. package/dist/cli/commands/debug/index.js +14 -0
  22. package/dist/cli/commands/debug/index.js.map +1 -0
  23. package/dist/cli/commands/debug/register.d.ts +11 -0
  24. package/dist/cli/commands/debug/register.d.ts.map +1 -0
  25. package/dist/cli/commands/debug/register.js +78 -0
  26. package/dist/cli/commands/debug/register.js.map +1 -0
  27. package/dist/cli/commands/debug/test.d.ts +13 -0
  28. package/dist/cli/commands/debug/test.d.ts.map +1 -0
  29. package/dist/cli/commands/debug/test.js +91 -0
  30. package/dist/cli/commands/debug/test.js.map +1 -0
  31. package/dist/cli/commands/debug/tools.d.ts +13 -0
  32. package/dist/cli/commands/debug/tools.d.ts.map +1 -0
  33. package/dist/cli/commands/debug/tools.js +86 -0
  34. package/dist/cli/commands/debug/tools.js.map +1 -0
  35. package/dist/cli/commands/debug/types.d.ts +119 -0
  36. package/dist/cli/commands/debug/types.d.ts.map +1 -0
  37. package/dist/cli/commands/debug/types.js +36 -0
  38. package/dist/cli/commands/debug/types.js.map +1 -0
  39. package/dist/cli/commands/debug/whoami.d.ts +13 -0
  40. package/dist/cli/commands/debug/whoami.d.ts.map +1 -0
  41. package/dist/cli/commands/debug/whoami.js +131 -0
  42. package/dist/cli/commands/debug/whoami.js.map +1 -0
  43. package/dist/cli/commands/doctor.d.ts +16 -0
  44. package/dist/cli/commands/doctor.d.ts.map +1 -0
  45. package/dist/cli/commands/doctor.js +413 -0
  46. package/dist/cli/commands/doctor.js.map +1 -0
  47. package/dist/cli/commands/doctor.types.d.ts +180 -0
  48. package/dist/cli/commands/doctor.types.d.ts.map +1 -0
  49. package/dist/cli/commands/doctor.types.js +140 -0
  50. package/dist/cli/commands/doctor.types.js.map +1 -0
  51. package/dist/cli/commands/feedback.d.ts +27 -0
  52. package/dist/cli/commands/feedback.d.ts.map +1 -0
  53. package/dist/cli/commands/feedback.js +262 -0
  54. package/dist/cli/commands/feedback.js.map +1 -0
  55. package/dist/cli/commands/feedback.types.d.ts +116 -0
  56. package/dist/cli/commands/feedback.types.d.ts.map +1 -0
  57. package/dist/cli/commands/feedback.types.js +65 -0
  58. package/dist/cli/commands/feedback.types.js.map +1 -0
  59. package/dist/cli/commands/index.d.ts +15 -0
  60. package/dist/cli/commands/index.d.ts.map +1 -0
  61. package/dist/cli/commands/index.js +21 -0
  62. package/dist/cli/commands/index.js.map +1 -0
  63. package/dist/cli/commands/init.d.ts +17 -0
  64. package/dist/cli/commands/init.d.ts.map +1 -0
  65. package/dist/cli/commands/init.js +773 -0
  66. package/dist/cli/commands/init.js.map +1 -0
  67. package/dist/cli/commands/init.types.d.ts +193 -0
  68. package/dist/cli/commands/init.types.d.ts.map +1 -0
  69. package/dist/cli/commands/init.types.js +29 -0
  70. package/dist/cli/commands/init.types.js.map +1 -0
  71. package/dist/cli/commands/invitations.d.ts +31 -0
  72. package/dist/cli/commands/invitations.d.ts.map +1 -0
  73. package/dist/cli/commands/invitations.js +250 -0
  74. package/dist/cli/commands/invitations.js.map +1 -0
  75. package/dist/cli/commands/login.d.ts +40 -0
  76. package/dist/cli/commands/login.d.ts.map +1 -0
  77. package/dist/cli/commands/login.js +139 -0
  78. package/dist/cli/commands/login.js.map +1 -0
  79. package/dist/cli/commands/members.d.ts +55 -0
  80. package/dist/cli/commands/members.d.ts.map +1 -0
  81. package/dist/cli/commands/members.js +296 -0
  82. package/dist/cli/commands/members.js.map +1 -0
  83. package/dist/cli/commands/scaffold/agent-types.d.ts +54 -0
  84. package/dist/cli/commands/scaffold/agent-types.d.ts.map +1 -0
  85. package/dist/cli/commands/scaffold/agent-types.js +22 -0
  86. package/dist/cli/commands/scaffold/agent-types.js.map +1 -0
  87. package/dist/cli/commands/scaffold/display.d.ts +56 -0
  88. package/dist/cli/commands/scaffold/display.d.ts.map +1 -0
  89. package/dist/cli/commands/scaffold/display.js +217 -0
  90. package/dist/cli/commands/scaffold/display.js.map +1 -0
  91. package/dist/cli/commands/scaffold/files.d.ts +99 -0
  92. package/dist/cli/commands/scaffold/files.d.ts.map +1 -0
  93. package/dist/cli/commands/scaffold/files.js +122 -0
  94. package/dist/cli/commands/scaffold/files.js.map +1 -0
  95. package/dist/cli/commands/scaffold/index.d.ts +13 -0
  96. package/dist/cli/commands/scaffold/index.d.ts.map +1 -0
  97. package/dist/cli/commands/scaffold/index.js +12 -0
  98. package/dist/cli/commands/scaffold/index.js.map +1 -0
  99. package/dist/cli/commands/scaffold/register.d.ts +11 -0
  100. package/dist/cli/commands/scaffold/register.d.ts.map +1 -0
  101. package/dist/cli/commands/scaffold/register.js +11 -0
  102. package/dist/cli/commands/scaffold/register.js.map +1 -0
  103. package/dist/cli/commands/scaffold/scaffold.d.ts +11 -0
  104. package/dist/cli/commands/scaffold/scaffold.d.ts.map +1 -0
  105. package/dist/cli/commands/scaffold/scaffold.js +728 -0
  106. package/dist/cli/commands/scaffold/scaffold.js.map +1 -0
  107. package/dist/cli/commands/scaffold/targets.d.ts +60 -0
  108. package/dist/cli/commands/scaffold/targets.d.ts.map +1 -0
  109. package/dist/cli/commands/scaffold/targets.js +251 -0
  110. package/dist/cli/commands/scaffold/targets.js.map +1 -0
  111. package/dist/cli/commands/scaffold/types.d.ts +170 -0
  112. package/dist/cli/commands/scaffold/types.d.ts.map +1 -0
  113. package/dist/cli/commands/scaffold/types.js +12 -0
  114. package/dist/cli/commands/scaffold/types.js.map +1 -0
  115. package/dist/cli/commands/scaffold/utils.d.ts +89 -0
  116. package/dist/cli/commands/scaffold/utils.d.ts.map +1 -0
  117. package/dist/cli/commands/scaffold/utils.js +401 -0
  118. package/dist/cli/commands/scaffold/utils.js.map +1 -0
  119. package/dist/cli/commands/status.d.ts +19 -0
  120. package/dist/cli/commands/status.d.ts.map +1 -0
  121. package/dist/cli/commands/status.js +124 -0
  122. package/dist/cli/commands/status.js.map +1 -0
  123. package/dist/cli/commands/status.types.d.ts +37 -0
  124. package/dist/cli/commands/status.types.d.ts.map +1 -0
  125. package/dist/cli/commands/status.types.js +7 -0
  126. package/dist/cli/commands/status.types.js.map +1 -0
  127. package/dist/cli/commands/switch.d.ts +18 -0
  128. package/dist/cli/commands/switch.d.ts.map +1 -0
  129. package/dist/cli/commands/switch.js +134 -0
  130. package/dist/cli/commands/switch.js.map +1 -0
  131. package/dist/cli/commands/switch.types.d.ts +123 -0
  132. package/dist/cli/commands/switch.types.d.ts.map +1 -0
  133. package/dist/cli/commands/switch.types.js +46 -0
  134. package/dist/cli/commands/switch.types.js.map +1 -0
  135. package/dist/cli/config/agent-teams.types.d.ts +120 -0
  136. package/dist/cli/config/agent-teams.types.d.ts.map +1 -0
  137. package/dist/cli/config/agent-teams.types.js +23 -0
  138. package/dist/cli/config/agent-teams.types.js.map +1 -0
  139. package/dist/cli/config/index.d.ts +13 -0
  140. package/dist/cli/config/index.d.ts.map +1 -0
  141. package/dist/cli/config/index.js +7 -0
  142. package/dist/cli/config/index.js.map +1 -0
  143. package/dist/cli/config/loader.d.ts +114 -0
  144. package/dist/cli/config/loader.d.ts.map +1 -0
  145. package/dist/cli/config/loader.js +198 -0
  146. package/dist/cli/config/loader.js.map +1 -0
  147. package/dist/cli/config/paths.d.ts +107 -0
  148. package/dist/cli/config/paths.d.ts.map +1 -0
  149. package/dist/cli/config/paths.js +79 -0
  150. package/dist/cli/config/paths.js.map +1 -0
  151. package/dist/cli/config/planning-sessions-registry.d.ts +52 -0
  152. package/dist/cli/config/planning-sessions-registry.d.ts.map +1 -0
  153. package/dist/cli/config/planning-sessions-registry.js +46 -0
  154. package/dist/cli/config/planning-sessions-registry.js.map +1 -0
  155. package/dist/cli/config/types.d.ts +186 -0
  156. package/dist/cli/config/types.d.ts.map +1 -0
  157. package/dist/cli/config/types.js +27 -0
  158. package/dist/cli/config/types.js.map +1 -0
  159. package/dist/cli/config/validation.d.ts +62 -0
  160. package/dist/cli/config/validation.d.ts.map +1 -0
  161. package/dist/cli/config/validation.js +133 -0
  162. package/dist/cli/config/validation.js.map +1 -0
  163. package/dist/cli/config/writer.d.ts +153 -0
  164. package/dist/cli/config/writer.d.ts.map +1 -0
  165. package/dist/cli/config/writer.js +168 -0
  166. package/dist/cli/config/writer.js.map +1 -0
  167. package/dist/cli/index.d.ts +30 -0
  168. package/dist/cli/index.d.ts.map +1 -0
  169. package/dist/cli/index.js +142 -0
  170. package/dist/cli/index.js.map +1 -0
  171. package/dist/cli/middleware/auth-guard.d.ts +107 -0
  172. package/dist/cli/middleware/auth-guard.d.ts.map +1 -0
  173. package/dist/cli/middleware/auth-guard.js +97 -0
  174. package/dist/cli/middleware/auth-guard.js.map +1 -0
  175. package/dist/cli/middleware/error-handler.d.ts +133 -0
  176. package/dist/cli/middleware/error-handler.d.ts.map +1 -0
  177. package/dist/cli/middleware/error-handler.js +139 -0
  178. package/dist/cli/middleware/error-handler.js.map +1 -0
  179. package/dist/cli/middleware/index.d.ts +6 -0
  180. package/dist/cli/middleware/index.d.ts.map +1 -0
  181. package/dist/cli/middleware/index.js +3 -0
  182. package/dist/cli/middleware/index.js.map +1 -0
  183. package/dist/cli/templates/agents/content/core/sfag-orchestrator.d.ts +9 -0
  184. package/dist/cli/templates/agents/content/core/sfag-orchestrator.d.ts.map +1 -0
  185. package/dist/cli/templates/agents/content/core/sfag-orchestrator.js +160 -0
  186. package/dist/cli/templates/agents/content/core/sfag-orchestrator.js.map +1 -0
  187. package/dist/cli/templates/agents/content/core/sfag-spec-creator.d.ts +9 -0
  188. package/dist/cli/templates/agents/content/core/sfag-spec-creator.d.ts.map +1 -0
  189. package/dist/cli/templates/agents/content/core/sfag-spec-creator.js +343 -0
  190. package/dist/cli/templates/agents/content/core/sfag-spec-creator.js.map +1 -0
  191. package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.d.ts +9 -0
  192. package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.d.ts.map +1 -0
  193. package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.js +254 -0
  194. package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.js.map +1 -0
  195. package/dist/cli/templates/agents/content/research/sfag-package-researcher.d.ts +8 -0
  196. package/dist/cli/templates/agents/content/research/sfag-package-researcher.d.ts.map +1 -0
  197. package/dist/cli/templates/agents/content/research/sfag-package-researcher.js +128 -0
  198. package/dist/cli/templates/agents/content/research/sfag-package-researcher.js.map +1 -0
  199. package/dist/cli/templates/agents/index.d.ts +23 -0
  200. package/dist/cli/templates/agents/index.d.ts.map +1 -0
  201. package/dist/cli/templates/agents/index.js +29 -0
  202. package/dist/cli/templates/agents/index.js.map +1 -0
  203. package/dist/cli/templates/commands.d.ts +17 -0
  204. package/dist/cli/templates/commands.d.ts.map +1 -0
  205. package/dist/cli/templates/commands.js +78 -0
  206. package/dist/cli/templates/commands.js.map +1 -0
  207. package/dist/cli/templates/content/sf-blockers.d.ts +7 -0
  208. package/dist/cli/templates/content/sf-blockers.d.ts.map +1 -0
  209. package/dist/cli/templates/content/sf-blockers.js +69 -0
  210. package/dist/cli/templates/content/sf-blockers.js.map +1 -0
  211. package/dist/cli/templates/content/sf-commit.d.ts +7 -0
  212. package/dist/cli/templates/content/sf-commit.d.ts.map +1 -0
  213. package/dist/cli/templates/content/sf-commit.js +79 -0
  214. package/dist/cli/templates/content/sf-commit.js.map +1 -0
  215. package/dist/cli/templates/content/sf-context.d.ts +7 -0
  216. package/dist/cli/templates/content/sf-context.d.ts.map +1 -0
  217. package/dist/cli/templates/content/sf-context.js +63 -0
  218. package/dist/cli/templates/content/sf-context.js.map +1 -0
  219. package/dist/cli/templates/content/sf-help.d.ts +7 -0
  220. package/dist/cli/templates/content/sf-help.d.ts.map +1 -0
  221. package/dist/cli/templates/content/sf-help.js +42 -0
  222. package/dist/cli/templates/content/sf-help.js.map +1 -0
  223. package/dist/cli/templates/content/sf-init.d.ts +7 -0
  224. package/dist/cli/templates/content/sf-init.d.ts.map +1 -0
  225. package/dist/cli/templates/content/sf-init.js +62 -0
  226. package/dist/cli/templates/content/sf-init.js.map +1 -0
  227. package/dist/cli/templates/content/sf-reset.d.ts +7 -0
  228. package/dist/cli/templates/content/sf-reset.d.ts.map +1 -0
  229. package/dist/cli/templates/content/sf-reset.js +67 -0
  230. package/dist/cli/templates/content/sf-reset.js.map +1 -0
  231. package/dist/cli/templates/content/sf-search.d.ts +7 -0
  232. package/dist/cli/templates/content/sf-search.d.ts.map +1 -0
  233. package/dist/cli/templates/content/sf-search.js +63 -0
  234. package/dist/cli/templates/content/sf-search.js.map +1 -0
  235. package/dist/cli/templates/content/sf-status.d.ts +7 -0
  236. package/dist/cli/templates/content/sf-status.d.ts.map +1 -0
  237. package/dist/cli/templates/content/sf-status.js +62 -0
  238. package/dist/cli/templates/content/sf-status.js.map +1 -0
  239. package/dist/cli/templates/index.d.ts +8 -0
  240. package/dist/cli/templates/index.d.ts.map +1 -0
  241. package/dist/cli/templates/index.js +17 -0
  242. package/dist/cli/templates/index.js.map +1 -0
  243. package/dist/cli/ui/banner.d.ts +38 -0
  244. package/dist/cli/ui/banner.d.ts.map +1 -0
  245. package/dist/cli/ui/banner.js +89 -0
  246. package/dist/cli/ui/banner.js.map +1 -0
  247. package/dist/cli/ui/colors.d.ts +76 -0
  248. package/dist/cli/ui/colors.d.ts.map +1 -0
  249. package/dist/cli/ui/colors.js +67 -0
  250. package/dist/cli/ui/colors.js.map +1 -0
  251. package/dist/cli/ui/index.d.ts +7 -0
  252. package/dist/cli/ui/index.d.ts.map +1 -0
  253. package/dist/cli/ui/index.js +4 -0
  254. package/dist/cli/ui/index.js.map +1 -0
  255. package/dist/cli/ui/output.d.ts +117 -0
  256. package/dist/cli/ui/output.d.ts.map +1 -0
  257. package/dist/cli/ui/output.js +83 -0
  258. package/dist/cli/ui/output.js.map +1 -0
  259. package/dist/cli/ui/spinner.d.ts +120 -0
  260. package/dist/cli/ui/spinner.d.ts.map +1 -0
  261. package/dist/cli/ui/spinner.js +158 -0
  262. package/dist/cli/ui/spinner.js.map +1 -0
  263. package/dist/cli/ui/time.d.ts +54 -0
  264. package/dist/cli/ui/time.d.ts.map +1 -0
  265. package/dist/cli/ui/time.js +119 -0
  266. package/dist/cli/ui/time.js.map +1 -0
  267. package/dist/client/api-client.d.ts +123 -0
  268. package/dist/client/api-client.d.ts.map +1 -0
  269. package/dist/client/api-client.js +105 -0
  270. package/dist/client/api-client.js.map +1 -0
  271. package/dist/client/cli-client.d.ts +26 -0
  272. package/dist/client/cli-client.d.ts.map +1 -0
  273. package/dist/client/cli-client.js +20 -0
  274. package/dist/client/cli-client.js.map +1 -0
  275. package/dist/config/index.d.ts +78 -0
  276. package/dist/config/index.d.ts.map +1 -0
  277. package/dist/config/index.js +79 -0
  278. package/dist/config/index.js.map +1 -0
  279. package/dist/index.d.ts +16 -0
  280. package/dist/index.d.ts.map +1 -0
  281. package/dist/index.js +73 -0
  282. package/dist/index.js.map +1 -0
  283. package/dist/lib/format.d.ts +126 -0
  284. package/dist/lib/format.d.ts.map +1 -0
  285. package/dist/lib/format.js +71 -0
  286. package/dist/lib/format.js.map +1 -0
  287. package/dist/lib/index.d.ts +11 -0
  288. package/dist/lib/index.d.ts.map +1 -0
  289. package/dist/lib/index.js +45 -0
  290. package/dist/lib/index.js.map +1 -0
  291. package/dist/lib/monorepo-detector.d.ts +31 -0
  292. package/dist/lib/monorepo-detector.d.ts.map +1 -0
  293. package/dist/lib/monorepo-detector.js +188 -0
  294. package/dist/lib/monorepo-detector.js.map +1 -0
  295. package/dist/lib/prompt-generator.d.ts +65 -0
  296. package/dist/lib/prompt-generator.d.ts.map +1 -0
  297. package/dist/lib/prompt-generator.js +162 -0
  298. package/dist/lib/prompt-generator.js.map +1 -0
  299. package/dist/lib/strategy-analyzer.d.ts +59 -0
  300. package/dist/lib/strategy-analyzer.d.ts.map +1 -0
  301. package/dist/lib/strategy-analyzer.js +91 -0
  302. package/dist/lib/strategy-analyzer.js.map +1 -0
  303. package/dist/lib/workflow-definitions.d.ts +41 -0
  304. package/dist/lib/workflow-definitions.d.ts.map +1 -0
  305. package/dist/lib/workflow-definitions.js +497 -0
  306. package/dist/lib/workflow-definitions.js.map +1 -0
  307. package/dist/patterns/index.d.ts +9 -0
  308. package/dist/patterns/index.d.ts.map +1 -0
  309. package/dist/patterns/index.js +21 -0
  310. package/dist/patterns/index.js.map +1 -0
  311. package/dist/patterns/inheritance.d.ts +193 -0
  312. package/dist/patterns/inheritance.d.ts.map +1 -0
  313. package/dist/patterns/inheritance.js +125 -0
  314. package/dist/patterns/inheritance.js.map +1 -0
  315. package/dist/server.d.ts +44 -0
  316. package/dist/server.d.ts.map +1 -0
  317. package/dist/server.js +91 -0
  318. package/dist/server.js.map +1 -0
  319. package/dist/tools/core/context-helper.d.ts +50 -0
  320. package/dist/tools/core/context-helper.d.ts.map +1 -0
  321. package/dist/tools/core/context-helper.js +49 -0
  322. package/dist/tools/core/context-helper.js.map +1 -0
  323. package/dist/tools/core/index.d.ts +2 -0
  324. package/dist/tools/core/index.d.ts.map +1 -0
  325. package/dist/tools/core/index.js +2 -0
  326. package/dist/tools/core/index.js.map +1 -0
  327. package/dist/tools/index.d.ts +58 -0
  328. package/dist/tools/index.d.ts.map +1 -0
  329. package/dist/tools/index.js +1499 -0
  330. package/dist/tools/index.js.map +1 -0
  331. package/dist/transport/__tests__/resolve-endpoint.test.d.ts +6 -0
  332. package/dist/transport/__tests__/resolve-endpoint.test.d.ts.map +1 -0
  333. package/dist/transport/index.d.ts +6 -0
  334. package/dist/transport/index.d.ts.map +1 -0
  335. package/dist/transport/index.js +5 -0
  336. package/dist/transport/index.js.map +1 -0
  337. package/dist/transport/interface.d.ts +25 -0
  338. package/dist/transport/interface.d.ts.map +1 -0
  339. package/dist/transport/interface.js +1 -0
  340. package/dist/transport/interface.js.map +1 -0
  341. package/dist/transport/remote.d.ts +38 -0
  342. package/dist/transport/remote.d.ts.map +1 -0
  343. package/dist/transport/remote.js +107 -0
  344. package/dist/transport/remote.js.map +1 -0
  345. package/dist/transport/resolve-endpoint.d.ts +58 -0
  346. package/dist/transport/resolve-endpoint.d.ts.map +1 -0
  347. package/dist/transport/resolve-endpoint.js +146 -0
  348. package/dist/transport/resolve-endpoint.js.map +1 -0
  349. package/dist/types/index.d.ts +212 -0
  350. package/dist/types/index.d.ts.map +1 -0
  351. package/dist/types/index.js +16 -0
  352. package/dist/types/index.js.map +1 -0
  353. package/dist/validation/index.d.ts +86 -0
  354. package/dist/validation/index.d.ts.map +1 -0
  355. package/dist/validation/index.js +392 -0
  356. package/dist/validation/index.js.map +1 -0
  357. package/dist/validation/ticket-validation.d.ts +162 -0
  358. package/dist/validation/ticket-validation.d.ts.map +1 -0
  359. package/dist/validation/ticket-validation.js +188 -0
  360. package/dist/validation/ticket-validation.js.map +1 -0
  361. package/package.json +69 -0
  362. package/src/cli/templates/agents/content/core/sfag-orchestrator.ts +165 -0
  363. package/src/cli/templates/agents/content/core/sfag-spec-creator.ts +348 -0
  364. package/src/cli/templates/agents/content/core/sfag-ticket-implementer.ts +259 -0
  365. package/src/cli/templates/agents/content/research/sfag-package-researcher.ts +132 -0
  366. package/src/cli/templates/agents/index.ts +47 -0
  367. package/src/cli/templates/commands.ts +90 -0
  368. package/src/cli/templates/content/sf-blockers.ts +71 -0
  369. package/src/cli/templates/content/sf-commit.ts +81 -0
  370. package/src/cli/templates/content/sf-context.ts +65 -0
  371. package/src/cli/templates/content/sf-help.ts +44 -0
  372. package/src/cli/templates/content/sf-init.ts +64 -0
  373. package/src/cli/templates/content/sf-reset.ts +69 -0
  374. package/src/cli/templates/content/sf-search.ts +65 -0
  375. package/src/cli/templates/content/sf-status.ts +64 -0
  376. package/src/cli/templates/index.ts +16 -0
  377. package/src/cli/templates/skills/specforge-conventions.md +114 -0
  378. package/src/cli/templates/skills/specforge-orchestrator.md +409 -0
  379. package/src/cli/templates/skills/specforge-validator.md +128 -0
  380. package/src/cli/templates/skills/specforge-worker.md +412 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/ui/output.ts"],"sourcesContent":["/**\n * Output Formatting Utilities\n *\n * Provides consistent output formatting for tables, lists, and structured data.\n */\n\nimport { colors, style, icons } from './colors.js';\n\n/**\n * Print a labeled value\n *\n * @example\n * printField('Project', 'my-project');\n * // Output: Project: my-project\n */\nexport function printField(label: string, value: string | null | undefined): void {\n const displayValue = value ?? colors.muted('(not set)');\n console.log(`${style.label(label)}: ${displayValue}`);\n}\n\n/**\n * Print a section header\n *\n * @example\n * printHeader('Configuration');\n * // Output:\n * // Configuration\n * // ─────────────\n */\nexport function printHeader(title: string): void {\n console.log();\n console.log(style.label(title));\n console.log(colors.muted('─'.repeat(title.length)));\n}\n\n/**\n * Print a blank line\n */\nexport function printBlank(): void {\n console.log();\n}\n\n/**\n * Print a list of items\n *\n * @example\n * printList(['Item 1', 'Item 2', 'Item 3']);\n * // Output:\n * // • Item 1\n * // • Item 2\n * // • Item 3\n */\nexport function printList(items: string[], bullet: string = icons.bullet): void {\n for (const item of items) {\n console.log(` ${bullet} ${item}`);\n }\n}\n\n/**\n * Print a success message\n *\n * @example\n * printSuccess('Configuration saved');\n * // Output: ✓ Configuration saved\n */\nexport function printSuccess(message: string): void {\n console.log(`${icons.success} ${message}`);\n}\n\n/**\n * Print an error message\n *\n * @example\n * printError('Failed to connect');\n * // Output: ✗ Failed to connect\n */\nexport function printError(message: string): void {\n console.error(`${icons.error} ${style.err(message)}`);\n}\n\n/**\n * Print a warning message\n *\n * @example\n * printWarning('API key will expire soon');\n * // Output: ⚠ API key will expire soon\n */\nexport function printWarning(message: string): void {\n console.error(`${icons.warning} ${style.warn(message)}`);\n}\n\n/**\n * Print an info message\n *\n * @example\n * printInfo('Fetching data...');\n * // Output: ℹ Fetching data...\n */\nexport function printInfo(message: string): void {\n console.log(`${icons.info} ${message}`);\n}\n\n/**\n * Print a key-value table\n *\n * @example\n * printTable({\n * 'Project': 'my-project',\n * 'Specification': 'v2.0',\n * 'Status': 'active',\n * });\n */\nexport function printTable(data: Record<string, string | null | undefined>): void {\n const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));\n\n for (const [key, value] of Object.entries(data)) {\n const paddedKey = key.padEnd(maxKeyLength);\n const displayValue = value ?? colors.muted('(not set)');\n console.log(` ${style.label(paddedKey)} ${displayValue}`);\n }\n}\n\n/**\n * Print JSON output (for --format json)\n *\n * @example\n * printJson({ project: 'my-project', status: 'active' });\n */\nexport function printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * Print a command hint\n *\n * @example\n * printHint('specforge init', 'Initialize project');\n * // Output: → Run `specforge init` to initialize project\n */\nexport function printHint(command: string, description: string): void {\n console.log(`${icons.arrow} Run ${style.command(command)} to ${description}`);\n}\n\n/**\n * Print a URL with description\n *\n * @example\n * printLink('https://specforge.com', 'Visit documentation');\n * // Output: → Visit documentation: https://specforge.com\n */\nexport function printLink(url: string, description: string): void {\n console.log(`${icons.arrow} ${description}: ${style.url(url)}`);\n}\n\n/**\n * Format bytes to human readable size\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n\n return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;\n}\n\n/**\n * Format duration in milliseconds to human readable\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;\n\n const hours = Math.floor(ms / 3600000);\n const minutes = Math.floor((ms % 3600000) / 60000);\n return `${hours}h ${minutes}m`;\n}\n\n/**\n * Truncate string with ellipsis\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength - 1) + '…';\n}\n"],"mappings":"AAMA,SAAS,QAAQ,OAAO,aAAa;AAS9B,SAAS,WAAW,OAAe,OAAwC;AAChF,QAAM,eAAe,SAAS,OAAO,MAAM,WAAW;AACtD,UAAQ,IAAI,GAAG,MAAM,MAAM,KAAK,CAAC,KAAK,YAAY,EAAE;AACtD;AAWO,SAAS,YAAY,OAAqB;AAC/C,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,MAAM,KAAK,CAAC;AAC9B,UAAQ,IAAI,OAAO,MAAM,SAAI,OAAO,MAAM,MAAM,CAAC,CAAC;AACpD;AAKO,SAAS,aAAmB;AACjC,UAAQ,IAAI;AACd;AAYO,SAAS,UAAU,OAAiB,SAAiB,MAAM,QAAc;AAC9E,aAAW,QAAQ,OAAO;AACxB,YAAQ,IAAI,KAAK,MAAM,IAAI,IAAI,EAAE;AAAA,EACnC;AACF;AASO,SAAS,aAAa,SAAuB;AAClD,UAAQ,IAAI,GAAG,MAAM,OAAO,IAAI,OAAO,EAAE;AAC3C;AASO,SAAS,WAAW,SAAuB;AAChD,UAAQ,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,IAAI,OAAO,CAAC,EAAE;AACtD;AASO,SAAS,aAAa,SAAuB;AAClD,UAAQ,MAAM,GAAG,MAAM,OAAO,IAAI,MAAM,KAAK,OAAO,CAAC,EAAE;AACzD;AASO,SAAS,UAAU,SAAuB;AAC/C,UAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE;AACxC;AAYO,SAAS,WAAW,MAAuD;AAChF,QAAM,eAAe,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,EAAE,IAAI,OAAK,EAAE,MAAM,CAAC;AAErE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAM,YAAY,IAAI,OAAO,YAAY;AACzC,UAAM,eAAe,SAAS,OAAO,MAAM,WAAW;AACtD,YAAQ,IAAI,KAAK,MAAM,MAAM,SAAS,CAAC,KAAK,YAAY,EAAE;AAAA,EAC5D;AACF;AAQO,SAAS,UAAU,MAAqB;AAC7C,UAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC3C;AASO,SAAS,UAAU,SAAiB,aAA2B;AACpE,UAAQ,IAAI,GAAG,MAAM,KAAK,QAAQ,MAAM,QAAQ,OAAO,CAAC,OAAO,WAAW,EAAE;AAC9E;AASO,SAAS,UAAU,KAAa,aAA2B;AAChE,UAAQ,IAAI,GAAG,MAAM,KAAK,IAAI,WAAW,KAAK,MAAM,IAAI,GAAG,CAAC,EAAE;AAChE;AAKO,SAAS,YAAY,OAAuB;AACjD,MAAI,UAAU,EAAG,QAAO;AAExB,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,MAAM,IAAI;AAC1C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AAErD,SAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC9D;AAKO,SAAS,eAAe,IAAoB;AACjD,MAAI,KAAK,IAAM,QAAO,GAAG,EAAE;AAC3B,MAAI,KAAK,IAAO,QAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAChD,MAAI,KAAK,KAAS,QAAO,GAAG,KAAK,MAAM,KAAK,GAAK,CAAC,KAAK,KAAK,MAAO,KAAK,MAAS,GAAI,CAAC;AAEtF,QAAM,QAAQ,KAAK,MAAM,KAAK,IAAO;AACrC,QAAM,UAAU,KAAK,MAAO,KAAK,OAAW,GAAK;AACjD,SAAO,GAAG,KAAK,KAAK,OAAO;AAC7B;AAKO,SAAS,SAAS,KAAa,WAA2B;AAC/D,MAAI,IAAI,UAAU,UAAW,QAAO;AACpC,SAAO,IAAI,MAAM,GAAG,YAAY,CAAC,IAAI;AACvC;","names":[]}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Loading Spinner Utilities
3
+ *
4
+ * Wraps ora spinner with consistent styling and error handling.
5
+ */
6
+ import { Ora } from 'ora';
7
+ /**
8
+ * A single stage in a forgeProgress pipeline.
9
+ */
10
+ export interface ForgeStage {
11
+ label: string;
12
+ fn: () => Promise<unknown>;
13
+ }
14
+ /**
15
+ * Run multi-stage operations with a branded gradient progress bar.
16
+ *
17
+ * TTY: draws the bar, stage counter, and labeled stage row, redrawing
18
+ * after each stage completes. Non-TTY: falls back to `[i/N] label...`
19
+ * log lines. Suppressed entirely when `SPECFORGE_JSON=1` is set.
20
+ */
21
+ export declare function forgeProgress(context: string, stages: ForgeStage[]): Promise<unknown[]>;
22
+ /**
23
+ * Spinner instance type
24
+ */
25
+ export type Spinner = Ora;
26
+ /**
27
+ * Create a new spinner with consistent styling
28
+ *
29
+ * @param text - Initial spinner text
30
+ * @returns Spinner instance
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const spinner = createSpinner('Loading...');
35
+ * spinner.start();
36
+ *
37
+ * try {
38
+ * await doAsyncWork();
39
+ * spinner.succeed('Done!');
40
+ * } catch (error) {
41
+ * spinner.fail('Failed');
42
+ * }
43
+ * ```
44
+ */
45
+ export declare function createSpinner(text: string): Spinner;
46
+ /**
47
+ * Run an async function with a spinner
48
+ *
49
+ * Automatically handles success/failure states.
50
+ *
51
+ * @param text - Spinner text while running
52
+ * @param fn - Async function to run
53
+ * @param successText - Text to show on success (optional)
54
+ * @returns Result of the async function
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const data = await withSpinner(
59
+ * 'Fetching tickets...',
60
+ * async () => await api.getTickets(),
61
+ * 'Tickets loaded'
62
+ * );
63
+ * ```
64
+ */
65
+ export declare function withSpinner<T>(text: string, fn: () => Promise<T>, successText?: string): Promise<T>;
66
+ /**
67
+ * Run multiple async operations with progress
68
+ *
69
+ * @param operations - Array of operations with labels
70
+ * @returns Array of results
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const results = await withProgress([
75
+ * { label: 'Loading config', fn: loadConfig },
76
+ * { label: 'Fetching data', fn: fetchData },
77
+ * { label: 'Processing', fn: process },
78
+ * ]);
79
+ * ```
80
+ */
81
+ export declare function withProgress<T>(operations: Array<{
82
+ label: string;
83
+ fn: () => Promise<T>;
84
+ }>): Promise<T[]>;
85
+ /**
86
+ * Create a manual spinner for custom control
87
+ *
88
+ * @param text - Initial text
89
+ * @returns Object with spinner control methods
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const { start, update, succeed, fail } = manualSpinner('Loading');
94
+ *
95
+ * start();
96
+ * update('Loading step 1...');
97
+ * update('Loading step 2...');
98
+ * succeed('All loaded');
99
+ * ```
100
+ */
101
+ export declare function manualSpinner(text: string): {
102
+ start: () => Ora;
103
+ stop: () => Ora;
104
+ update: (newText: string) => void;
105
+ succeed: (finalText?: string) => void;
106
+ fail: (finalText?: string) => void;
107
+ warn: (finalText?: string) => void;
108
+ info: (finalText?: string) => void;
109
+ };
110
+ /**
111
+ * Check if spinners should be disabled (non-TTY)
112
+ */
113
+ export declare function shouldDisableSpinner(): boolean;
114
+ /**
115
+ * Simple progress indicator for non-TTY environments
116
+ *
117
+ * @param text - Progress text
118
+ */
119
+ export declare function progressLog(text: string): void;
120
+ //# sourceMappingURL=spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../../src/cli/ui/spinner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAY,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AA0C/B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,EAAE,GACnB,OAAO,CAAC,OAAO,EAAE,CAAC,CAmDpB;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC;AAE1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,CAAC,CAAC,CAYZ;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,UAAU,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,GACzD,OAAO,CAAC,CAAC,EAAE,CAAC,CAad;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM;;;sBAMpB,MAAM;0BAGF,MAAM;uBAGT,MAAM;uBAGN,MAAM;uBAGN,MAAM;EAI5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9C"}
@@ -0,0 +1,158 @@
1
+ import readline from "node:readline";
2
+ import ora from "ora";
3
+ import { icons } from "./colors.js";
4
+ import { BRAND_PALETTE, ANSI_RESET, ANSI_BOLD } from "./banner.js";
5
+ const C = BRAND_PALETTE;
6
+ const GRADIENT_STOPS = [C.g1, C.g2, C.g3, C.g4, C.g5, C.g6, C.g7, C.g8, C.pink];
7
+ const BAR_WIDTH = 23;
8
+ const FORGE_RENDER_LINES = 5;
9
+ function renderGradientBar(filledCount, totalWidth) {
10
+ let out = "";
11
+ for (let i = 0; i < totalWidth; i++) {
12
+ if (i < filledCount) {
13
+ const stopIdx = Math.min(
14
+ GRADIENT_STOPS.length - 1,
15
+ Math.floor(i / Math.max(1, totalWidth - 1) * (GRADIENT_STOPS.length - 1))
16
+ );
17
+ out += `${GRADIENT_STOPS[stopIdx]}\u25B0${ANSI_RESET}`;
18
+ } else {
19
+ out += `${C.darkGray}\u25B1${ANSI_RESET}`;
20
+ }
21
+ }
22
+ return out;
23
+ }
24
+ function renderStageLabels(stages, activeIdx, allDone) {
25
+ const parts = [];
26
+ for (let i = 0; i < stages.length; i++) {
27
+ if (allDone || i < activeIdx) {
28
+ parts.push(`${C.g4}\u2713${ANSI_RESET} ${stages[i].label}`);
29
+ } else if (i === activeIdx) {
30
+ parts.push(`${C.pink}\u2692${ANSI_RESET} ${stages[i].label}`);
31
+ }
32
+ }
33
+ return parts.join(" ");
34
+ }
35
+ async function forgeProgress(context, stages) {
36
+ const results = [];
37
+ const silent = process.env.SPECFORGE_JSON === "1";
38
+ const tty = process.stdout.isTTY && !silent;
39
+ if (silent) {
40
+ for (const stage of stages) {
41
+ results.push(await stage.fn());
42
+ }
43
+ return results;
44
+ }
45
+ if (!tty) {
46
+ for (let i = 0; i < stages.length; i++) {
47
+ console.log(`[${i + 1}/${stages.length}] ${stages[i].label}...`);
48
+ results.push(await stages[i].fn());
49
+ }
50
+ return results;
51
+ }
52
+ const render = (activeIdx, allDone) => {
53
+ const current = allDone ? stages.length : activeIdx;
54
+ const filled = Math.round(current / stages.length * BAR_WIDTH);
55
+ const header = ` ${C.darkGray}${context}${ANSI_RESET} ${C.darkGray}\xB7${ANSI_RESET} ${ANSI_BOLD}${C.g5}${current}/${stages.length}${ANSI_RESET} ${C.darkGray}stages${ANSI_RESET}`;
56
+ const bar = ` ${renderGradientBar(filled, BAR_WIDTH)}`;
57
+ const labels = ` ${renderStageLabels(stages, activeIdx, allDone)}`;
58
+ return `${header}
59
+
60
+ ${bar}
61
+
62
+ ${labels}`;
63
+ };
64
+ process.stdout.write(render(0, false) + "\n");
65
+ for (let i = 0; i < stages.length; i++) {
66
+ try {
67
+ const result = await stages[i].fn();
68
+ results.push(result);
69
+ readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);
70
+ readline.clearScreenDown(process.stdout);
71
+ const done = i === stages.length - 1;
72
+ process.stdout.write(render(done ? i : i + 1, done) + "\n");
73
+ } catch (err) {
74
+ readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);
75
+ readline.clearScreenDown(process.stdout);
76
+ const failHeader = ` ${C.darkGray}${context}${ANSI_RESET} ${C.pink}failed${ANSI_RESET} ${C.darkGray}at${ANSI_RESET} ${ANSI_BOLD}${C.pink}${stages[i].label}${ANSI_RESET}`;
77
+ process.stdout.write(`${failHeader}
78
+
79
+
80
+
81
+
82
+ `);
83
+ throw err;
84
+ }
85
+ }
86
+ return results;
87
+ }
88
+ function createSpinner(text) {
89
+ return ora({
90
+ text,
91
+ color: "cyan",
92
+ spinner: "dots"
93
+ });
94
+ }
95
+ async function withSpinner(text, fn, successText) {
96
+ const spinner = createSpinner(text);
97
+ spinner.start();
98
+ try {
99
+ const result = await fn();
100
+ spinner.succeed(successText || text.replace(/\.{3}$/, ""));
101
+ return result;
102
+ } catch (error) {
103
+ spinner.fail(text.replace(/\.{3}$/, " failed"));
104
+ throw error;
105
+ }
106
+ }
107
+ async function withProgress(operations) {
108
+ const results = [];
109
+ for (let i = 0; i < operations.length; i++) {
110
+ const { label, fn } = operations[i];
111
+ const progress = `[${i + 1}/${operations.length}]`;
112
+ const text = `${progress} ${label}...`;
113
+ const result = await withSpinner(text, fn);
114
+ results.push(result);
115
+ }
116
+ return results;
117
+ }
118
+ function manualSpinner(text) {
119
+ const spinner = createSpinner(text);
120
+ return {
121
+ start: () => spinner.start(),
122
+ stop: () => spinner.stop(),
123
+ update: (newText) => {
124
+ spinner.text = newText;
125
+ },
126
+ succeed: (finalText) => {
127
+ spinner.succeed(finalText);
128
+ },
129
+ fail: (finalText) => {
130
+ spinner.fail(finalText);
131
+ },
132
+ warn: (finalText) => {
133
+ spinner.warn(finalText);
134
+ },
135
+ info: (finalText) => {
136
+ spinner.info(finalText);
137
+ }
138
+ };
139
+ }
140
+ function shouldDisableSpinner() {
141
+ return !process.stdout.isTTY;
142
+ }
143
+ function progressLog(text) {
144
+ if (process.stdout.isTTY) {
145
+ return;
146
+ }
147
+ console.log(`${icons.running} ${text}`);
148
+ }
149
+ export {
150
+ createSpinner,
151
+ forgeProgress,
152
+ manualSpinner,
153
+ progressLog,
154
+ shouldDisableSpinner,
155
+ withProgress,
156
+ withSpinner
157
+ };
158
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/ui/spinner.ts"],"sourcesContent":["/**\n * Loading Spinner Utilities\n *\n * Wraps ora spinner with consistent styling and error handling.\n */\n\nimport readline from 'node:readline';\nimport ora, { Ora } from 'ora';\nimport { colors, icons } from './colors.js';\nimport { BRAND_PALETTE, ANSI_RESET, ANSI_BOLD } from './banner.js';\n\nconst C = BRAND_PALETTE;\n\nconst GRADIENT_STOPS = [C.g1, C.g2, C.g3, C.g4, C.g5, C.g6, C.g7, C.g8, C.pink] as const;\nconst BAR_WIDTH = 23;\nconst FORGE_RENDER_LINES = 5;\n\nfunction renderGradientBar(filledCount: number, totalWidth: number): string {\n let out = '';\n for (let i = 0; i < totalWidth; i++) {\n if (i < filledCount) {\n const stopIdx = Math.min(\n GRADIENT_STOPS.length - 1,\n Math.floor((i / Math.max(1, totalWidth - 1)) * (GRADIENT_STOPS.length - 1))\n );\n out += `${GRADIENT_STOPS[stopIdx]}▰${ANSI_RESET}`;\n } else {\n out += `${C.darkGray}▱${ANSI_RESET}`;\n }\n }\n return out;\n}\n\nfunction renderStageLabels(\n stages: ReadonlyArray<ForgeStage>,\n activeIdx: number,\n allDone: boolean\n): string {\n const parts: string[] = [];\n for (let i = 0; i < stages.length; i++) {\n if (allDone || i < activeIdx) {\n parts.push(`${C.g4}✓${ANSI_RESET} ${stages[i].label}`);\n } else if (i === activeIdx) {\n parts.push(`${C.pink}⚒${ANSI_RESET} ${stages[i].label}`);\n }\n }\n return parts.join(' ');\n}\n\n/**\n * A single stage in a forgeProgress pipeline.\n */\nexport interface ForgeStage {\n label: string;\n fn: () => Promise<unknown>;\n}\n\n/**\n * Run multi-stage operations with a branded gradient progress bar.\n *\n * TTY: draws the bar, stage counter, and labeled stage row, redrawing\n * after each stage completes. Non-TTY: falls back to `[i/N] label...`\n * log lines. Suppressed entirely when `SPECFORGE_JSON=1` is set.\n */\nexport async function forgeProgress(\n context: string,\n stages: ForgeStage[]\n): Promise<unknown[]> {\n const results: unknown[] = [];\n const silent = process.env.SPECFORGE_JSON === '1';\n const tty = process.stdout.isTTY && !silent;\n\n if (silent) {\n for (const stage of stages) {\n results.push(await stage.fn());\n }\n return results;\n }\n\n if (!tty) {\n for (let i = 0; i < stages.length; i++) {\n console.log(`[${i + 1}/${stages.length}] ${stages[i].label}...`);\n results.push(await stages[i].fn());\n }\n return results;\n }\n\n const render = (activeIdx: number, allDone: boolean): string => {\n const current = allDone ? stages.length : activeIdx;\n const filled = Math.round((current / stages.length) * BAR_WIDTH);\n const header = ` ${C.darkGray}${context}${ANSI_RESET} ${C.darkGray}·${ANSI_RESET} ${ANSI_BOLD}${C.g5}${current}/${stages.length}${ANSI_RESET} ${C.darkGray}stages${ANSI_RESET}`;\n const bar = ` ${renderGradientBar(filled, BAR_WIDTH)}`;\n const labels = ` ${renderStageLabels(stages, activeIdx, allDone)}`;\n return `${header}\\n\\n${bar}\\n\\n${labels}`;\n };\n\n // Initial frame with stage 0 active\n process.stdout.write(render(0, false) + '\\n');\n\n for (let i = 0; i < stages.length; i++) {\n try {\n const result = await stages[i].fn();\n results.push(result);\n\n readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);\n readline.clearScreenDown(process.stdout);\n const done = i === stages.length - 1;\n process.stdout.write(render(done ? i : i + 1, done) + '\\n');\n } catch (err) {\n readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);\n readline.clearScreenDown(process.stdout);\n const failHeader = ` ${C.darkGray}${context}${ANSI_RESET} ${C.pink}failed${ANSI_RESET} ${C.darkGray}at${ANSI_RESET} ${ANSI_BOLD}${C.pink}${stages[i].label}${ANSI_RESET}`;\n process.stdout.write(`${failHeader}\\n\\n\\n\\n\\n`);\n throw err;\n }\n }\n\n return results;\n}\n\n/**\n * Spinner instance type\n */\nexport type Spinner = Ora;\n\n/**\n * Create a new spinner with consistent styling\n *\n * @param text - Initial spinner text\n * @returns Spinner instance\n *\n * @example\n * ```typescript\n * const spinner = createSpinner('Loading...');\n * spinner.start();\n *\n * try {\n * await doAsyncWork();\n * spinner.succeed('Done!');\n * } catch (error) {\n * spinner.fail('Failed');\n * }\n * ```\n */\nexport function createSpinner(text: string): Spinner {\n return ora({\n text,\n color: 'cyan',\n spinner: 'dots',\n });\n}\n\n/**\n * Run an async function with a spinner\n *\n * Automatically handles success/failure states.\n *\n * @param text - Spinner text while running\n * @param fn - Async function to run\n * @param successText - Text to show on success (optional)\n * @returns Result of the async function\n *\n * @example\n * ```typescript\n * const data = await withSpinner(\n * 'Fetching tickets...',\n * async () => await api.getTickets(),\n * 'Tickets loaded'\n * );\n * ```\n */\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>,\n successText?: string\n): Promise<T> {\n const spinner = createSpinner(text);\n spinner.start();\n\n try {\n const result = await fn();\n spinner.succeed(successText || text.replace(/\\.{3}$/, ''));\n return result;\n } catch (error) {\n spinner.fail(text.replace(/\\.{3}$/, ' failed'));\n throw error;\n }\n}\n\n/**\n * Run multiple async operations with progress\n *\n * @param operations - Array of operations with labels\n * @returns Array of results\n *\n * @example\n * ```typescript\n * const results = await withProgress([\n * { label: 'Loading config', fn: loadConfig },\n * { label: 'Fetching data', fn: fetchData },\n * { label: 'Processing', fn: process },\n * ]);\n * ```\n */\nexport async function withProgress<T>(\n operations: Array<{ label: string; fn: () => Promise<T> }>\n): Promise<T[]> {\n const results: T[] = [];\n\n for (let i = 0; i < operations.length; i++) {\n const { label, fn } = operations[i];\n const progress = `[${i + 1}/${operations.length}]`;\n const text = `${progress} ${label}...`;\n\n const result = await withSpinner(text, fn);\n results.push(result);\n }\n\n return results;\n}\n\n/**\n * Create a manual spinner for custom control\n *\n * @param text - Initial text\n * @returns Object with spinner control methods\n *\n * @example\n * ```typescript\n * const { start, update, succeed, fail } = manualSpinner('Loading');\n *\n * start();\n * update('Loading step 1...');\n * update('Loading step 2...');\n * succeed('All loaded');\n * ```\n */\nexport function manualSpinner(text: string) {\n const spinner = createSpinner(text);\n\n return {\n start: () => spinner.start(),\n stop: () => spinner.stop(),\n update: (newText: string) => {\n spinner.text = newText;\n },\n succeed: (finalText?: string) => {\n spinner.succeed(finalText);\n },\n fail: (finalText?: string) => {\n spinner.fail(finalText);\n },\n warn: (finalText?: string) => {\n spinner.warn(finalText);\n },\n info: (finalText?: string) => {\n spinner.info(finalText);\n },\n };\n}\n\n/**\n * Check if spinners should be disabled (non-TTY)\n */\nexport function shouldDisableSpinner(): boolean {\n return !process.stdout.isTTY;\n}\n\n/**\n * Simple progress indicator for non-TTY environments\n *\n * @param text - Progress text\n */\nexport function progressLog(text: string): void {\n if (process.stdout.isTTY) {\n // In TTY, use spinner instead\n return;\n }\n\n console.log(`${icons.running} ${text}`);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Time Utilities
3
+ *
4
+ * Formatting utilities for time display in CLI output.
5
+ */
6
+ /**
7
+ * Format ISO timestamp to relative time string
8
+ *
9
+ * Examples:
10
+ * - "just now" (< 1 minute)
11
+ * - "5m ago" (minutes)
12
+ * - "2h ago" (hours)
13
+ * - "3 days ago" (days)
14
+ * - "2 weeks ago" (weeks)
15
+ * - "1 month ago" (months)
16
+ * - "1 year ago" (years)
17
+ *
18
+ * @param isoString ISO 8601 timestamp string
19
+ * @returns Human-readable relative time
20
+ */
21
+ export declare function formatRelativeTime(isoString: string): string;
22
+ /**
23
+ * Format duration between two timestamps
24
+ *
25
+ * @param startIso Start timestamp (ISO 8601)
26
+ * @param endIso End timestamp (ISO 8601), defaults to now
27
+ * @returns Human-readable duration string
28
+ */
29
+ export declare function formatDuration(startIso: string, endIso?: string): string;
30
+ /**
31
+ * Format a timestamp for display
32
+ *
33
+ * @param isoString ISO 8601 timestamp string
34
+ * @param format Output format: 'relative', 'short', 'long'
35
+ * @returns Formatted time string
36
+ */
37
+ export declare function formatTimestamp(isoString: string, format?: 'relative' | 'short' | 'long'): string;
38
+ /**
39
+ * Check if a timestamp is within a given time window
40
+ *
41
+ * @param isoString ISO 8601 timestamp string
42
+ * @param windowMs Time window in milliseconds
43
+ * @returns True if within window
44
+ */
45
+ export declare function isWithinTimeWindow(isoString: string, windowMs: number): boolean;
46
+ /**
47
+ * Get "time ago" for session display
48
+ * Similar to formatRelativeTime but more compact
49
+ *
50
+ * @param isoString ISO 8601 timestamp string
51
+ * @returns Compact relative time (e.g., "2h 15m ago")
52
+ */
53
+ export declare function formatTimeAgo(isoString: string): string;
54
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../src/cli/ui/time.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAgD5D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BxE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,UAAU,GAAG,OAAO,GAAG,MAAmB,GACjD,MAAM,CA6BR;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK/E;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0BvD"}
@@ -0,0 +1,119 @@
1
+ const MS_PER_MINUTE = 60 * 1e3;
2
+ const MS_PER_HOUR = 60 * MS_PER_MINUTE;
3
+ const MS_PER_DAY = 24 * MS_PER_HOUR;
4
+ const MS_PER_WEEK = 7 * MS_PER_DAY;
5
+ const MS_PER_MONTH = 30 * MS_PER_DAY;
6
+ const MS_PER_YEAR = 365 * MS_PER_DAY;
7
+ function formatRelativeTime(isoString) {
8
+ const date = new Date(isoString);
9
+ const now = /* @__PURE__ */ new Date();
10
+ const diffMs = now.getTime() - date.getTime();
11
+ if (diffMs < 0) {
12
+ return "in the future";
13
+ }
14
+ if (diffMs < MS_PER_MINUTE) {
15
+ return "just now";
16
+ }
17
+ if (diffMs < MS_PER_HOUR) {
18
+ const minutes = Math.floor(diffMs / MS_PER_MINUTE);
19
+ return `${minutes}m ago`;
20
+ }
21
+ if (diffMs < MS_PER_DAY) {
22
+ const hours = Math.floor(diffMs / MS_PER_HOUR);
23
+ return `${hours}h ago`;
24
+ }
25
+ if (diffMs < MS_PER_WEEK) {
26
+ const days = Math.floor(diffMs / MS_PER_DAY);
27
+ return days === 1 ? "1 day ago" : `${days} days ago`;
28
+ }
29
+ if (diffMs < MS_PER_MONTH) {
30
+ const weeks = Math.floor(diffMs / MS_PER_WEEK);
31
+ return weeks === 1 ? "1 week ago" : `${weeks} weeks ago`;
32
+ }
33
+ if (diffMs < MS_PER_YEAR) {
34
+ const months = Math.floor(diffMs / MS_PER_MONTH);
35
+ return months === 1 ? "1 month ago" : `${months} months ago`;
36
+ }
37
+ const years = Math.floor(diffMs / MS_PER_YEAR);
38
+ return years === 1 ? "1 year ago" : `${years} years ago`;
39
+ }
40
+ function formatDuration(startIso, endIso) {
41
+ const start = new Date(startIso);
42
+ const end = endIso ? new Date(endIso) : /* @__PURE__ */ new Date();
43
+ const diffMs = end.getTime() - start.getTime();
44
+ if (diffMs < 0) {
45
+ return "0m";
46
+ }
47
+ const days = Math.floor(diffMs / MS_PER_DAY);
48
+ const hours = Math.floor(diffMs % MS_PER_DAY / MS_PER_HOUR);
49
+ const minutes = Math.floor(diffMs % MS_PER_HOUR / MS_PER_MINUTE);
50
+ if (days > 0) {
51
+ return `${days}d ${hours}h`;
52
+ }
53
+ if (hours > 0) {
54
+ return `${hours}h ${minutes}m`;
55
+ }
56
+ if (minutes > 0) {
57
+ return `${minutes}m`;
58
+ }
59
+ return "just started";
60
+ }
61
+ function formatTimestamp(isoString, format = "relative") {
62
+ const date = new Date(isoString);
63
+ switch (format) {
64
+ case "relative":
65
+ return formatRelativeTime(isoString);
66
+ case "short":
67
+ return date.toLocaleString("en-US", {
68
+ month: "short",
69
+ day: "numeric",
70
+ hour: "numeric",
71
+ minute: "2-digit"
72
+ });
73
+ case "long":
74
+ return date.toLocaleString("en-US", {
75
+ year: "numeric",
76
+ month: "long",
77
+ day: "numeric",
78
+ hour: "numeric",
79
+ minute: "2-digit"
80
+ });
81
+ default:
82
+ return formatRelativeTime(isoString);
83
+ }
84
+ }
85
+ function isWithinTimeWindow(isoString, windowMs) {
86
+ const date = new Date(isoString);
87
+ const now = /* @__PURE__ */ new Date();
88
+ const diffMs = now.getTime() - date.getTime();
89
+ return diffMs >= 0 && diffMs <= windowMs;
90
+ }
91
+ function formatTimeAgo(isoString) {
92
+ const date = new Date(isoString);
93
+ const now = /* @__PURE__ */ new Date();
94
+ const diffMs = now.getTime() - date.getTime();
95
+ if (diffMs < 0) {
96
+ return "in the future";
97
+ }
98
+ if (diffMs < MS_PER_MINUTE) {
99
+ return "just now";
100
+ }
101
+ const days = Math.floor(diffMs / MS_PER_DAY);
102
+ const hours = Math.floor(diffMs % MS_PER_DAY / MS_PER_HOUR);
103
+ const minutes = Math.floor(diffMs % MS_PER_HOUR / MS_PER_MINUTE);
104
+ if (days > 0) {
105
+ return `${days}d ${hours}h ago`;
106
+ }
107
+ if (hours > 0) {
108
+ return `${hours}h ${minutes}m ago`;
109
+ }
110
+ return `${minutes}m ago`;
111
+ }
112
+ export {
113
+ formatDuration,
114
+ formatRelativeTime,
115
+ formatTimeAgo,
116
+ formatTimestamp,
117
+ isWithinTimeWindow
118
+ };
119
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/ui/time.ts"],"sourcesContent":["/**\n * Time Utilities\n *\n * Formatting utilities for time display in CLI output.\n */\n\n/**\n * Time unit thresholds in milliseconds\n */\nconst MS_PER_MINUTE = 60 * 1000;\nconst MS_PER_HOUR = 60 * MS_PER_MINUTE;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\nconst MS_PER_WEEK = 7 * MS_PER_DAY;\nconst MS_PER_MONTH = 30 * MS_PER_DAY;\nconst MS_PER_YEAR = 365 * MS_PER_DAY;\n\n/**\n * Format ISO timestamp to relative time string\n *\n * Examples:\n * - \"just now\" (< 1 minute)\n * - \"5m ago\" (minutes)\n * - \"2h ago\" (hours)\n * - \"3 days ago\" (days)\n * - \"2 weeks ago\" (weeks)\n * - \"1 month ago\" (months)\n * - \"1 year ago\" (years)\n *\n * @param isoString ISO 8601 timestamp string\n * @returns Human-readable relative time\n */\nexport function formatRelativeTime(isoString: string): string {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n\n // Handle future dates\n if (diffMs < 0) {\n return 'in the future';\n }\n\n // Just now (< 1 minute)\n if (diffMs < MS_PER_MINUTE) {\n return 'just now';\n }\n\n // Minutes\n if (diffMs < MS_PER_HOUR) {\n const minutes = Math.floor(diffMs / MS_PER_MINUTE);\n return `${minutes}m ago`;\n }\n\n // Hours\n if (diffMs < MS_PER_DAY) {\n const hours = Math.floor(diffMs / MS_PER_HOUR);\n return `${hours}h ago`;\n }\n\n // Days\n if (diffMs < MS_PER_WEEK) {\n const days = Math.floor(diffMs / MS_PER_DAY);\n return days === 1 ? '1 day ago' : `${days} days ago`;\n }\n\n // Weeks\n if (diffMs < MS_PER_MONTH) {\n const weeks = Math.floor(diffMs / MS_PER_WEEK);\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n }\n\n // Months\n if (diffMs < MS_PER_YEAR) {\n const months = Math.floor(diffMs / MS_PER_MONTH);\n return months === 1 ? '1 month ago' : `${months} months ago`;\n }\n\n // Years\n const years = Math.floor(diffMs / MS_PER_YEAR);\n return years === 1 ? '1 year ago' : `${years} years ago`;\n}\n\n/**\n * Format duration between two timestamps\n *\n * @param startIso Start timestamp (ISO 8601)\n * @param endIso End timestamp (ISO 8601), defaults to now\n * @returns Human-readable duration string\n */\nexport function formatDuration(startIso: string, endIso?: string): string {\n const start = new Date(startIso);\n const end = endIso ? new Date(endIso) : new Date();\n const diffMs = end.getTime() - start.getTime();\n\n if (diffMs < 0) {\n return '0m';\n }\n\n const days = Math.floor(diffMs / MS_PER_DAY);\n const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR);\n const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE);\n\n if (days > 0) {\n return `${days}d ${hours}h`;\n }\n\n if (hours > 0) {\n return `${hours}h ${minutes}m`;\n }\n\n if (minutes > 0) {\n return `${minutes}m`;\n }\n\n return 'just started';\n}\n\n/**\n * Format a timestamp for display\n *\n * @param isoString ISO 8601 timestamp string\n * @param format Output format: 'relative', 'short', 'long'\n * @returns Formatted time string\n */\nexport function formatTimestamp(\n isoString: string,\n format: 'relative' | 'short' | 'long' = 'relative'\n): string {\n const date = new Date(isoString);\n\n switch (format) {\n case 'relative':\n return formatRelativeTime(isoString);\n\n case 'short':\n // e.g., \"Jan 15, 2:30 PM\"\n return date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n });\n\n case 'long':\n // e.g., \"January 15, 2024 at 2:30 PM\"\n return date.toLocaleString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n });\n\n default:\n return formatRelativeTime(isoString);\n }\n}\n\n/**\n * Check if a timestamp is within a given time window\n *\n * @param isoString ISO 8601 timestamp string\n * @param windowMs Time window in milliseconds\n * @returns True if within window\n */\nexport function isWithinTimeWindow(isoString: string, windowMs: number): boolean {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n return diffMs >= 0 && diffMs <= windowMs;\n}\n\n/**\n * Get \"time ago\" for session display\n * Similar to formatRelativeTime but more compact\n *\n * @param isoString ISO 8601 timestamp string\n * @returns Compact relative time (e.g., \"2h 15m ago\")\n */\nexport function formatTimeAgo(isoString: string): string {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n\n if (diffMs < 0) {\n return 'in the future';\n }\n\n if (diffMs < MS_PER_MINUTE) {\n return 'just now';\n }\n\n const days = Math.floor(diffMs / MS_PER_DAY);\n const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR);\n const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE);\n\n if (days > 0) {\n return `${days}d ${hours}h ago`;\n }\n\n if (hours > 0) {\n return `${hours}h ${minutes}m ago`;\n }\n\n return `${minutes}m ago`;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * SpecForge API Client
3
+ *
4
+ * HTTP client for communicating with the SpecForge API Gateway.
5
+ * Handles authentication, request/response formatting, and error handling.
6
+ */
7
+ import { McpConfig } from '../config/index.js';
8
+ import type { ITransport, TransportOrigin } from '../transport/interface.js';
9
+ /**
10
+ * API response structure returned by SpecForge API
11
+ */
12
+ export interface ApiResponse<T = unknown> {
13
+ /** Response data on success */
14
+ data?: T;
15
+ /** Error message on failure */
16
+ error?: string;
17
+ }
18
+ /**
19
+ * Result of API key validation
20
+ */
21
+ export interface ApiKeyValidationResult {
22
+ /** Whether the API key is valid */
23
+ valid: boolean;
24
+ /** User ID associated with the key (if valid) */
25
+ userId?: string;
26
+ /** Permissions granted to the key (if valid) */
27
+ permissions?: {
28
+ read?: boolean;
29
+ write?: boolean;
30
+ admin?: boolean;
31
+ };
32
+ /** Error message (if invalid) */
33
+ message?: string;
34
+ }
35
+ /**
36
+ * Options for API requests
37
+ */
38
+ export interface ApiRequestOptions {
39
+ /** Request timeout in milliseconds */
40
+ timeout?: number;
41
+ /** Additional headers to include */
42
+ headers?: Record<string, string>;
43
+ /** Signal for aborting the request */
44
+ signal?: AbortSignal;
45
+ }
46
+ /**
47
+ * API Client for SpecForge
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * import { getConfig } from './config/index.js';
52
+ * import { ApiClient } from './client/api-client.js';
53
+ *
54
+ * const config = getConfig();
55
+ * const client = new ApiClient(config);
56
+ *
57
+ * // Make an API call
58
+ * const projects = await client.call('list_projects', { userId: 'user123' });
59
+ * ```
60
+ */
61
+ export declare class ApiClient {
62
+ private config;
63
+ private transport;
64
+ private sessionId;
65
+ /**
66
+ * @param config - resolved MCP config (URL, key, debug)
67
+ * @param options - optional client options; `origin` sets the
68
+ * `X-SpecForge-Origin` header (M9.7), defaulting to `mcp` (agent traffic).
69
+ * The human CLI surface ({@link createCliClient} / the feedback command)
70
+ * passes `cli`.
71
+ */
72
+ constructor(config: McpConfig, options?: {
73
+ origin?: TransportOrigin;
74
+ });
75
+ /**
76
+ * Get the underlying transport for direct use by tool handlers
77
+ */
78
+ getTransport(): ITransport;
79
+ /**
80
+ * Get the session ID for this MCP connection
81
+ * Used for working context management
82
+ */
83
+ getSessionId(): string;
84
+ /**
85
+ * Make an API call to SpecForge
86
+ *
87
+ * @param operation - Name of the operation to execute
88
+ * @param args - Arguments for the operation
89
+ * @param options - Optional request configuration
90
+ * @returns Promise resolving to the operation result
91
+ * @throws {Error} If the request fails or returns an error
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * // List projects
96
+ * const result = await client.call('list_projects');
97
+ *
98
+ * // Get a specific ticket
99
+ * const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });
100
+ * ```
101
+ */
102
+ call<T = unknown>(operation: string, args?: Record<string, unknown>, _options?: ApiRequestOptions): Promise<T>;
103
+ /**
104
+ * Validate the configured API key
105
+ *
106
+ * Makes a lightweight call to verify the API key is valid and
107
+ * returns the associated user information.
108
+ *
109
+ * @returns Promise resolving to validation result
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const result = await client.validateApiKey();
114
+ * if (result.valid) {
115
+ * console.log('Authenticated as:', result.userId);
116
+ * } else {
117
+ * console.error('Invalid API key:', result.message);
118
+ * }
119
+ * ```
120
+ */
121
+ validateApiKey(): Promise<ApiKeyValidationResult>;
122
+ }
123
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/client/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG7E;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,WAAW,CAAC,EAAE;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,sCAAsC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAOD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;;OAMG;gBACS,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAA;KAAO;IAYzE;;OAEG;IACH,YAAY,IAAI,UAAU;IAI1B;;;OAGG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,QAAQ,GAAE,iBAAsB,GAC/B,OAAO,CAAC,CAAC,CAAC;IAkBb;;;;;;;;;;;;;;;;;OAiBG;IACG,cAAc,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAwBxD"}