@garygentry/feature-forge 0.2.13 → 0.3.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 (437) hide show
  1. package/README.md +6 -3
  2. package/adapters/GENERATION-REPORT.md +20 -0
  3. package/adapters/claude/.feature-forge-bundle.json +1 -1
  4. package/adapters/claude/references/epic-manifest-schema.json +5 -0
  5. package/adapters/claude/references/forge-config-schema.json +2 -2
  6. package/adapters/claude/references/shared-conventions.md +12 -0
  7. package/adapters/claude/references/stacks/_generic.md +21 -0
  8. package/adapters/claude/references/stacks/go.md +19 -0
  9. package/adapters/claude/references/stacks/python.md +21 -0
  10. package/adapters/claude/references/stacks/rust.md +19 -0
  11. package/adapters/claude/references/stacks/typescript.md +23 -0
  12. package/adapters/claude/scripts/epic-manifest.py +75 -5
  13. package/adapters/claude/scripts/forge-root.sh +102 -14
  14. package/adapters/claude/scripts/forge-session.py +68 -9
  15. package/adapters/claude/skills/forge/references/shared-conventions.md +12 -0
  16. package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +12 -0
  17. package/adapters/claude/skills/forge-1-prd/SKILL.md +2 -0
  18. package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +12 -0
  19. package/adapters/claude/skills/forge-2-tech/SKILL.md +2 -0
  20. package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +12 -0
  21. package/adapters/claude/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  22. package/adapters/claude/skills/forge-2-tech/references/stacks/go.md +19 -0
  23. package/adapters/claude/skills/forge-2-tech/references/stacks/python.md +21 -0
  24. package/adapters/claude/skills/forge-2-tech/references/stacks/rust.md +19 -0
  25. package/adapters/claude/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  26. package/adapters/claude/skills/forge-3-specs/SKILL.md +2 -0
  27. package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +12 -0
  28. package/adapters/claude/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  29. package/adapters/claude/skills/forge-3-specs/references/stacks/go.md +19 -0
  30. package/adapters/claude/skills/forge-3-specs/references/stacks/python.md +21 -0
  31. package/adapters/claude/skills/forge-3-specs/references/stacks/rust.md +19 -0
  32. package/adapters/claude/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  33. package/adapters/claude/skills/forge-4-backlog/SKILL.md +5 -0
  34. package/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  35. package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  36. package/adapters/claude/skills/forge-5-loop/SKILL.md +7 -9
  37. package/adapters/claude/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  38. package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +41 -0
  39. package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +12 -0
  40. package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +12 -0
  41. package/adapters/claude/skills/forge-fix/references/shared-conventions.md +12 -0
  42. package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
  43. package/adapters/claude/skills/forge-guide/references/shared-conventions.md +12 -0
  44. package/adapters/claude/skills/forge-guide/references/stacks/_generic.md +21 -0
  45. package/adapters/claude/skills/forge-guide/references/stacks/go.md +19 -0
  46. package/adapters/claude/skills/forge-guide/references/stacks/python.md +21 -0
  47. package/adapters/claude/skills/forge-guide/references/stacks/rust.md +19 -0
  48. package/adapters/claude/skills/forge-guide/references/stacks/typescript.md +23 -0
  49. package/adapters/claude/skills/forge-verify/SKILL.md +3 -3
  50. package/adapters/claude/skills/forge-verify/references/shared-conventions.md +12 -0
  51. package/adapters/claude/skills/forge-verify/references/verification-checklists.md +82 -3
  52. package/adapters/codex/.feature-forge-bundle.json +1 -1
  53. package/adapters/codex/references/epic-manifest-schema.json +5 -0
  54. package/adapters/codex/references/forge-config-schema.json +2 -2
  55. package/adapters/codex/references/shared-conventions.md +12 -0
  56. package/adapters/codex/references/stacks/_generic.md +21 -0
  57. package/adapters/codex/references/stacks/go.md +19 -0
  58. package/adapters/codex/references/stacks/python.md +21 -0
  59. package/adapters/codex/references/stacks/rust.md +19 -0
  60. package/adapters/codex/references/stacks/typescript.md +23 -0
  61. package/adapters/codex/scripts/epic-manifest.py +75 -5
  62. package/adapters/codex/scripts/forge-root.sh +102 -14
  63. package/adapters/codex/scripts/forge-session.py +68 -9
  64. package/adapters/codex/skills/forge/references/shared-conventions.md +12 -0
  65. package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +12 -0
  66. package/adapters/codex/skills/forge-1-prd/SKILL.md +2 -0
  67. package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +12 -0
  68. package/adapters/codex/skills/forge-2-tech/SKILL.md +2 -0
  69. package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +12 -0
  70. package/adapters/codex/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  71. package/adapters/codex/skills/forge-2-tech/references/stacks/go.md +19 -0
  72. package/adapters/codex/skills/forge-2-tech/references/stacks/python.md +21 -0
  73. package/adapters/codex/skills/forge-2-tech/references/stacks/rust.md +19 -0
  74. package/adapters/codex/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  75. package/adapters/codex/skills/forge-3-specs/SKILL.md +2 -0
  76. package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +12 -0
  77. package/adapters/codex/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  78. package/adapters/codex/skills/forge-3-specs/references/stacks/go.md +19 -0
  79. package/adapters/codex/skills/forge-3-specs/references/stacks/python.md +21 -0
  80. package/adapters/codex/skills/forge-3-specs/references/stacks/rust.md +19 -0
  81. package/adapters/codex/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  82. package/adapters/codex/skills/forge-4-backlog/SKILL.md +5 -0
  83. package/adapters/codex/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  84. package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  85. package/adapters/codex/skills/forge-5-loop/SKILL.md +7 -9
  86. package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  87. package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +41 -0
  88. package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +12 -0
  89. package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +12 -0
  90. package/adapters/codex/skills/forge-fix/references/shared-conventions.md +12 -0
  91. package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
  92. package/adapters/codex/skills/forge-guide/references/shared-conventions.md +12 -0
  93. package/adapters/codex/skills/forge-guide/references/stacks/_generic.md +21 -0
  94. package/adapters/codex/skills/forge-guide/references/stacks/go.md +19 -0
  95. package/adapters/codex/skills/forge-guide/references/stacks/python.md +21 -0
  96. package/adapters/codex/skills/forge-guide/references/stacks/rust.md +19 -0
  97. package/adapters/codex/skills/forge-guide/references/stacks/typescript.md +23 -0
  98. package/adapters/codex/skills/forge-verify/SKILL.md +3 -3
  99. package/adapters/codex/skills/forge-verify/references/shared-conventions.md +12 -0
  100. package/adapters/codex/skills/forge-verify/references/verification-checklists.md +82 -3
  101. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  102. package/adapters/copilot/references/epic-manifest-schema.json +5 -0
  103. package/adapters/copilot/references/forge-config-schema.json +2 -2
  104. package/adapters/copilot/references/shared-conventions.md +12 -0
  105. package/adapters/copilot/references/stacks/_generic.md +21 -0
  106. package/adapters/copilot/references/stacks/go.md +19 -0
  107. package/adapters/copilot/references/stacks/python.md +21 -0
  108. package/adapters/copilot/references/stacks/rust.md +19 -0
  109. package/adapters/copilot/references/stacks/typescript.md +23 -0
  110. package/adapters/copilot/scripts/epic-manifest.py +75 -5
  111. package/adapters/copilot/scripts/forge-root.sh +102 -14
  112. package/adapters/copilot/scripts/forge-session.py +68 -9
  113. package/adapters/copilot/skills/forge/references/shared-conventions.md +12 -0
  114. package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +12 -0
  115. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +2 -0
  116. package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +12 -0
  117. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +2 -0
  118. package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +12 -0
  119. package/adapters/copilot/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  120. package/adapters/copilot/skills/forge-2-tech/references/stacks/go.md +19 -0
  121. package/adapters/copilot/skills/forge-2-tech/references/stacks/python.md +21 -0
  122. package/adapters/copilot/skills/forge-2-tech/references/stacks/rust.md +19 -0
  123. package/adapters/copilot/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  124. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +2 -0
  125. package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +12 -0
  126. package/adapters/copilot/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  127. package/adapters/copilot/skills/forge-3-specs/references/stacks/go.md +19 -0
  128. package/adapters/copilot/skills/forge-3-specs/references/stacks/python.md +21 -0
  129. package/adapters/copilot/skills/forge-3-specs/references/stacks/rust.md +19 -0
  130. package/adapters/copilot/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  131. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +5 -0
  132. package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  133. package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  134. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +7 -9
  135. package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  136. package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +41 -0
  137. package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +12 -0
  138. package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +12 -0
  139. package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +12 -0
  140. package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
  141. package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +12 -0
  142. package/adapters/copilot/skills/forge-guide/references/stacks/_generic.md +21 -0
  143. package/adapters/copilot/skills/forge-guide/references/stacks/go.md +19 -0
  144. package/adapters/copilot/skills/forge-guide/references/stacks/python.md +21 -0
  145. package/adapters/copilot/skills/forge-guide/references/stacks/rust.md +19 -0
  146. package/adapters/copilot/skills/forge-guide/references/stacks/typescript.md +23 -0
  147. package/adapters/copilot/skills/forge-verify/forge-verify.md +3 -3
  148. package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +12 -0
  149. package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +82 -3
  150. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  151. package/adapters/cursor/references/epic-manifest-schema.json +5 -0
  152. package/adapters/cursor/references/forge-config-schema.json +2 -2
  153. package/adapters/cursor/references/shared-conventions.md +12 -0
  154. package/adapters/cursor/references/stacks/_generic.md +21 -0
  155. package/adapters/cursor/references/stacks/go.md +19 -0
  156. package/adapters/cursor/references/stacks/python.md +21 -0
  157. package/adapters/cursor/references/stacks/rust.md +19 -0
  158. package/adapters/cursor/references/stacks/typescript.md +23 -0
  159. package/adapters/cursor/scripts/epic-manifest.py +75 -5
  160. package/adapters/cursor/scripts/forge-root.sh +102 -14
  161. package/adapters/cursor/scripts/forge-session.py +68 -9
  162. package/adapters/cursor/skills/forge/references/shared-conventions.md +12 -0
  163. package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +12 -0
  164. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +2 -0
  165. package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +12 -0
  166. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +2 -0
  167. package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +12 -0
  168. package/adapters/cursor/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  169. package/adapters/cursor/skills/forge-2-tech/references/stacks/go.md +19 -0
  170. package/adapters/cursor/skills/forge-2-tech/references/stacks/python.md +21 -0
  171. package/adapters/cursor/skills/forge-2-tech/references/stacks/rust.md +19 -0
  172. package/adapters/cursor/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  173. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +2 -0
  174. package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +12 -0
  175. package/adapters/cursor/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  176. package/adapters/cursor/skills/forge-3-specs/references/stacks/go.md +19 -0
  177. package/adapters/cursor/skills/forge-3-specs/references/stacks/python.md +21 -0
  178. package/adapters/cursor/skills/forge-3-specs/references/stacks/rust.md +19 -0
  179. package/adapters/cursor/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  180. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +5 -0
  181. package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  182. package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  183. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +7 -9
  184. package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  185. package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +41 -0
  186. package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +12 -0
  187. package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +12 -0
  188. package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +12 -0
  189. package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
  190. package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +12 -0
  191. package/adapters/cursor/skills/forge-guide/references/stacks/_generic.md +21 -0
  192. package/adapters/cursor/skills/forge-guide/references/stacks/go.md +19 -0
  193. package/adapters/cursor/skills/forge-guide/references/stacks/python.md +21 -0
  194. package/adapters/cursor/skills/forge-guide/references/stacks/rust.md +19 -0
  195. package/adapters/cursor/skills/forge-guide/references/stacks/typescript.md +23 -0
  196. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +3 -3
  197. package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +12 -0
  198. package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +82 -3
  199. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  200. package/adapters/gemini/gemini-extension.json +1 -1
  201. package/adapters/gemini/references/epic-manifest-schema.json +5 -0
  202. package/adapters/gemini/references/forge-config-schema.json +2 -2
  203. package/adapters/gemini/references/shared-conventions.md +12 -0
  204. package/adapters/gemini/references/stacks/_generic.md +21 -0
  205. package/adapters/gemini/references/stacks/go.md +19 -0
  206. package/adapters/gemini/references/stacks/python.md +21 -0
  207. package/adapters/gemini/references/stacks/rust.md +19 -0
  208. package/adapters/gemini/references/stacks/typescript.md +23 -0
  209. package/adapters/gemini/scripts/epic-manifest.py +75 -5
  210. package/adapters/gemini/scripts/forge-root.sh +102 -14
  211. package/adapters/gemini/scripts/forge-session.py +68 -9
  212. package/adapters/gemini/skills/forge/references/shared-conventions.md +12 -0
  213. package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +12 -0
  214. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +2 -0
  215. package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +12 -0
  216. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +2 -0
  217. package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +12 -0
  218. package/adapters/gemini/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  219. package/adapters/gemini/skills/forge-2-tech/references/stacks/go.md +19 -0
  220. package/adapters/gemini/skills/forge-2-tech/references/stacks/python.md +21 -0
  221. package/adapters/gemini/skills/forge-2-tech/references/stacks/rust.md +19 -0
  222. package/adapters/gemini/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  223. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +2 -0
  224. package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +12 -0
  225. package/adapters/gemini/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  226. package/adapters/gemini/skills/forge-3-specs/references/stacks/go.md +19 -0
  227. package/adapters/gemini/skills/forge-3-specs/references/stacks/python.md +21 -0
  228. package/adapters/gemini/skills/forge-3-specs/references/stacks/rust.md +19 -0
  229. package/adapters/gemini/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  230. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +5 -0
  231. package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  232. package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  233. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +7 -9
  234. package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  235. package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +41 -0
  236. package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +12 -0
  237. package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +12 -0
  238. package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +12 -0
  239. package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
  240. package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +12 -0
  241. package/adapters/gemini/skills/forge-guide/references/stacks/_generic.md +21 -0
  242. package/adapters/gemini/skills/forge-guide/references/stacks/go.md +19 -0
  243. package/adapters/gemini/skills/forge-guide/references/stacks/python.md +21 -0
  244. package/adapters/gemini/skills/forge-guide/references/stacks/rust.md +19 -0
  245. package/adapters/gemini/skills/forge-guide/references/stacks/typescript.md +23 -0
  246. package/adapters/gemini/skills/forge-verify/forge-verify.md +3 -3
  247. package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +12 -0
  248. package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +82 -3
  249. package/adapters/pi/.feature-forge-bundle.json +6 -0
  250. package/adapters/pi/agents/forge-researcher.md +139 -0
  251. package/adapters/pi/agents/forge-spec-writer.md +116 -0
  252. package/adapters/pi/agents/forge-verifier.md +126 -0
  253. package/adapters/pi/extensions/ask-user-question/LICENSE +21 -0
  254. package/adapters/pi/extensions/ask-user-question/README.md +91 -0
  255. package/adapters/pi/extensions/ask-user-question/ask-user-question.ts +298 -0
  256. package/adapters/pi/extensions/ask-user-question/config.ts +78 -0
  257. package/adapters/pi/extensions/ask-user-question/events.ts +57 -0
  258. package/adapters/pi/extensions/ask-user-question/index.ts +61 -0
  259. package/adapters/pi/extensions/ask-user-question/locales/de.json +27 -0
  260. package/adapters/pi/extensions/ask-user-question/locales/en.json +27 -0
  261. package/adapters/pi/extensions/ask-user-question/locales/es.json +27 -0
  262. package/adapters/pi/extensions/ask-user-question/locales/fr.json +27 -0
  263. package/adapters/pi/extensions/ask-user-question/locales/pt-BR.json +27 -0
  264. package/adapters/pi/extensions/ask-user-question/locales/pt.json +27 -0
  265. package/adapters/pi/extensions/ask-user-question/locales/ru.json +27 -0
  266. package/adapters/pi/extensions/ask-user-question/locales/uk.json +27 -0
  267. package/adapters/pi/extensions/ask-user-question/locales/zh.json +29 -0
  268. package/adapters/pi/extensions/ask-user-question/reconcile.ts +49 -0
  269. package/adapters/pi/extensions/ask-user-question/rpc-fallback.ts +168 -0
  270. package/adapters/pi/extensions/ask-user-question/state/build-questionnaire.ts +302 -0
  271. package/adapters/pi/extensions/ask-user-question/state/i18n-bridge.ts +53 -0
  272. package/adapters/pi/extensions/ask-user-question/state/key-router.ts +277 -0
  273. package/adapters/pi/extensions/ask-user-question/state/questionnaire-session.ts +234 -0
  274. package/adapters/pi/extensions/ask-user-question/state/row-intent.ts +145 -0
  275. package/adapters/pi/extensions/ask-user-question/state/selectors/contract.ts +26 -0
  276. package/adapters/pi/extensions/ask-user-question/state/selectors/derivations.ts +42 -0
  277. package/adapters/pi/extensions/ask-user-question/state/selectors/focus.ts +19 -0
  278. package/adapters/pi/extensions/ask-user-question/state/selectors/projections.ts +101 -0
  279. package/adapters/pi/extensions/ask-user-question/state/state-reducer.ts +292 -0
  280. package/adapters/pi/extensions/ask-user-question/state/state.ts +55 -0
  281. package/adapters/pi/extensions/ask-user-question/tool/format-answer.ts +31 -0
  282. package/adapters/pi/extensions/ask-user-question/tool/response-envelope.ts +49 -0
  283. package/adapters/pi/extensions/ask-user-question/tool/types.ts +147 -0
  284. package/adapters/pi/extensions/ask-user-question/tool/validate-questionnaire.ts +58 -0
  285. package/adapters/pi/extensions/ask-user-question/vendor-config-shim.ts +65 -0
  286. package/adapters/pi/extensions/ask-user-question/view/component-binding.ts +47 -0
  287. package/adapters/pi/extensions/ask-user-question/view/components/inline-input.ts +98 -0
  288. package/adapters/pi/extensions/ask-user-question/view/components/multi-select-view.ts +193 -0
  289. package/adapters/pi/extensions/ask-user-question/view/components/option-list-view.ts +70 -0
  290. package/adapters/pi/extensions/ask-user-question/view/components/preview/markdown-content-cache.ts +79 -0
  291. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-block-renderer.ts +111 -0
  292. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-box-renderer.ts +88 -0
  293. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-layout-decider.ts +202 -0
  294. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-pane.ts +228 -0
  295. package/adapters/pi/extensions/ask-user-question/view/components/submit-picker.ts +67 -0
  296. package/adapters/pi/extensions/ask-user-question/view/components/tab-bar.ts +59 -0
  297. package/adapters/pi/extensions/ask-user-question/view/components/wrapping-select.ts +293 -0
  298. package/adapters/pi/extensions/ask-user-question/view/dialog-builder.ts +224 -0
  299. package/adapters/pi/extensions/ask-user-question/view/props-adapter.ts +125 -0
  300. package/adapters/pi/extensions/ask-user-question/view/stateful-view.ts +26 -0
  301. package/adapters/pi/extensions/ask-user-question/view/tab-components.ts +18 -0
  302. package/adapters/pi/extensions/ask-user-question/view/tab-content-strategy.ts +252 -0
  303. package/adapters/pi/package.json +26 -0
  304. package/adapters/pi/references/epic-manifest-schema.json +125 -0
  305. package/adapters/pi/references/forge-config-schema.json +236 -0
  306. package/adapters/pi/references/pipeline-state-schema.json +191 -0
  307. package/adapters/pi/references/portable-root.md +71 -0
  308. package/adapters/pi/references/process-overview.md +143 -0
  309. package/adapters/pi/references/ralph-loop-contract.md +221 -0
  310. package/adapters/pi/references/shared-conventions.md +295 -0
  311. package/adapters/pi/references/skill-frontmatter.schema.json +17 -0
  312. package/adapters/pi/references/stack-resolution.md +54 -0
  313. package/adapters/pi/references/stacks/_generic.md +111 -0
  314. package/adapters/pi/references/stacks/go.md +157 -0
  315. package/adapters/pi/references/stacks/python.md +184 -0
  316. package/adapters/pi/references/stacks/rust.md +170 -0
  317. package/adapters/pi/references/stacks/typescript.md +134 -0
  318. package/adapters/pi/references/stage-exit-protocol.md +258 -0
  319. package/adapters/pi/references/templates/specs-hygiene/AGENTS.md +32 -0
  320. package/adapters/pi/references/templates/specs-hygiene/CLAUDE.md +31 -0
  321. package/adapters/pi/references/vendor-construct-inventory.md +50 -0
  322. package/adapters/pi/scripts/epic-manifest.py +1694 -0
  323. package/adapters/pi/scripts/forge-bootstrap.py +1070 -0
  324. package/adapters/pi/scripts/forge-init.sh +58 -0
  325. package/adapters/pi/scripts/forge-root.sh +179 -0
  326. package/adapters/pi/scripts/forge-session.py +1888 -0
  327. package/adapters/pi/scripts/validate-traceability.py +150 -0
  328. package/adapters/pi/skills/forge/SKILL.md +243 -0
  329. package/adapters/pi/skills/forge/references/pipeline-state-schema.json +191 -0
  330. package/adapters/pi/skills/forge/references/process-overview.md +143 -0
  331. package/adapters/pi/skills/forge/references/shared-conventions.md +295 -0
  332. package/adapters/pi/skills/forge/references/stage-exit-protocol.md +258 -0
  333. package/adapters/pi/skills/forge-0-epic/SKILL.md +308 -0
  334. package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +266 -0
  335. package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +75 -0
  336. package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
  337. package/adapters/pi/skills/forge-0-epic/references/portable-root.md +71 -0
  338. package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +295 -0
  339. package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
  340. package/adapters/pi/skills/forge-1-prd/SKILL.md +164 -0
  341. package/adapters/pi/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
  342. package/adapters/pi/skills/forge-1-prd/references/prd-template.md +106 -0
  343. package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +295 -0
  344. package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
  345. package/adapters/pi/skills/forge-2-tech/SKILL.md +225 -0
  346. package/adapters/pi/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
  347. package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +295 -0
  348. package/adapters/pi/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  349. package/adapters/pi/skills/forge-2-tech/references/stack-resolution.md +54 -0
  350. package/adapters/pi/skills/forge-2-tech/references/stacks/_generic.md +111 -0
  351. package/adapters/pi/skills/forge-2-tech/references/stacks/go.md +157 -0
  352. package/adapters/pi/skills/forge-2-tech/references/stacks/python.md +184 -0
  353. package/adapters/pi/skills/forge-2-tech/references/stacks/rust.md +170 -0
  354. package/adapters/pi/skills/forge-2-tech/references/stacks/typescript.md +134 -0
  355. package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
  356. package/adapters/pi/skills/forge-3-specs/SKILL.md +178 -0
  357. package/adapters/pi/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
  358. package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +295 -0
  359. package/adapters/pi/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  360. package/adapters/pi/skills/forge-3-specs/references/spec-examples.md +71 -0
  361. package/adapters/pi/skills/forge-3-specs/references/stacks/_generic.md +111 -0
  362. package/adapters/pi/skills/forge-3-specs/references/stacks/go.md +157 -0
  363. package/adapters/pi/skills/forge-3-specs/references/stacks/python.md +184 -0
  364. package/adapters/pi/skills/forge-3-specs/references/stacks/rust.md +170 -0
  365. package/adapters/pi/skills/forge-3-specs/references/stacks/typescript.md +134 -0
  366. package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
  367. package/adapters/pi/skills/forge-4-backlog/SKILL.md +175 -0
  368. package/adapters/pi/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
  369. package/adapters/pi/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
  370. package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +295 -0
  371. package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
  372. package/adapters/pi/skills/forge-5-loop/SKILL.md +314 -0
  373. package/adapters/pi/skills/forge-5-loop/references/forge-config-schema.json +236 -0
  374. package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
  375. package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +85 -0
  376. package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +341 -0
  377. package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +295 -0
  378. package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
  379. package/adapters/pi/skills/forge-6-docs/SKILL.md +202 -0
  380. package/adapters/pi/skills/forge-6-docs/references/doc-conventions.md +126 -0
  381. package/adapters/pi/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
  382. package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +295 -0
  383. package/adapters/pi/skills/forge-bootstrap/SKILL.md +250 -0
  384. package/adapters/pi/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
  385. package/adapters/pi/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
  386. package/adapters/pi/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
  387. package/adapters/pi/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
  388. package/adapters/pi/skills/forge-bootstrap/references/templates/go/main.go +12 -0
  389. package/adapters/pi/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
  390. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +35 -0
  391. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +36 -0
  392. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
  393. package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
  394. package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
  395. package/adapters/pi/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
  396. package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
  397. package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
  398. package/adapters/pi/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
  399. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
  400. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
  401. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
  402. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
  403. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
  404. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
  405. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
  406. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
  407. package/adapters/pi/skills/forge-fix/SKILL.md +98 -0
  408. package/adapters/pi/skills/forge-fix/references/shared-conventions.md +295 -0
  409. package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +258 -0
  410. package/adapters/pi/skills/forge-guide/SKILL.md +192 -0
  411. package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +236 -0
  412. package/adapters/pi/skills/forge-guide/references/process-overview.md +143 -0
  413. package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +221 -0
  414. package/adapters/pi/skills/forge-guide/references/shared-conventions.md +295 -0
  415. package/adapters/pi/skills/forge-guide/references/stack-resolution.md +54 -0
  416. package/adapters/pi/skills/forge-guide/references/stacks/_generic.md +111 -0
  417. package/adapters/pi/skills/forge-guide/references/stacks/go.md +157 -0
  418. package/adapters/pi/skills/forge-guide/references/stacks/python.md +184 -0
  419. package/adapters/pi/skills/forge-guide/references/stacks/rust.md +170 -0
  420. package/adapters/pi/skills/forge-guide/references/stacks/typescript.md +134 -0
  421. package/adapters/pi/skills/forge-init/SKILL.md +72 -0
  422. package/adapters/pi/skills/forge-verify/SKILL.md +273 -0
  423. package/adapters/pi/skills/forge-verify/references/pipeline-state-schema.json +191 -0
  424. package/adapters/pi/skills/forge-verify/references/shared-conventions.md +295 -0
  425. package/adapters/pi/skills/forge-verify/references/verification-checklists.md +477 -0
  426. package/dist/agent-targets.d.ts +1 -1
  427. package/dist/agent-targets.js +23 -3
  428. package/dist/detect.d.ts +1 -1
  429. package/dist/detect.js +2 -1
  430. package/dist/manifest.d.ts +1 -1
  431. package/dist/manifest.js +2 -2
  432. package/dist/placements.js +5 -1
  433. package/dist/rauf.d.ts +4 -4
  434. package/dist/rauf.js +3 -3
  435. package/dist/types.d.ts +31 -6
  436. package/dist/types.js +6 -3
  437. package/package.json +14 -3
