@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/commands/invitations.ts"],"sourcesContent":["/**\n * Invitations Command\n *\n * Manage project invitations - view received, accept, decline.\n */\n\nimport { Command } from 'commander';\nimport ora from 'ora';\nimport Table from 'cli-table3';\nimport { resolveConfig } from '../config/index.js';\nimport { withErrorHandler, CliError, NetworkError } from '../middleware/error-handler.js';\nimport { printBlank, printHeader } from '../ui/output.js';\nimport { colors } from '../ui/colors.js';\nimport { formatRelativeTime } from '../ui/time.js';\nimport { createCliClient } from '../../client/cli-client.js';\n\n/**\n * Received invitation from API response\n */\ninterface ReceivedInvitation {\n id: string;\n type: 'member' | 'invitation';\n projectId: string;\n projectName?: string;\n role: 'owner' | 'editor' | 'viewer';\n invitedBy?: string;\n invitedAt: string;\n expiresAt?: string;\n}\n\n/**\n * Sent invitation from API response\n */\ninterface SentInvitation {\n id: string;\n projectId: string;\n projectName?: string;\n email: string;\n role: 'editor' | 'viewer';\n status: string;\n createdAt: string;\n expiresAt: string;\n}\n\n/**\n * API response for list_invitations\n */\ninterface ListInvitationsResponse {\n received: ReceivedInvitation[];\n sent?: SentInvitation[];\n}\n\n/**\n * Options for invitations command\n */\ninterface InvitationsOptions {\n sent?: boolean;\n json?: boolean;\n}\n\n/**\n * Get role badge with color\n */\nfunction getRoleBadge(role: string): string {\n switch (role) {\n case 'owner':\n return colors.warning('owner');\n case 'editor':\n return colors.primary('editor');\n case 'viewer':\n return colors.muted('viewer');\n default:\n return role;\n }\n}\n\n/**\n * Display received invitations as table\n */\nfunction displayReceivedInvitations(invitations: ReceivedInvitation[]): void {\n if (invitations.length === 0) {\n console.log(colors.muted('No pending invitations.'));\n return;\n }\n\n printHeader('Received Invitations');\n printBlank();\n\n const table = new Table({\n head: [\n colors.muted('PROJECT'),\n colors.muted('ROLE'),\n colors.muted('INVITED'),\n colors.muted('ID'),\n ],\n style: { head: [], border: [] },\n chars: {\n 'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',\n 'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',\n 'left': '', 'left-mid': '', 'mid': '', 'mid-mid': '',\n 'right': '', 'right-mid': '', 'middle': ' ',\n },\n });\n\n for (const inv of invitations) {\n table.push([\n inv.projectName || inv.projectId,\n getRoleBadge(inv.role),\n formatRelativeTime(inv.invitedAt),\n colors.muted(inv.id.substring(0, 12) + '...'),\n ]);\n }\n\n console.log(table.toString());\n printBlank();\n console.log(colors.muted('Accept: specforge invitations accept <id>'));\n console.log(colors.muted('Decline: specforge invitations decline <id>'));\n}\n\n/**\n * Display sent invitations as table\n */\nfunction displaySentInvitations(invitations: SentInvitation[]): void {\n if (invitations.length === 0) {\n console.log(colors.muted('No sent invitations.'));\n return;\n }\n\n printBlank();\n printHeader('Sent Invitations');\n printBlank();\n\n const table = new Table({\n head: [\n colors.muted('EMAIL'),\n colors.muted('PROJECT'),\n colors.muted('ROLE'),\n colors.muted('STATUS'),\n colors.muted('SENT'),\n ],\n style: { head: [], border: [] },\n chars: {\n 'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',\n 'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',\n 'left': '', 'left-mid': '', 'mid': '', 'mid-mid': '',\n 'right': '', 'right-mid': '', 'middle': ' ',\n },\n });\n\n for (const inv of invitations) {\n let statusBadge: string;\n switch (inv.status) {\n case 'pending':\n statusBadge = colors.warning('pending');\n break;\n case 'accepted':\n statusBadge = colors.success('accepted');\n break;\n case 'expired':\n statusBadge = colors.error('expired');\n break;\n case 'cancelled':\n statusBadge = colors.muted('cancelled');\n break;\n default:\n statusBadge = inv.status;\n }\n\n table.push([\n inv.email,\n inv.projectName || inv.projectId.substring(0, 12) + '...',\n getRoleBadge(inv.role),\n statusBadge,\n formatRelativeTime(inv.createdAt),\n ]);\n }\n\n console.log(table.toString());\n}\n\n/**\n * List invitations action handler\n */\nexport async function invitationsListAction(options: InvitationsOptions): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const spinner = ora({\n text: 'Fetching invitations...',\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n const response = await client.call<ListInvitationsResponse>('list_invitations', {\n includeSent: options.sent,\n });\n\n spinner.stop();\n\n if (options.json) {\n console.log(JSON.stringify(response, null, 2));\n return;\n }\n\n printBlank();\n displayReceivedInvitations(response.received);\n\n if (options.sent && response.sent) {\n displaySentInvitations(response.sent);\n }\n\n printBlank();\n } catch (error) {\n spinner.fail('Failed to fetch invitations');\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to fetch invitations: ${message}`);\n }\n}\n\n/**\n * Accept invitation action handler\n */\nexport async function invitationsAcceptAction(idOrToken: string): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const spinner = ora({\n text: 'Accepting invitation...',\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // Determine if it's a token or invitation ID\n const args: Record<string, unknown> = {};\n if (idOrToken.startsWith('proj_inv_')) {\n args.token = idOrToken;\n } else {\n args.invitationId = idOrToken;\n }\n\n const response = await client.call<{\n success: boolean;\n projectId: string;\n projectName: string;\n role: string;\n message: string;\n }>('accept_project_invitation', args);\n\n spinner.succeed(`Joined ${response.projectName} as ${response.role}`);\n printBlank();\n console.log(colors.muted(response.message));\n printBlank();\n } catch (error) {\n spinner.fail('Failed to accept invitation');\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to accept invitation: ${message}`);\n }\n}\n\n/**\n * Decline invitation action handler\n */\nexport async function invitationsDeclineAction(idOrToken: string): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const spinner = ora({\n text: 'Declining invitation...',\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // Determine if it's a token or invitation ID\n const args: Record<string, unknown> = {};\n if (idOrToken.startsWith('proj_inv_')) {\n args.token = idOrToken;\n } else {\n args.invitationId = idOrToken;\n }\n\n const response = await client.call<{\n success: boolean;\n message: string;\n }>('decline_project_invitation', args);\n\n spinner.succeed('Invitation declined');\n printBlank();\n } catch (error) {\n spinner.fail('Failed to decline invitation');\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to decline invitation: ${message}`);\n }\n}\n\n/**\n * Register invitations command with Commander\n */\nexport function registerInvitationsCommand(program: Command): void {\n const invitationsCmd = program\n .command('invitations')\n .description('Manage project invitations')\n .option('--sent', 'Include sent invitations')\n .option('--json', 'Output as JSON')\n .action(withErrorHandler(invitationsListAction));\n\n // Accept subcommand\n invitationsCmd\n .command('accept <id-or-token>')\n .description('Accept a project invitation')\n .action(withErrorHandler(invitationsAcceptAction));\n\n // Decline subcommand\n invitationsCmd\n .command('decline <id-or-token>')\n .description('Decline a project invitation')\n .action(withErrorHandler(invitationsDeclineAction));\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Login Command
3
+ *
4
+ * Authenticates users with their SpecForge API key.
5
+ * Supports both interactive (masked input) and non-interactive (--api-key flag) modes.
6
+ */
7
+ import { Command } from 'commander';
8
+ /**
9
+ * Login command options
10
+ */
11
+ export interface LoginOptions {
12
+ /** API key provided via --api-key flag */
13
+ apiKey?: string;
14
+ /** Force re-authentication even if already logged in */
15
+ force?: boolean;
16
+ }
17
+ /**
18
+ * User information returned from API validation
19
+ */
20
+ export interface UserInfo {
21
+ /** User's email address */
22
+ email: string;
23
+ /** User's display name */
24
+ name?: string;
25
+ /** User ID */
26
+ userId?: string;
27
+ }
28
+ /**
29
+ * Login command implementation
30
+ *
31
+ * @param options - Command options
32
+ */
33
+ export declare function loginAction(options: LoginOptions): Promise<void>;
34
+ /**
35
+ * Register login command with Commander
36
+ *
37
+ * @param program - Commander program instance
38
+ */
39
+ export declare function registerLoginCommand(program: Command): void;
40
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA0GD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqEtE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB3D"}
@@ -0,0 +1,139 @@
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { saveGlobalConfig } from "../config/writer.js";
5
+ import { loadGlobalConfig } from "../config/loader.js";
6
+ import { validateApiKeyFormat } from "../middleware/auth-guard.js";
7
+ import { withErrorHandler, NetworkError, ValidationError } from "../middleware/error-handler.js";
8
+ import { printSuccess, printWarning, printHint, printBlank } from "../ui/output.js";
9
+ import { printBanner } from "../ui/banner.js";
10
+ import { createCliClient } from "../../client/cli-client.js";
11
+ import { CONFIG_DEFAULTS } from "../config/types.js";
12
+ async function promptForApiKey() {
13
+ const answers = await inquirer.prompt([
14
+ {
15
+ type: "password",
16
+ name: "apiKey",
17
+ message: "Enter your API key:",
18
+ mask: "*",
19
+ validate: (input) => {
20
+ if (!input || input.trim() === "") {
21
+ return "API key is required";
22
+ }
23
+ const validation = validateApiKeyFormat(input);
24
+ if (!validation.valid) {
25
+ return validation.error || "Invalid API key format";
26
+ }
27
+ return true;
28
+ }
29
+ }
30
+ ]);
31
+ return answers.apiKey;
32
+ }
33
+ async function validateApiKeyWithBackend(apiKey) {
34
+ const client = createCliClient({
35
+ apiKey,
36
+ apiUrl: process.env.SPECFORGE_API_URL || CONFIG_DEFAULTS.apiUrl,
37
+ debug: process.env.SPECFORGE_DEBUG === "true"
38
+ });
39
+ try {
40
+ const user = await client.call("get_current_user", {});
41
+ return {
42
+ email: user.email || "user",
43
+ name: user.name,
44
+ userId: user.userId ?? user.id
45
+ };
46
+ } catch (error) {
47
+ if (error instanceof ValidationError) {
48
+ throw error;
49
+ }
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
52
+ throw new NetworkError(
53
+ "Could not connect to SpecForge API",
54
+ "Check your internet connection and try again"
55
+ );
56
+ }
57
+ if (message.includes("401") || message.includes("Unauthorized") || message.includes("403") || message.includes("Forbidden")) {
58
+ throw new ValidationError(
59
+ "Invalid API key",
60
+ "Get a valid API key from https://specforge.com.br/settings/api-keys"
61
+ );
62
+ }
63
+ if (message.includes("429") || message.includes("rate limit")) {
64
+ throw new NetworkError(
65
+ "Rate limit exceeded",
66
+ "Wait a moment and try again"
67
+ );
68
+ }
69
+ throw new NetworkError(
70
+ `API error: ${message}`,
71
+ "If the problem persists, check https://status.specforge.com"
72
+ );
73
+ }
74
+ }
75
+ async function loginAction(options) {
76
+ const existingConfig = loadGlobalConfig();
77
+ if (existingConfig.apiKey && !options.force) {
78
+ printBlank();
79
+ printWarning("You are already authenticated.");
80
+ console.log(
81
+ `Use ${chalk.cyan("specforge login --force")} to re-authenticate with a different API key.`
82
+ );
83
+ printBlank();
84
+ return;
85
+ }
86
+ printBlank();
87
+ printBanner("figlet");
88
+ printBlank();
89
+ let apiKey;
90
+ if (options.apiKey) {
91
+ apiKey = options.apiKey;
92
+ const formatValidation = validateApiKeyFormat(apiKey);
93
+ if (!formatValidation.valid) {
94
+ throw new ValidationError(
95
+ formatValidation.error || "Invalid API key format",
96
+ 'API key should start with "sf_live_" and be at least 28 characters'
97
+ );
98
+ }
99
+ } else {
100
+ console.log(chalk.gray("Get your API key from: ") + chalk.cyan.underline("https://specforge.com.br/settings/api-keys"));
101
+ printBlank();
102
+ apiKey = await promptForApiKey();
103
+ }
104
+ const spinner = ora({
105
+ text: "Validating API key...",
106
+ color: "cyan"
107
+ }).start();
108
+ try {
109
+ const userInfo = await validateApiKeyWithBackend(apiKey);
110
+ spinner.succeed(`Authenticated as ${chalk.cyan(userInfo.email)}`);
111
+ saveGlobalConfig({ apiKey });
112
+ printBlank();
113
+ printSuccess("API key saved to ~/.specforge/config.json");
114
+ printBlank();
115
+ console.log(chalk.bold("Next steps:"));
116
+ printHint("specforge init", "initialize SpecForge in your project");
117
+ printHint("specforge status", "check your current configuration");
118
+ printBlank();
119
+ } catch (error) {
120
+ spinner.fail("Authentication failed");
121
+ throw error;
122
+ }
123
+ }
124
+ function registerLoginCommand(program) {
125
+ program.command("login").description("Authenticate with SpecForge API").option("--api-key <key>", "Provide API key non-interactively (for CI/scripts)").option("-f, --force", "Overwrite existing authentication").addHelpText("after", `
126
+ Examples:
127
+ $ specforge login # Interactive login (recommended)
128
+ $ specforge login --api-key <key> # Non-interactive login
129
+ $ specforge login --force # Re-authenticate with new key
130
+
131
+ Get your API key:
132
+ https://specforge.com.br/settings/api-keys
133
+ `).action(withErrorHandler(loginAction));
134
+ }
135
+ export {
136
+ loginAction,
137
+ registerLoginCommand
138
+ };
139
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/commands/login.ts"],"sourcesContent":["/**\n * Login Command\n *\n * Authenticates users with their SpecForge API key.\n * Supports both interactive (masked input) and non-interactive (--api-key flag) modes.\n */\n\nimport { Command } from 'commander';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { saveGlobalConfig } from '../config/writer.js';\nimport { loadGlobalConfig } from '../config/loader.js';\nimport { validateApiKeyFormat } from '../middleware/auth-guard.js';\nimport { withErrorHandler, NetworkError, ValidationError } from '../middleware/error-handler.js';\nimport { printSuccess, printWarning, printHint, printBlank } from '../ui/output.js';\nimport { printBanner } from '../ui/banner.js';\nimport { createCliClient } from '../../client/cli-client.js';\nimport { CONFIG_DEFAULTS } from '../config/types.js';\n\n/**\n * Login command options\n */\nexport interface LoginOptions {\n /** API key provided via --api-key flag */\n apiKey?: string;\n /** Force re-authentication even if already logged in */\n force?: boolean;\n}\n\n/**\n * User information returned from API validation\n */\nexport interface UserInfo {\n /** User's email address */\n email: string;\n /** User's display name */\n name?: string;\n /** User ID */\n userId?: string;\n}\n\n/**\n * Prompt user for API key with masked input\n *\n * @returns Promise resolving to the entered API key\n */\nasync function promptForApiKey(): Promise<string> {\n const answers = await inquirer.prompt([\n {\n type: 'password',\n name: 'apiKey',\n message: 'Enter your API key:',\n mask: '*',\n validate: (input: string) => {\n if (!input || input.trim() === '') {\n return 'API key is required';\n }\n\n const validation = validateApiKeyFormat(input);\n if (!validation.valid) {\n return validation.error || 'Invalid API key format';\n }\n\n return true;\n },\n },\n ]);\n\n return answers.apiKey;\n}\n\n/**\n * Validate API key against the backend and retrieve user info\n *\n * @param apiKey - API key to validate\n * @returns Promise resolving to user information\n * @throws NetworkError if connection fails\n * @throws ValidationError if API key is invalid\n */\nasync function validateApiKeyWithBackend(apiKey: string): Promise<UserInfo> {\n // Validate by making one token-authorized `/cli` call: a valid key resolves\n // the current user; an invalid/revoked/expired key fails at the authorizer\n // (401/403). Onboarding login telemetry (LOGIN_DETECTED / LOGIN_ATTEMPTED_INVALID)\n // is deferred to M12 — a stream-driven onboarding sibling + an explicit login\n // event via platform→operations — and is NOT emitted here.\n const client = createCliClient({\n apiKey,\n apiUrl: process.env.SPECFORGE_API_URL || CONFIG_DEFAULTS.apiUrl,\n debug: process.env.SPECFORGE_DEBUG === 'true',\n });\n\n try {\n const user = await client.call<{\n userId?: string;\n id?: string;\n email?: string;\n name?: string;\n }>('get_current_user', {});\n\n return {\n email: user.email || 'user',\n name: user.name,\n userId: user.userId ?? user.id,\n };\n } catch (error) {\n // Handle specific error types\n if (error instanceof ValidationError) {\n throw error;\n }\n\n const message = error instanceof Error ? error.message : String(error);\n\n // Network errors\n if (message.includes('ENOTFOUND') || message.includes('ECONNREFUSED') || message.includes('fetch failed')) {\n throw new NetworkError(\n 'Could not connect to SpecForge API',\n 'Check your internet connection and try again'\n );\n }\n\n // Authentication errors — invalid / revoked / expired key\n if (message.includes('401') || message.includes('Unauthorized') ||\n message.includes('403') || message.includes('Forbidden')) {\n throw new ValidationError(\n 'Invalid API key',\n 'Get a valid API key from https://specforge.com.br/settings/api-keys'\n );\n }\n\n // Rate limiting\n if (message.includes('429') || message.includes('rate limit')) {\n throw new NetworkError(\n 'Rate limit exceeded',\n 'Wait a moment and try again'\n );\n }\n\n // Unknown errors\n throw new NetworkError(\n `API error: ${message}`,\n 'If the problem persists, check https://status.specforge.com'\n );\n }\n}\n\n/**\n * Login command implementation\n *\n * @param options - Command options\n */\nexport async function loginAction(options: LoginOptions): Promise<void> {\n const existingConfig = loadGlobalConfig();\n\n // Check if already logged in\n if (existingConfig.apiKey && !options.force) {\n printBlank();\n printWarning('You are already authenticated.');\n console.log(\n `Use ${chalk.cyan('specforge login --force')} to re-authenticate with a different API key.`\n );\n printBlank();\n return;\n }\n\n // Header\n printBlank();\n printBanner('figlet');\n printBlank();\n\n // Get API key\n let apiKey: string;\n\n if (options.apiKey) {\n // Non-interactive mode\n apiKey = options.apiKey;\n\n // Validate format first\n const formatValidation = validateApiKeyFormat(apiKey);\n if (!formatValidation.valid) {\n throw new ValidationError(\n formatValidation.error || 'Invalid API key format',\n 'API key should start with \"sf_live_\" and be at least 28 characters'\n );\n }\n } else {\n // Interactive mode - prompt for API key\n console.log(chalk.gray('Get your API key from: ') + chalk.cyan.underline('https://specforge.com.br/settings/api-keys'));\n printBlank();\n\n apiKey = await promptForApiKey();\n }\n\n // Validate against backend\n const spinner = ora({\n text: 'Validating API key...',\n color: 'cyan',\n }).start();\n\n try {\n const userInfo = await validateApiKeyWithBackend(apiKey);\n\n spinner.succeed(`Authenticated as ${chalk.cyan(userInfo.email)}`);\n\n // Save to global config\n saveGlobalConfig({ apiKey });\n\n printBlank();\n printSuccess('API key saved to ~/.specforge/config.json');\n printBlank();\n\n // Next steps\n console.log(chalk.bold('Next steps:'));\n printHint('specforge init', 'initialize SpecForge in your project');\n printHint('specforge status', 'check your current configuration');\n printBlank();\n } catch (error) {\n spinner.fail('Authentication failed');\n throw error;\n }\n}\n\n/**\n * Register login command with Commander\n *\n * @param program - Commander program instance\n */\nexport function registerLoginCommand(program: Command): void {\n program\n .command('login')\n .description('Authenticate with SpecForge API')\n .option('--api-key <key>', 'Provide API key non-interactively (for CI/scripts)')\n .option('-f, --force', 'Overwrite existing authentication')\n .addHelpText('after', `\nExamples:\n $ specforge login # Interactive login (recommended)\n $ specforge login --api-key <key> # Non-interactive login\n $ specforge login --force # Re-authenticate with new key\n\nGet your API key:\n https://specforge.com.br/settings/api-keys\n`)\n .action(withErrorHandler(loginAction));\n}\n"],"mappings":"AAQA,OAAO,cAAc;AACrB,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AACjC,SAAS,4BAA4B;AACrC,SAAS,kBAAkB,cAAc,uBAAuB;AAChE,SAAS,cAAc,cAAc,WAAW,kBAAkB;AAClE,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AA6BhC,eAAe,kBAAmC;AAChD,QAAM,UAAU,MAAM,SAAS,OAAO;AAAA,IACpC;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU,CAAC,UAAkB;AAC3B,YAAI,CAAC,SAAS,MAAM,KAAK,MAAM,IAAI;AACjC,iBAAO;AAAA,QACT;AAEA,cAAM,aAAa,qBAAqB,KAAK;AAC7C,YAAI,CAAC,WAAW,OAAO;AACrB,iBAAO,WAAW,SAAS;AAAA,QAC7B;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,QAAQ;AACjB;AAUA,eAAe,0BAA0B,QAAmC;AAM1E,QAAM,SAAS,gBAAgB;AAAA,IAC7B;AAAA,IACA,QAAQ,QAAQ,IAAI,qBAAqB,gBAAgB;AAAA,IACzD,OAAO,QAAQ,IAAI,oBAAoB;AAAA,EACzC,CAAC;AAED,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAKvB,oBAAoB,CAAC,CAAC;AAEzB,WAAO;AAAA,MACL,OAAO,KAAK,SAAS;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK,UAAU,KAAK;AAAA,IAC9B;AAAA,EACF,SAAS,OAAO;AAEd,QAAI,iBAAiB,iBAAiB;AACpC,YAAM;AAAA,IACR;AAEA,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAGrE,QAAI,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,cAAc,KAAK,QAAQ,SAAS,cAAc,GAAG;AACzG,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,cAAc,KAC1D,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG;AAC5D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,YAAY,GAAG;AAC7D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,IAAI;AAAA,MACR,cAAc,OAAO;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAOA,eAAsB,YAAY,SAAsC;AACtE,QAAM,iBAAiB,iBAAiB;AAGxC,MAAI,eAAe,UAAU,CAAC,QAAQ,OAAO;AAC3C,eAAW;AACX,iBAAa,gCAAgC;AAC7C,YAAQ;AAAA,MACN,OAAO,MAAM,KAAK,yBAAyB,CAAC;AAAA,IAC9C;AACA,eAAW;AACX;AAAA,EACF;AAGA,aAAW;AACX,cAAY,QAAQ;AACpB,aAAW;AAGX,MAAI;AAEJ,MAAI,QAAQ,QAAQ;AAElB,aAAS,QAAQ;AAGjB,UAAM,mBAAmB,qBAAqB,MAAM;AACpD,QAAI,CAAC,iBAAiB,OAAO;AAC3B,YAAM,IAAI;AAAA,QACR,iBAAiB,SAAS;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AAEL,YAAQ,IAAI,MAAM,KAAK,yBAAyB,IAAI,MAAM,KAAK,UAAU,4CAA4C,CAAC;AACtH,eAAW;AAEX,aAAS,MAAM,gBAAgB;AAAA,EACjC;AAGA,QAAM,UAAU,IAAI;AAAA,IAClB,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC,EAAE,MAAM;AAET,MAAI;AACF,UAAM,WAAW,MAAM,0BAA0B,MAAM;AAEvD,YAAQ,QAAQ,oBAAoB,MAAM,KAAK,SAAS,KAAK,CAAC,EAAE;AAGhE,qBAAiB,EAAE,OAAO,CAAC;AAE3B,eAAW;AACX,iBAAa,2CAA2C;AACxD,eAAW;AAGX,YAAQ,IAAI,MAAM,KAAK,aAAa,CAAC;AACrC,cAAU,kBAAkB,sCAAsC;AAClE,cAAU,oBAAoB,kCAAkC;AAChE,eAAW;AAAA,EACb,SAAS,OAAO;AACd,YAAQ,KAAK,uBAAuB;AACpC,UAAM;AAAA,EACR;AACF;AAOO,SAAS,qBAAqB,SAAwB;AAC3D,UACG,QAAQ,OAAO,EACf,YAAY,iCAAiC,EAC7C,OAAO,mBAAmB,oDAAoD,EAC9E,OAAO,eAAe,mCAAmC,EACzD,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAQzB,EACI,OAAO,iBAAiB,WAAW,CAAC;AACzC;","names":[]}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Members Command
3
+ *
4
+ * Manage project members - list, add, remove, and update roles.
5
+ */
6
+ import { Command } from 'commander';
7
+ /**
8
+ * Options for members command
9
+ */
10
+ interface MembersOptions {
11
+ project?: string;
12
+ json?: boolean;
13
+ }
14
+ /**
15
+ * Options for members add command
16
+ */
17
+ interface MembersAddOptions {
18
+ project?: string;
19
+ role?: 'editor' | 'viewer';
20
+ message?: string;
21
+ }
22
+ /**
23
+ * Options for members remove command
24
+ */
25
+ interface MembersRemoveOptions {
26
+ project?: string;
27
+ }
28
+ /**
29
+ * Options for members role command
30
+ */
31
+ interface MembersRoleOptions {
32
+ project?: string;
33
+ }
34
+ /**
35
+ * List members action handler
36
+ */
37
+ export declare function membersListAction(options: MembersOptions): Promise<void>;
38
+ /**
39
+ * Add member action handler
40
+ */
41
+ export declare function membersAddAction(email: string, options: MembersAddOptions): Promise<void>;
42
+ /**
43
+ * Remove member action handler
44
+ */
45
+ export declare function membersRemoveAction(emailOrId: string, options: MembersRemoveOptions): Promise<void>;
46
+ /**
47
+ * Update member role action handler
48
+ */
49
+ export declare function membersRoleAction(emailOrId: string, role: string, options: MembersRoleOptions): Promise<void>;
50
+ /**
51
+ * Register members command with Commander
52
+ */
53
+ export declare function registerMembersCommand(program: Command): void;
54
+ export {};
55
+ //# sourceMappingURL=members.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/members.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkDpC;;GAEG;AACH,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkGD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsD9E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqDf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwDf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqEf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8B7D"}
@@ -0,0 +1,296 @@
1
+ import ora from "ora";
2
+ import Table from "cli-table3";
3
+ import { resolveConfig } from "../config/index.js";
4
+ import { withErrorHandler, CliError, NetworkError } from "../middleware/error-handler.js";
5
+ import { printBlank, printHeader } from "../ui/output.js";
6
+ import { colors } from "../ui/colors.js";
7
+ import { createCliClient } from "../../client/cli-client.js";
8
+ function getRoleBadge(role) {
9
+ switch (role) {
10
+ case "owner":
11
+ return colors.warning("owner");
12
+ case "editor":
13
+ return colors.primary("editor");
14
+ case "viewer":
15
+ return colors.muted("viewer");
16
+ default:
17
+ return role;
18
+ }
19
+ }
20
+ function getStatusBadge(status) {
21
+ switch (status) {
22
+ case "accepted":
23
+ return colors.success("active");
24
+ case "pending":
25
+ return colors.warning("pending");
26
+ case "declined":
27
+ return colors.error("declined");
28
+ default:
29
+ return status;
30
+ }
31
+ }
32
+ function displayMembersTable(response) {
33
+ printBlank();
34
+ printHeader(`Members - ${response.projectName}`);
35
+ printBlank();
36
+ const table = new Table({
37
+ head: [
38
+ colors.muted("EMAIL"),
39
+ colors.muted("ROLE"),
40
+ colors.muted("STATUS")
41
+ ],
42
+ style: { head: [], border: [] },
43
+ chars: {
44
+ "top": "",
45
+ "top-mid": "",
46
+ "top-left": "",
47
+ "top-right": "",
48
+ "bottom": "",
49
+ "bottom-mid": "",
50
+ "bottom-left": "",
51
+ "bottom-right": "",
52
+ "left": "",
53
+ "left-mid": "",
54
+ "mid": "",
55
+ "mid-mid": "",
56
+ "right": "",
57
+ "right-mid": "",
58
+ "middle": " "
59
+ }
60
+ });
61
+ table.push([
62
+ response.owner.email,
63
+ getRoleBadge("owner"),
64
+ getStatusBadge("accepted")
65
+ ]);
66
+ for (const member of response.members.filter((m) => m.status !== "declined")) {
67
+ table.push([
68
+ member.email,
69
+ getRoleBadge(member.role),
70
+ getStatusBadge(member.status)
71
+ ]);
72
+ }
73
+ for (const inv of response.pendingInvitations) {
74
+ table.push([
75
+ inv.email,
76
+ getRoleBadge(inv.role),
77
+ colors.warning("invited")
78
+ ]);
79
+ }
80
+ console.log(table.toString());
81
+ printBlank();
82
+ console.log(colors.muted(`Total: ${response.totalMembers} member(s)`));
83
+ printBlank();
84
+ }
85
+ function displayEmptyState() {
86
+ printBlank();
87
+ console.log(colors.muted("No additional members."));
88
+ console.log(colors.muted("Use `specforge members add <email>` to invite someone."));
89
+ printBlank();
90
+ }
91
+ async function membersListAction(options) {
92
+ const config = resolveConfig();
93
+ if (!config.apiKey) {
94
+ throw new CliError(
95
+ "Not authenticated",
96
+ 1,
97
+ "Run `specforge login` to authenticate first"
98
+ );
99
+ }
100
+ const projectId = options.project || config.projectId;
101
+ if (!projectId) {
102
+ throw new CliError(
103
+ "No project specified",
104
+ 1,
105
+ "Use --project <id> or run `specforge init` to set a default project"
106
+ );
107
+ }
108
+ const spinner = ora({
109
+ text: "Fetching members...",
110
+ color: "cyan"
111
+ }).start();
112
+ try {
113
+ const client = createCliClient({
114
+ apiKey: config.apiKey,
115
+ apiUrl: config.apiUrl,
116
+ debug: config.debug
117
+ });
118
+ const response = await client.call("list_project_members", {
119
+ projectId
120
+ });
121
+ spinner.stop();
122
+ if (options.json) {
123
+ console.log(JSON.stringify(response, null, 2));
124
+ return;
125
+ }
126
+ if (response.members.length === 0 && response.pendingInvitations.length === 0) {
127
+ displayEmptyState();
128
+ return;
129
+ }
130
+ displayMembersTable(response);
131
+ } catch (error) {
132
+ spinner.fail("Failed to fetch members");
133
+ const message = error instanceof Error ? error.message : "Unknown error";
134
+ throw new NetworkError(`Failed to fetch members: ${message}`);
135
+ }
136
+ }
137
+ async function membersAddAction(email, options) {
138
+ const config = resolveConfig();
139
+ if (!config.apiKey) {
140
+ throw new CliError(
141
+ "Not authenticated",
142
+ 1,
143
+ "Run `specforge login` to authenticate first"
144
+ );
145
+ }
146
+ const projectId = options.project || config.projectId;
147
+ if (!projectId) {
148
+ throw new CliError(
149
+ "No project specified",
150
+ 1,
151
+ "Use --project <id> or run `specforge init` to set a default project"
152
+ );
153
+ }
154
+ const spinner = ora({
155
+ text: `Inviting ${email}...`,
156
+ color: "cyan"
157
+ }).start();
158
+ try {
159
+ const client = createCliClient({
160
+ apiKey: config.apiKey,
161
+ apiUrl: config.apiUrl,
162
+ debug: config.debug
163
+ });
164
+ const response = await client.call("invite_to_project", {
165
+ projectId,
166
+ email,
167
+ role: options.role || "editor",
168
+ message: options.message
169
+ });
170
+ spinner.succeed(`Invited ${email} as ${response.role}`);
171
+ printBlank();
172
+ console.log(colors.muted(response.message));
173
+ printBlank();
174
+ } catch (error) {
175
+ spinner.fail(`Failed to invite ${email}`);
176
+ const message = error instanceof Error ? error.message : "Unknown error";
177
+ throw new NetworkError(`Failed to invite: ${message}`);
178
+ }
179
+ }
180
+ async function membersRemoveAction(emailOrId, options) {
181
+ const config = resolveConfig();
182
+ if (!config.apiKey) {
183
+ throw new CliError(
184
+ "Not authenticated",
185
+ 1,
186
+ "Run `specforge login` to authenticate first"
187
+ );
188
+ }
189
+ const projectId = options.project || config.projectId;
190
+ if (!projectId) {
191
+ throw new CliError(
192
+ "No project specified",
193
+ 1,
194
+ "Use --project <id> or run `specforge init` to set a default project"
195
+ );
196
+ }
197
+ const spinner = ora({
198
+ text: `Removing ${emailOrId}...`,
199
+ color: "cyan"
200
+ }).start();
201
+ try {
202
+ const client = createCliClient({
203
+ apiKey: config.apiKey,
204
+ apiUrl: config.apiUrl,
205
+ debug: config.debug
206
+ });
207
+ const args = { projectId };
208
+ if (emailOrId.includes("@")) {
209
+ const members = await client.call("list_project_members", { projectId });
210
+ const member = members.members.find((m) => m.email.toLowerCase() === emailOrId.toLowerCase());
211
+ if (!member) {
212
+ throw new Error(`Member with email ${emailOrId} not found`);
213
+ }
214
+ args.memberId = member.id;
215
+ } else {
216
+ args.memberId = emailOrId;
217
+ }
218
+ const response = await client.call("remove_project_member", args);
219
+ spinner.succeed(response.message);
220
+ printBlank();
221
+ } catch (error) {
222
+ spinner.fail(`Failed to remove ${emailOrId}`);
223
+ const message = error instanceof Error ? error.message : "Unknown error";
224
+ throw new NetworkError(`Failed to remove member: ${message}`);
225
+ }
226
+ }
227
+ async function membersRoleAction(emailOrId, role, options) {
228
+ const config = resolveConfig();
229
+ if (!config.apiKey) {
230
+ throw new CliError(
231
+ "Not authenticated",
232
+ 1,
233
+ "Run `specforge login` to authenticate first"
234
+ );
235
+ }
236
+ if (role !== "editor" && role !== "viewer") {
237
+ throw new CliError(
238
+ "Invalid role",
239
+ 1,
240
+ 'Role must be "editor" or "viewer"'
241
+ );
242
+ }
243
+ const projectId = options.project || config.projectId;
244
+ if (!projectId) {
245
+ throw new CliError(
246
+ "No project specified",
247
+ 1,
248
+ "Use --project <id> or run `specforge init` to set a default project"
249
+ );
250
+ }
251
+ const spinner = ora({
252
+ text: `Updating role for ${emailOrId}...`,
253
+ color: "cyan"
254
+ }).start();
255
+ try {
256
+ const client = createCliClient({
257
+ apiKey: config.apiKey,
258
+ apiUrl: config.apiUrl,
259
+ debug: config.debug
260
+ });
261
+ let memberId = emailOrId;
262
+ if (emailOrId.includes("@")) {
263
+ const members = await client.call("list_project_members", { projectId });
264
+ const member = members.members.find((m) => m.email.toLowerCase() === emailOrId.toLowerCase());
265
+ if (!member) {
266
+ throw new Error(`Member with email ${emailOrId} not found`);
267
+ }
268
+ memberId = member.id;
269
+ }
270
+ const response = await client.call("update_member_role", {
271
+ projectId,
272
+ memberId,
273
+ role
274
+ });
275
+ spinner.succeed(response.message);
276
+ printBlank();
277
+ } catch (error) {
278
+ spinner.fail(`Failed to update role for ${emailOrId}`);
279
+ const message = error instanceof Error ? error.message : "Unknown error";
280
+ throw new NetworkError(`Failed to update role: ${message}`);
281
+ }
282
+ }
283
+ function registerMembersCommand(program) {
284
+ const membersCmd = program.command("members").description("Manage project members").option("--project <id>", "Project ID (uses current project if not specified)").option("--json", "Output as JSON").action(withErrorHandler(membersListAction));
285
+ membersCmd.command("add <email>").description("Invite a user to the project").option("--project <id>", "Project ID").option("--role <role>", "Role to assign (editor or viewer)", "editor").option("--message <msg>", "Optional invitation message").action(withErrorHandler(membersAddAction));
286
+ membersCmd.command("remove <email-or-id>").description("Remove a member from the project").option("--project <id>", "Project ID").action(withErrorHandler(membersRemoveAction));
287
+ membersCmd.command("role <email-or-id> <role>").description("Update a member's role (editor or viewer)").option("--project <id>", "Project ID").action(withErrorHandler(membersRoleAction));
288
+ }
289
+ export {
290
+ membersAddAction,
291
+ membersListAction,
292
+ membersRemoveAction,
293
+ membersRoleAction,
294
+ registerMembersCommand
295
+ };
296
+ //# sourceMappingURL=members.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/commands/members.ts"],"sourcesContent":["/**\n * Members Command\n *\n * Manage project members - list, add, remove, and update roles.\n */\n\nimport { Command } from 'commander';\nimport ora from 'ora';\nimport Table from 'cli-table3';\nimport { resolveConfig } from '../config/index.js';\nimport { withErrorHandler, CliError, NetworkError } from '../middleware/error-handler.js';\nimport { printBlank, printHeader } from '../ui/output.js';\nimport { colors } from '../ui/colors.js';\nimport { createCliClient } from '../../client/cli-client.js';\n\n/**\n * Member from API response\n */\ninterface Member {\n id: string;\n userId: string;\n email: string;\n name?: string;\n role: 'owner' | 'editor' | 'viewer';\n status: 'pending' | 'accepted' | 'declined';\n invitedAt?: string;\n acceptedAt?: string;\n}\n\n/**\n * Pending invitation from API response\n */\ninterface PendingInvitation {\n id: string;\n email: string;\n role: 'editor' | 'viewer';\n invitedAt: string;\n expiresAt: string;\n}\n\n/**\n * API response for list_project_members\n */\ninterface ListMembersResponse {\n projectId: string;\n projectName: string;\n owner: {\n userId: string;\n email: string;\n name?: string;\n };\n members: Member[];\n pendingInvitations: PendingInvitation[];\n totalMembers: number;\n}\n\n/**\n * Options for members command\n */\ninterface MembersOptions {\n project?: string;\n json?: boolean;\n}\n\n/**\n * Options for members add command\n */\ninterface MembersAddOptions {\n project?: string;\n role?: 'editor' | 'viewer';\n message?: string;\n}\n\n/**\n * Options for members remove command\n */\ninterface MembersRemoveOptions {\n project?: string;\n}\n\n/**\n * Options for members role command\n */\ninterface MembersRoleOptions {\n project?: string;\n}\n\n/**\n * Get role badge with color\n */\nfunction getRoleBadge(role: string): string {\n switch (role) {\n case 'owner':\n return colors.warning('owner');\n case 'editor':\n return colors.primary('editor');\n case 'viewer':\n return colors.muted('viewer');\n default:\n return role;\n }\n}\n\n/**\n * Get status badge with color\n */\nfunction getStatusBadge(status: string): string {\n switch (status) {\n case 'accepted':\n return colors.success('active');\n case 'pending':\n return colors.warning('pending');\n case 'declined':\n return colors.error('declined');\n default:\n return status;\n }\n}\n\n/**\n * Display members as table\n */\nfunction displayMembersTable(response: ListMembersResponse): void {\n printBlank();\n printHeader(`Members - ${response.projectName}`);\n printBlank();\n\n const table = new Table({\n head: [\n colors.muted('EMAIL'),\n colors.muted('ROLE'),\n colors.muted('STATUS'),\n ],\n style: { head: [], border: [] },\n chars: {\n 'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',\n 'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',\n 'left': '', 'left-mid': '', 'mid': '', 'mid-mid': '',\n 'right': '', 'right-mid': '', 'middle': ' ',\n },\n });\n\n // Add owner\n table.push([\n response.owner.email,\n getRoleBadge('owner'),\n getStatusBadge('accepted'),\n ]);\n\n // Add members\n for (const member of response.members.filter(m => m.status !== 'declined')) {\n table.push([\n member.email,\n getRoleBadge(member.role),\n getStatusBadge(member.status),\n ]);\n }\n\n // Add pending invitations\n for (const inv of response.pendingInvitations) {\n table.push([\n inv.email,\n getRoleBadge(inv.role),\n colors.warning('invited'),\n ]);\n }\n\n console.log(table.toString());\n printBlank();\n console.log(colors.muted(`Total: ${response.totalMembers} member(s)`));\n printBlank();\n}\n\n/**\n * Display empty state\n */\nfunction displayEmptyState(): void {\n printBlank();\n console.log(colors.muted('No additional members.'));\n console.log(colors.muted('Use `specforge members add <email>` to invite someone.'));\n printBlank();\n}\n\n/**\n * List members action handler\n */\nexport async function membersListAction(options: MembersOptions): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const projectId = options.project || config.projectId;\n if (!projectId) {\n throw new CliError(\n 'No project specified',\n 1,\n 'Use --project <id> or run `specforge init` to set a default project'\n );\n }\n\n const spinner = ora({\n text: 'Fetching members...',\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n const response = await client.call<ListMembersResponse>('list_project_members', {\n projectId,\n });\n\n spinner.stop();\n\n if (options.json) {\n console.log(JSON.stringify(response, null, 2));\n return;\n }\n\n if (response.members.length === 0 && response.pendingInvitations.length === 0) {\n displayEmptyState();\n return;\n }\n\n displayMembersTable(response);\n } catch (error) {\n spinner.fail('Failed to fetch members');\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to fetch members: ${message}`);\n }\n}\n\n/**\n * Add member action handler\n */\nexport async function membersAddAction(\n email: string,\n options: MembersAddOptions\n): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const projectId = options.project || config.projectId;\n if (!projectId) {\n throw new CliError(\n 'No project specified',\n 1,\n 'Use --project <id> or run `specforge init` to set a default project'\n );\n }\n\n const spinner = ora({\n text: `Inviting ${email}...`,\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n const response = await client.call<{\n success: boolean;\n email: string;\n role: string;\n message: string;\n }>('invite_to_project', {\n projectId,\n email,\n role: options.role || 'editor',\n message: options.message,\n });\n\n spinner.succeed(`Invited ${email} as ${response.role}`);\n printBlank();\n console.log(colors.muted(response.message));\n printBlank();\n } catch (error) {\n spinner.fail(`Failed to invite ${email}`);\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to invite: ${message}`);\n }\n}\n\n/**\n * Remove member action handler\n */\nexport async function membersRemoveAction(\n emailOrId: string,\n options: MembersRemoveOptions\n): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const projectId = options.project || config.projectId;\n if (!projectId) {\n throw new CliError(\n 'No project specified',\n 1,\n 'Use --project <id> or run `specforge init` to set a default project'\n );\n }\n\n const spinner = ora({\n text: `Removing ${emailOrId}...`,\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // Determine if it's an ID or needs lookup\n const args: Record<string, unknown> = { projectId };\n if (emailOrId.includes('@')) {\n // It's an email - we need to find the member first\n const members = await client.call<ListMembersResponse>('list_project_members', { projectId });\n const member = members.members.find(m => m.email.toLowerCase() === emailOrId.toLowerCase());\n if (!member) {\n throw new Error(`Member with email ${emailOrId} not found`);\n }\n args.memberId = member.id;\n } else {\n // Assume it's a member ID\n args.memberId = emailOrId;\n }\n\n const response = await client.call<{ success: boolean; message: string }>('remove_project_member', args);\n\n spinner.succeed(response.message);\n printBlank();\n } catch (error) {\n spinner.fail(`Failed to remove ${emailOrId}`);\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to remove member: ${message}`);\n }\n}\n\n/**\n * Update member role action handler\n */\nexport async function membersRoleAction(\n emailOrId: string,\n role: string,\n options: MembersRoleOptions\n): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n if (role !== 'editor' && role !== 'viewer') {\n throw new CliError(\n 'Invalid role',\n 1,\n 'Role must be \"editor\" or \"viewer\"'\n );\n }\n\n const projectId = options.project || config.projectId;\n if (!projectId) {\n throw new CliError(\n 'No project specified',\n 1,\n 'Use --project <id> or run `specforge init` to set a default project'\n );\n }\n\n const spinner = ora({\n text: `Updating role for ${emailOrId}...`,\n color: 'cyan',\n }).start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // Find member ID if email provided\n let memberId = emailOrId;\n if (emailOrId.includes('@')) {\n const members = await client.call<ListMembersResponse>('list_project_members', { projectId });\n const member = members.members.find(m => m.email.toLowerCase() === emailOrId.toLowerCase());\n if (!member) {\n throw new Error(`Member with email ${emailOrId} not found`);\n }\n memberId = member.id;\n }\n\n const response = await client.call<{\n success: boolean;\n previousRole: string;\n newRole: string;\n message: string;\n }>('update_member_role', {\n projectId,\n memberId,\n role,\n });\n\n spinner.succeed(response.message);\n printBlank();\n } catch (error) {\n spinner.fail(`Failed to update role for ${emailOrId}`);\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new NetworkError(`Failed to update role: ${message}`);\n }\n}\n\n/**\n * Register members command with Commander\n */\nexport function registerMembersCommand(program: Command): void {\n const membersCmd = program\n .command('members')\n .description('Manage project members')\n .option('--project <id>', 'Project ID (uses current project if not specified)')\n .option('--json', 'Output as JSON')\n .action(withErrorHandler(membersListAction));\n\n // Add subcommand\n membersCmd\n .command('add <email>')\n .description('Invite a user to the project')\n .option('--project <id>', 'Project ID')\n .option('--role <role>', 'Role to assign (editor or viewer)', 'editor')\n .option('--message <msg>', 'Optional invitation message')\n .action(withErrorHandler(membersAddAction));\n\n // Remove subcommand\n membersCmd\n .command('remove <email-or-id>')\n .description('Remove a member from the project')\n .option('--project <id>', 'Project ID')\n .action(withErrorHandler(membersRemoveAction));\n\n // Role subcommand\n membersCmd\n .command('role <email-or-id> <role>')\n .description('Update a member\\'s role (editor or viewer)')\n .option('--project <id>', 'Project ID')\n .action(withErrorHandler(membersRoleAction));\n}\n"],"mappings":"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","names":[]}