@@ -0,0 +1,27 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Введите что-нибудь.",
5
+ "sentinel.next": "Далее",
6
+
7
+ "submit.label": "Отправить ответы",
8
+ "submit.cancel": "Отмена",
9
+
10
+ "hint.enter": "Enter — выбрать",
11
+ "hint.navigate": "↑/↓ — навигация",
12
+ "hint.toggle": "Space — переключить",
13
+ "hint.notes": "n — добавить заметки",
14
+ "hint.tab": "Tab — следующий вопрос",
15
+ "hint.cancel": "Esc — отменить",
16
+
17
+ "review.heading": "Проверьте свои ответы",
18
+ "review.ready": "Готовы отправить ответы?",
19
+ "review.incomplete": "⚠ Ответьте на оставшиеся вопросы перед отправкой:",
20
+
21
+ "preview.no_preview": "Предпросмотр недоступен",
22
+ "preview.notes_affordance": "Заметки: нажмите n, чтобы добавить заметки",
23
+ "notes.header": "Заметки:",
24
+
25
+ "rpc.multi_instructions": "Введите номера всех подходящих вариантов через запятую (например, \"1,3\") или введите свой ответ текстом.",
26
+ "rpc.custom_answer_title": "Введите свой ответ:"
27
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Введіть щось.",
5
+ "sentinel.next": "Далі",
6
+
7
+ "submit.label": "Надіслати відповіді",
8
+ "submit.cancel": "Скасувати",
9
+
10
+ "hint.enter": "Enter — обрати",
11
+ "hint.navigate": "↑/↓ — навігація",
12
+ "hint.toggle": "Space — перемкнути",
13
+ "hint.notes": "n — додати нотатки",
14
+ "hint.tab": "Tab — наступне питання",
15
+ "hint.cancel": "Esc — скасувати",
16
+
17
+ "review.heading": "Перегляньте свої відповіді",
18
+ "review.ready": "Готові надіслати відповіді?",
19
+ "review.incomplete": "⚠ Дайте відповіді на решту питань перед надсиланням:",
20
+
21
+ "preview.no_preview": "Попередній перегляд недоступний",
22
+ "preview.notes_affordance": "Нотатки: натисніть n, щоб додати нотатки",
23
+ "notes.header": "Нотатки:",
24
+
25
+ "rpc.multi_instructions": "Введіть номери всіх відповідних варіантів через кому (наприклад, \"1,3\") або введіть власну відповідь текстом.",
26
+ "rpc.custom_answer_title": "Введіть свою відповідь:"
27
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "_meta.notes": "中文翻译。欢迎中文母语者通过 PR 审校。TUI 符号(↑/↓、⚠)为通用 CLI 约定,保持原文不译。Enter、Space 采用中文惯用说法(回车、空格);Esc、Tab、Ctrl、n 等保留原文。",
3
+
4
+ "sentinel.other": "输入内容",
5
+ "sentinel.next": "下一个",
6
+
7
+ "submit.label": "提交答案",
8
+ "submit.cancel": "取消",
9
+
10
+ "hint.enter": "回车选择",
11
+ "hint.navigate": "↑/↓ 导航",
12
+ "hint.toggle": "空格切换",
13
+ "hint.notes": "按 n 添加备注",
14
+ "hint.tab": "Tab 切换问题",
15
+ "hint.collapse": "Ctrl+] 折叠",
16
+ "hint.expand_line": "Ctrl+] 展开 · Esc 取消",
17
+ "hint.cancel": "Esc 取消",
18
+
19
+ "review.heading": "检查你的答案",
20
+ "review.ready": "准备提交答案了吗?",
21
+ "review.incomplete": "⚠ 提交前请先回答以下问题:",
22
+
23
+ "preview.no_preview": "暂无预览",
24
+ "preview.notes_affordance": "备注:按 n 添加",
25
+ "notes.header": "备注:",
26
+
27
+ "rpc.multi_instructions": "输入所有适用选项的编号,用逗号分隔(例如 \"1,3\"),或直接输入自定义回答。",
28
+ "rpc.custom_answer_title": "输入你的回答:"
29
+ }
@@ -0,0 +1,49 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/reconcile.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ /**
4
+ * reconcile — mid-session lifecycle reconciliation for ask_user_question.
5
+ *
6
+ * Strips or re-adds the tool to the active set so it is invisible to the LLM
7
+ * in non-interactive runs (no UI) and present in interactive ones. Mirrors the
8
+ * advisor's reconcileAdvisorTool / registerAdvisorBeforeAgentStart pattern
9
+ * (packages/rpiv-advisor/advisor/handlers.ts), simplified: ask_user_question's
10
+ * only gating signal is ctx.hasUI (no model or executor blocklist), so it
11
+ * reads the flag directly and omits the notify.
12
+ *
13
+ * RPC hosts (ctx.mode === "rpc": VSCode pendant, Zed, Paseo) are deliberately
14
+ * NOT stripped: since PR #100 the tool renders there via the select/input
15
+ * dialog walker (rpc-fallback.ts), so hasUI is the honest signal again. The
16
+ * brief period where RPC was stripped (872ef1c) predates that fallback.
17
+ */
18
+
19
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
20
+ import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-user-question.js";
21
+
22
+ /**
23
+ * Strip-or-restore `ask_user_question` to match `ctx.hasUI`. Reads the active
24
+ * tool list itself. Idempotent: when the tool is already in the right state it
25
+ * leaves the active set (and sibling tools) untouched.
26
+ */
27
+ export function reconcileAskUserQuestionTool(pi: ExtensionAPI, ctx: ExtensionContext): void {
28
+ const active = pi.getActiveTools();
29
+ const hasTool = active.includes(ASK_USER_QUESTION_TOOL_NAME);
30
+ // !hasUI → strip so the tool never reaches the LLM's tool list in
31
+ // non-interactive runs; hasUI → restore. The in-handler guards in
32
+ // ask-user-question.ts (!hasUI, and the custom()-undefined → dialog-walker /
33
+ // no_custom_ui backstop) remain as one-turn backstops if a future Pi change
34
+ // reorders the tool-list snapshot ahead of before_agent_start, or a host
35
+ // reports hasUI without any usable rendering primitive.
36
+ if (!ctx.hasUI && hasTool) {
37
+ pi.setActiveTools(active.filter((n) => n !== ASK_USER_QUESTION_TOOL_NAME));
38
+ } else if (ctx.hasUI && !hasTool) {
39
+ pi.setActiveTools([...active, ASK_USER_QUESTION_TOOL_NAME]);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Attach the reconciler to `before_agent_start` so the active set is fixed up
45
+ * before each turn's tool-list snapshot is read. Safe to call once at load.
46
+ */
47
+ export function registerAskUserQuestionReconciler(pi: ExtensionAPI): void {
48
+ pi.on("before_agent_start", (_event, ctx) => reconcileAskUserQuestionTool(pi, ctx));
49
+ }
@@ -0,0 +1,168 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/rpc-fallback.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ /**
4
+ * RPC / dialog-primitive fallback for `ask_user_question`.
5
+ *
6
+ * The canonical TUI path (`ctx.ui.custom()`) renders a tabbed overlay that
7
+ * needs a real terminal. RPC-mode hosts (the VSCode pendant, ACP clients such
8
+ * as Zed or Paseo) report `hasUI: true` because pi's dialog sub-protocol
9
+ * (`extension_ui_request`/`extension_ui_response`) works, but `ui.custom()`
10
+ * resolves `undefined` without rendering anything (issue #78). `ui.select()`
11
+ * and `ui.input()` ARE functional there — the host renders them natively — so
12
+ * this module walks the questions sequentially with those primitives and
13
+ * returns the same `QuestionnaireResult` shapes the TUI produces, feeding the
14
+ * shared `buildQuestionnaireResponse` envelope.
15
+ *
16
+ * Parity trade-offs vs the TUI, inherent to the select/input API surface: no
17
+ * side-by-side preview pane (previews are folded into the prompt title), no
18
+ * tabbed multi-question review (one dialog per question), and multi-select is
19
+ * a free-text numbers input instead of checkbox rows. The "Type something."
20
+ * escape is preserved on both variants — multi-select treats any non-index
21
+ * input as a typed custom answer — matching
22
+ * `ROW_INTENT_META.other.autoAppendOnMultiSelect`.
23
+ */
24
+
25
+ import { displayLabel, t } from "./state/i18n-bridge.js";
26
+ import type { QuestionAnswer, QuestionData, QuestionnaireResult, QuestionParams } from "./tool/types.js";
27
+
28
+ /**
29
+ * Canonical-English fallbacks; resolved through `t()` at dialog time so the
30
+ * live locale applies (top-level `const x = t(...)` would bake load-time
31
+ * English in — see i18n-bridge.ts). The sentinel row label comes from
32
+ * `displayLabel("other")` — same source as the TUI row.
33
+ */
34
+ const MULTI_SELECT_INSTRUCTIONS =
35
+ 'Enter the numbers of all that apply, comma-separated (e.g. "1,3"), or type a custom answer as plain text.';
36
+ const CUSTOM_ANSWER_TITLE = "Type your answer:";
37
+ const MULTI_SELECT_PLACEHOLDER = "1,3";
38
+
39
+ /** Longest preview slice folded into a select title before truncation. */
40
+ const MAX_PREVIEW_CHARS = 600;
41
+
42
+ /**
43
+ * The dialog-primitive slice of `ExtensionUIContext` this walker needs.
44
+ * Structural on purpose: the pinned pi 0.74 peer types predate `ctx.mode`,
45
+ * and jiti transpiles without type-checking — `hasDialogUI` is the runtime
46
+ * gate that makes the shape trustworthy.
47
+ */
48
+ export type DialogUI = {
49
+ select: (title: string, options: string[]) => Promise<string | undefined>;
50
+ input: (title: string, placeholder?: string) => Promise<string | undefined>;
51
+ };
52
+
53
+ /** True when the host implements the select/input dialog primitives. */
54
+ export function hasDialogUI(ui: unknown): ui is DialogUI {
55
+ const u = ui as Partial<Record<"select" | "input", unknown>> | null | undefined;
56
+ return typeof u?.select === "function" && typeof u?.input === "function";
57
+ }
58
+
59
+ type Option = QuestionData["options"][number];
60
+
61
+ function formatOptionLine(option: Option, index: number): string {
62
+ return `${index + 1}. ${option.label} — ${option.description}`;
63
+ }
64
+
65
+ /**
66
+ * Parse a user-entered (or select-returned) "N…" token to a 0-based option
67
+ * index. `parseInt` reads the leading digits of "2. B — b" as 2; NaN and
68
+ * out-of-range fail the bounds check and return null.
69
+ */
70
+ function parseIndex(token: string, count: number): number | null {
71
+ const i = Number.parseInt(token, 10) - 1;
72
+ return i >= 0 && i < count ? i : null;
73
+ }
74
+
75
+ /** Previews folded into the select title — RPC has no side-by-side pane. */
76
+ function buildPreviewBlock(question: QuestionData): string {
77
+ const blocks = question.options.flatMap((o, i) =>
78
+ o.preview && o.preview.length > 0
79
+ ? [`--- ${i + 1}. ${o.label} preview ---\n${o.preview.slice(0, MAX_PREVIEW_CHARS)}`]
80
+ : [],
81
+ );
82
+ return blocks.length > 0 ? `\n\n${blocks.join("\n\n")}` : "";
83
+ }
84
+
85
+ /**
86
+ * Walk the questionnaire one native dialog at a time. Dismissing any dialog
87
+ * (the primitive resolves `undefined`) cancels the whole questionnaire —
88
+ * mirroring Esc in the TUI — and the shared envelope emits DECLINE. A
89
+ * `QuestionAnswer` is produced per question otherwise, so the envelope is
90
+ * identical to the TUI path's.
91
+ */
92
+ export async function runRpcQuestionnaire(ui: DialogUI, params: QuestionParams): Promise<QuestionnaireResult> {
93
+ const answers: QuestionAnswer[] = [];
94
+ for (let qi = 0; qi < params.questions.length; qi++) {
95
+ const q = params.questions[qi];
96
+ const header = q.header ? `[${q.header}] ` : "";
97
+ const answer = q.multiSelect ? await askMultiSelect(ui, q, qi, header) : await askSingleSelect(ui, q, qi, header);
98
+ if (answer === undefined) return { answers, cancelled: true };
99
+ answers.push(answer);
100
+ }
101
+ return { answers, cancelled: false };
102
+ }
103
+
104
+ /** `undefined` means the user dismissed the dialog (cancel the questionnaire). */
105
+ async function askSingleSelect(
106
+ ui: DialogUI,
107
+ q: QuestionData,
108
+ questionIndex: number,
109
+ header: string,
110
+ ): Promise<QuestionAnswer | undefined> {
111
+ const options = q.options.map(formatOptionLine);
112
+ options.push(`${q.options.length + 1}. ${displayLabel("other")}`);
113
+ const chosen = await ui.select(`${header}${q.question}${buildPreviewBlock(q)}`, options);
114
+ if (chosen == null) return undefined;
115
+ const idx = parseIndex(chosen, options.length);
116
+ // A host returning something outside the offered list is indistinguishable
117
+ // from a dismissal — treat it as one rather than fabricate an answer.
118
+ if (idx == null) return undefined;
119
+ if (idx < q.options.length) {
120
+ const o = q.options[idx];
121
+ return {
122
+ questionIndex,
123
+ question: q.question,
124
+ kind: "option",
125
+ answer: o.label,
126
+ preview: o.preview && o.preview.length > 0 ? o.preview : undefined,
127
+ };
128
+ }
129
+ // "Type something." sentinel → free-text follow-up.
130
+ const typed = await ui.input(`${header}${q.question}\n\n${t("rpc.custom_answer_title", CUSTOM_ANSWER_TITLE)}`, "");
131
+ if (typed == null) return undefined;
132
+ return { questionIndex, question: q.question, kind: "custom", answer: typed };
133
+ }
134
+
135
+ /** `undefined` means the user dismissed the dialog (cancel the questionnaire). */
136
+ async function askMultiSelect(
137
+ ui: DialogUI,
138
+ q: QuestionData,
139
+ questionIndex: number,
140
+ header: string,
141
+ ): Promise<QuestionAnswer | undefined> {
142
+ const list = q.options.map(formatOptionLine).join("\n");
143
+ const value = await ui.input(
144
+ `${header}${q.question}\n\n${list}\n\n${t("rpc.multi_instructions", MULTI_SELECT_INSTRUCTIONS)}`,
145
+ MULTI_SELECT_PLACEHOLDER,
146
+ );
147
+ if (value == null) return undefined;
148
+ const trimmed = value.trim();
149
+ if (trimmed.length === 0) {
150
+ // Deliberate empty commit — same as pressing "Next" with nothing toggled.
151
+ return { questionIndex, question: q.question, kind: "multi", answer: null, selected: [] };
152
+ }
153
+ const tokens = trimmed.split(/[,\s]+/).filter((tok) => tok.length > 0);
154
+ const indices = tokens.map((tok) => (/^\d+\.?$/.test(tok) ? parseIndex(tok, q.options.length) : null));
155
+ if (indices.every((i): i is number => i != null)) {
156
+ const selected: string[] = [];
157
+ for (const i of indices) {
158
+ const label = q.options[i].label;
159
+ if (!selected.includes(label)) selected.push(label);
160
+ }
161
+ return { questionIndex, question: q.question, kind: "multi", answer: null, selected };
162
+ }
163
+ // Any non-index token (words, or an out-of-range number like "13" for three
164
+ // options) means the user typed an answer, not a selection. Preserve it
165
+ // verbatim as a custom answer instead of silently dropping their input —
166
+ // this is also the multi-select "Type something." escape.
167
+ return { questionIndex, question: q.question, kind: "custom", answer: trimmed };
168
+ }
@@ -0,0 +1,302 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/build-questionnaire.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import { getMarkdownTheme, type Theme } from "@earendil-works/pi-coding-agent";
4
+ import { Input } from "@earendil-works/pi-tui";
5
+ import type { QuestionData } from "../tool/types.js";
6
+ import {
7
+ type BoundGlobalBinding,
8
+ type BoundPerTabBinding,
9
+ globalBinding,
10
+ perTabBinding,
11
+ } from "../view/component-binding.js";
12
+ import { MultiSelectView } from "../view/components/multi-select-view.js";
13
+ import { OptionListView } from "../view/components/option-list-view.js";
14
+ import { PreviewBlockRenderer } from "../view/components/preview/preview-block-renderer.js";
15
+ import { crossTabLeftWidthWithDonation } from "../view/components/preview/preview-layout-decider.js";
16
+ import type { PreviewPaneProps } from "../view/components/preview/preview-pane.js";
17
+ import { PreviewPane } from "../view/components/preview/preview-pane.js";
18
+ import { SubmitPicker } from "../view/components/submit-picker.js";
19
+ import { TabBar } from "../view/components/tab-bar.js";
20
+ import type { WrappingSelectItem, WrappingSelectTheme } from "../view/components/wrapping-select.js";
21
+ import { DialogView } from "../view/dialog-builder.js";
22
+ import { QuestionnairePropsAdapter } from "../view/props-adapter.js";
23
+ import type { StatefulView } from "../view/stateful-view.js";
24
+ import type { TabBodyHeights, TabComponents } from "../view/tab-components.js";
25
+ import type { PerTabSelector } from "./selectors/contract.js";
26
+ import { selectActivePreviewPaneIndex } from "./selectors/derivations.js";
27
+ import {
28
+ selectDialogProps,
29
+ selectMultiSelectProps,
30
+ selectOptionListProps,
31
+ selectPreviewPaneProps,
32
+ selectSubmitPickerProps,
33
+ selectTabBarProps,
34
+ } from "./selectors/projections.js";
35
+ import type { QuestionnaireState } from "./state.js";
36
+
37
+ export interface QuestionnaireBuildConfig {
38
+ tui: { terminal: { columns: number; rows: number }; requestRender(): void };
39
+ theme: Theme;
40
+ questions: readonly QuestionData[];
41
+ itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;
42
+ isMulti: boolean;
43
+ initialState: QuestionnaireState;
44
+ getCurrentTab: () => number;
45
+ }
46
+
47
+ export interface QuestionnaireBuilt {
48
+ adapter: QuestionnairePropsAdapter;
49
+ notesInput: Input;
50
+ inlineInput: Input;
51
+ render: (width: number) => string[];
52
+ invalidate: () => void;
53
+ }
54
+
55
+ interface HeightComputers {
56
+ global: (width: number) => number;
57
+ current: (width: number) => number;
58
+ }
59
+
60
+ function previewBodyHeights(pane: PreviewPane): (width: number) => TabBodyHeights {
61
+ return (width) => ({
62
+ current: pane.naturalHeight(width),
63
+ max: pane.maxNaturalHeight(width),
64
+ });
65
+ }
66
+
67
+ function multiSelectBodyHeights(view: MultiSelectView): (width: number) => TabBodyHeights {
68
+ return (width) => {
69
+ const h = view.naturalHeight(width);
70
+ return { current: h, max: h };
71
+ };
72
+ }
73
+
74
+ const isActiveTab: PerTabSelector<boolean> = (s, ctx) =>
75
+ ctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions);
76
+
77
+ /**
78
+ * Pure factory: assembles every TUI component, the props adapter, and a
79
+ * lifecycle handle. Session-state dependencies arrive via `getCurrentTab` and
80
+ * the `inputBuffer` cell. Initial paint is delegated to
81
+ * `adapter.apply(initialState)` (called by the session at construction-end);
82
+ * no selector is invoked here.
83
+ */
84
+ export function buildQuestionnaire(config: QuestionnaireBuildConfig): QuestionnaireBuilt {
85
+ return new QuestionnaireBuilder(config).build();
86
+ }
87
+
88
+ /**
89
+ * Programming-by-intention assembly: each private method names one
90
+ * construction step from `build()`. Read top-down; the class is build-only and
91
+ * discarded once the handle is returned.
92
+ */
93
+ class QuestionnaireBuilder {
94
+ private readonly tui: QuestionnaireBuildConfig["tui"];
95
+ private readonly theme: Theme;
96
+ private readonly questions: readonly QuestionData[];
97
+ private readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;
98
+ private readonly isMulti: boolean;
99
+ private readonly initialState: QuestionnaireState;
100
+ private readonly getCurrentTab: () => number;
101
+
102
+ private readonly selectTheme: WrappingSelectTheme;
103
+ private readonly markdownTheme = getMarkdownTheme();
104
+ private readonly notesInput = new Input();
105
+ private readonly inlineInput = new Input();
106
+ private readonly getTerminalWidth = () => this.tui.terminal.columns;
107
+ private readonly getTerminalRows = () => this.tui.terminal.rows;
108
+
109
+ constructor(config: QuestionnaireBuildConfig) {
110
+ this.tui = config.tui;
111
+ this.theme = config.theme;
112
+ this.questions = config.questions;
113
+ this.itemsByTab = config.itemsByTab;
114
+ this.isMulti = config.isMulti;
115
+ this.initialState = config.initialState;
116
+ this.getCurrentTab = config.getCurrentTab;
117
+
118
+ this.selectTheme = this.makeSelectTheme();
119
+ }
120
+
121
+ build(): QuestionnaireBuilt {
122
+ const tabs = this.buildTabComponents();
123
+ this.injectGlobalLeftWidth(tabs);
124
+ const submitPicker = this.buildSubmitPicker();
125
+ const tabBar = this.buildTabBar();
126
+ const heights = this.buildHeightComputers(tabs);
127
+ const dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);
128
+ const globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);
129
+ const perTabBindings = this.buildPerTabBindings();
130
+ const adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);
131
+ return this.handle(adapter, dialog);
132
+ }
133
+
134
+ private makeSelectTheme(): WrappingSelectTheme {
135
+ const t = this.theme;
136
+ return {
137
+ selectedText: (s) => t.fg("accent", t.bold(s)),
138
+ description: (s) => t.fg("muted", s),
139
+ scrollInfo: (s) => t.fg("dim", s),
140
+ };
141
+ }
142
+
143
+ private buildTabComponents(): ReadonlyArray<TabComponents> {
144
+ return this.questions.map((q, i) => this.buildTabFor(q, i));
145
+ }
146
+
147
+ private buildTabFor(question: QuestionData, index: number): TabComponents {
148
+ const optionList = new OptionListView({
149
+ items: this.itemsByTab[index] ?? [],
150
+ theme: this.selectTheme,
151
+ });
152
+ const previewBlock = new PreviewBlockRenderer({
153
+ question,
154
+ theme: this.theme,
155
+ markdownTheme: this.markdownTheme,
156
+ });
157
+ const preview = new PreviewPane({
158
+ question,
159
+ getTerminalWidth: this.getTerminalWidth,
160
+ optionListView: optionList,
161
+ previewBlock,
162
+ });
163
+ const multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : undefined;
164
+ const bodyHeights = this.buildBodyHeights(question, preview, multiSelect);
165
+ return { optionList, preview, multiSelect, bodyHeights };
166
+ }
167
+
168
+ private buildBodyHeights(
169
+ question: QuestionData,
170
+ preview: PreviewPane,
171
+ multiSelect: MultiSelectView | undefined,
172
+ ): (width: number) => TabBodyHeights {
173
+ return question.multiSelect ? multiSelectBodyHeights(multiSelect!) : previewBodyHeights(preview);
174
+ }
175
+
176
+ /**
177
+ * Compute cross-tab max adaptive left width and inject into each PreviewPane.
178
+ * Mirrors buildHeightComputers pattern — iterates all tabs, takes max.
179
+ * Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth
180
+ * is set before any rendering occurs).
181
+ */
182
+ private injectGlobalLeftWidth(tabs: ReadonlyArray<TabComponents>): void {
183
+ const questions = this.questions;
184
+ const itemsByTab = this.itemsByTab;
185
+ const tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));
186
+ const globalLeftWidth = (paneWidth: number): number =>
187
+ crossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth);
188
+ for (const tab of tabs) {
189
+ tab.preview.setGlobalLeftWidth(globalLeftWidth);
190
+ }
191
+ }
192
+
193
+ private buildSubmitPicker(): SubmitPicker | undefined {
194
+ return this.isMulti ? new SubmitPicker(this.theme) : undefined;
195
+ }
196
+
197
+ private buildTabBar(): TabBar | undefined {
198
+ return this.isMulti ? new TabBar(this.theme) : undefined;
199
+ }
200
+
201
+ private buildHeightComputers(tabs: ReadonlyArray<TabComponents>): HeightComputers {
202
+ const global = (width: number): number => {
203
+ let max = 0;
204
+ for (const tab of tabs) {
205
+ const h = tab.bodyHeights(width).max;
206
+ if (h > max) max = h;
207
+ }
208
+ return Math.max(1, max);
209
+ };
210
+ const current = (width: number): number => {
211
+ const idx = Math.min(this.getCurrentTab(), tabs.length - 1);
212
+ return Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);
213
+ };
214
+ return { global, current };
215
+ }
216
+
217
+ private pickInitialActivePreview(tabs: ReadonlyArray<TabComponents>): StatefulView<PreviewPaneProps> {
218
+ const idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);
219
+ return tabs[idx]?.preview ?? tabs[0]!.preview;
220
+ }
221
+
222
+ private buildDialog(
223
+ tabs: ReadonlyArray<TabComponents>,
224
+ submitPicker: SubmitPicker | undefined,
225
+ tabBar: TabBar | undefined,
226
+ heights: HeightComputers,
227
+ ): DialogView {
228
+ return new DialogView(
229
+ {
230
+ theme: this.theme,
231
+ questions: this.questions,
232
+ tabBar,
233
+ notesInput: this.notesInput,
234
+ isMulti: this.isMulti,
235
+ tabsByIndex: tabs,
236
+ submitPicker,
237
+ getBodyHeight: heights.global,
238
+ getCurrentBodyHeight: heights.current,
239
+ getTerminalRows: this.getTerminalRows,
240
+ },
241
+ { state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },
242
+ );
243
+ }
244
+
245
+ private buildGlobalBindings(
246
+ dialog: DialogView,
247
+ submitPicker: SubmitPicker | undefined,
248
+ tabBar: TabBar | undefined,
249
+ ): ReadonlyArray<BoundGlobalBinding> {
250
+ return [
251
+ globalBinding({ component: dialog, select: selectDialogProps }),
252
+ ...(submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : []),
253
+ ...(tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []),
254
+ ];
255
+ }
256
+
257
+ private buildPerTabBindings(): ReadonlyArray<BoundPerTabBinding> {
258
+ return [
259
+ perTabBinding({
260
+ resolve: (tab) => tab.optionList,
261
+ predicate: isActiveTab,
262
+ select: selectOptionListProps,
263
+ }),
264
+ perTabBinding({
265
+ resolve: (tab) => tab.preview,
266
+ predicate: isActiveTab,
267
+ select: selectPreviewPaneProps,
268
+ }),
269
+ perTabBinding({
270
+ resolve: (tab) => tab.multiSelect,
271
+ select: selectMultiSelectProps,
272
+ }),
273
+ ];
274
+ }
275
+
276
+ private buildAdapter(
277
+ tabs: ReadonlyArray<TabComponents>,
278
+ globalBindings: ReadonlyArray<BoundGlobalBinding>,
279
+ perTabBindings: ReadonlyArray<BoundPerTabBinding>,
280
+ ): QuestionnairePropsAdapter {
281
+ return new QuestionnairePropsAdapter({
282
+ tui: this.tui,
283
+ questions: this.questions,
284
+ itemsByTab: this.itemsByTab,
285
+ tabsByIndex: tabs,
286
+ inlineInput: this.inlineInput,
287
+ globalBindings,
288
+ perTabBindings,
289
+ extraInvalidatables: [this.notesInput],
290
+ });
291
+ }
292
+
293
+ private handle(adapter: QuestionnairePropsAdapter, dialog: DialogView): QuestionnaireBuilt {
294
+ return {
295
+ adapter,
296
+ notesInput: this.notesInput,
297
+ inlineInput: this.inlineInput,
298
+ render: (w) => dialog.render(w),
299
+ invalidate: () => adapter.invalidate(),
300
+ };
301
+ }
302
+ }
@@ -0,0 +1,53 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/i18n-bridge.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ /**
4
+ * i18n bridge for rpiv-ask-user-question — single thin import surface so every
5
+ * call site routes through one place. Backed by `@juicesharp/rpiv-i18n`'s SDK
6
+ * when available; degrades to canonical-English fallbacks when not.
7
+ *
8
+ * - `t(key, fallback)` is `scope("@juicesharp/rpiv-ask-user-question")` if the
9
+ * SDK is installed (live `/languages` updates propagate). If the SDK is
10
+ * missing (standalone install without rpiv-i18n), `t` is an identity
11
+ * passthrough that returns the inline English fallback at every call site,
12
+ * so the extension stays online with English UI.
13
+ * - `displayLabel(kind)` resolves a sentinel kind to its locale-aware label,
14
+ * with the canonical English `ROW_INTENT_META[kind].label` as fallback so
15
+ * nothing renders blank if the namespace isn't registered.
16
+ *
17
+ * Strings are registered ONCE at extension load (see ../index.ts). Call sites
18
+ * MUST use this module at render time — never bake the result into a top-level
19
+ * `const X = displayLabel(...)`.
20
+ *
21
+ * Reserved-label validation stays English-locked: `RESERVED_LABEL_SET` checks
22
+ * the canonical `ROW_INTENT_META[kind].label`, never `displayLabel(kind)`.
23
+ */
24
+
25
+ import { ROW_INTENT_META, type SentinelKind } from "./row-intent.js";
26
+
27
+ export const I18N_NAMESPACE = "@juicesharp/rpiv-ask-user-question";
28
+
29
+ type ScopeFn = (key: string, fallback: string) => string;
30
+ type I18nSDK = { scope: (namespace: string) => ScopeFn };
31
+
32
+ // Prefer the live SDK if installed: closures it returns track the active
33
+ // locale, so /languages picker propagates to our render call sites. If the
34
+ // SDK isn't installed (standalone install of this extension without
35
+ // rpiv-i18n), the dynamic import fails, every t(key, fallback) returns the
36
+ // canonical English literal, and the extension stays online.
37
+ //
38
+ // Top-level await is required so a synchronous `t(...)` call from any
39
+ // downstream module sees the resolved scope; ESM module loading awaits this
40
+ // before evaluating any importer.
41
+ let scopeImpl: ScopeFn;
42
+ try {
43
+ const sdk = (await import("@juicesharp/rpiv-i18n")) as I18nSDK;
44
+ scopeImpl = sdk.scope(I18N_NAMESPACE);
45
+ } catch {
46
+ scopeImpl = (_key, fallback) => fallback;
47
+ }
48
+
49
+ export const t: ScopeFn = scopeImpl;
50
+
51
+ export function displayLabel(kind: SentinelKind): string {
52
+ return t(`sentinel.${kind}`, ROW_INTENT_META[kind].label);
53
+ }