@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,42 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/selectors/derivations.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import type { QuestionAnswer, QuestionData } from "../../tool/types.js";
4
+ import type { WrappingSelectItem } from "../../view/components/wrapping-select.js";
5
+
6
+ /**
7
+ * Which row in the active tab should be marked as "previously confirmed"? Drives the
8
+ * `WrappingSelect` confirmed-row indicator (label + ` ✔`) when the user navigates back
9
+ * to a question they already answered. Returns `undefined` when no marker should be drawn —
10
+ * multi-select handles its own `[✔]` boxes via `multiSelectChecked`, and a missing/non-matching
11
+ * answer (defensive) silently skips the marker.
12
+ */
13
+ export function selectConfirmedIndicator(
14
+ questions: readonly QuestionData[],
15
+ currentTab: number,
16
+ answers: ReadonlyMap<number, QuestionAnswer>,
17
+ items: readonly WrappingSelectItem[],
18
+ ): { index: number; labelOverride?: string } | undefined {
19
+ const q = questions[currentTab];
20
+ if (!q || q.multiSelect === true) return undefined;
21
+ const prior = answers.get(currentTab);
22
+ if (!prior) return undefined;
23
+ if (prior.kind === "custom") {
24
+ const otherIndex = items.findIndex((it) => it.kind === "other");
25
+ if (otherIndex < 0) return undefined;
26
+ return { index: otherIndex, labelOverride: prior.answer ?? "" };
27
+ }
28
+ if (prior.kind !== "option" || typeof prior.answer !== "string") return undefined;
29
+ const index = items.findIndex((it) => it.kind === "option" && it.label === prior.answer);
30
+ if (index < 0) return undefined;
31
+ return { index };
32
+ }
33
+
34
+ /**
35
+ * Index of the preview pane to display for the current tab. The Submit tab (currentTab ===
36
+ * questions.length) reuses the last question's pane purely for layout — the strategy
37
+ * machinery picks the right body component independently. Defensive against `totalQuestions === 0`.
38
+ */
39
+ export function selectActivePreviewPaneIndex(currentTab: number, totalQuestions: number): number {
40
+ if (totalQuestions <= 0) return 0;
41
+ return Math.min(currentTab, totalQuestions - 1);
42
+ }
@@ -0,0 +1,19 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/selectors/focus.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import type { ActiveView } from "../../view/stateful-view.js";
4
+
5
+ /**
6
+ * Discriminated focus selector — single source of truth for "which view owns
7
+ * focus this tick?" Priority order matches the dispatcher cascade
8
+ * (`key-router.ts`) and the reducer's defensive clears (`state-reducer.ts`).
9
+ *
10
+ * Priority: notes > submit > options.
11
+ */
12
+ export function selectActiveView(
13
+ state: { notesVisible: boolean; currentTab: number },
14
+ totalQuestions: number,
15
+ ): ActiveView {
16
+ if (state.notesVisible) return "notes";
17
+ if (state.currentTab === totalQuestions) return "submit";
18
+ return "options";
19
+ }
@@ -0,0 +1,101 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/selectors/projections.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import { MULTI_SUBMIT_LABEL, type MultiSelectViewProps } from "../../view/components/multi-select-view.js";
4
+ import type { OptionListViewProps } from "../../view/components/option-list-view.js";
5
+ import type { PreviewPaneProps } from "../../view/components/preview/preview-pane.js";
6
+ import type { SubmitPickerProps } from "../../view/components/submit-picker.js";
7
+ import type { TabBarProps } from "../../view/components/tab-bar.js";
8
+ import type { DialogProps } from "../../view/dialog-builder.js";
9
+ import { displayLabel } from "../i18n-bridge.js";
10
+ import type { GlobalSelector, PerTabSelector } from "./contract.js";
11
+ import { selectConfirmedIndicator } from "./derivations.js";
12
+
13
+ export const selectMultiSelectProps: PerTabSelector<MultiSelectViewProps> = (state, ctx) => {
14
+ const question = ctx.questions[ctx.i];
15
+ if (!question) {
16
+ return {
17
+ rows: [],
18
+ other: {
19
+ active: false,
20
+ inputMode: false,
21
+ inputBuffer: ctx.inputBuffer,
22
+ inputCursorOffset: ctx.inputCursorOffset,
23
+ },
24
+ nextActive: false,
25
+ nextLabel: displayLabel("next"),
26
+ };
27
+ }
28
+ const focused = ctx.activeView === "options";
29
+ const rows: { checked: boolean; active: boolean }[] = [];
30
+ for (let i = 0; i < question.options.length; i++) {
31
+ rows.push({
32
+ checked: state.multiSelectChecked.has(i),
33
+ active: focused && i === state.optionIndex,
34
+ });
35
+ }
36
+ const otherActive = focused && state.optionIndex === question.options.length;
37
+ const nextActive = focused && state.optionIndex === question.options.length + 1;
38
+ const isLastQuestion = ctx.i === ctx.questions.length - 1;
39
+ const nextLabel = isLastQuestion ? MULTI_SUBMIT_LABEL : displayLabel("next");
40
+ return {
41
+ rows,
42
+ other: {
43
+ active: otherActive,
44
+ inputMode: state.inputMode,
45
+ inputBuffer: ctx.inputBuffer,
46
+ inputCursorOffset: ctx.inputCursorOffset,
47
+ },
48
+ nextActive,
49
+ nextLabel,
50
+ };
51
+ };
52
+
53
+ export const selectOptionListProps: PerTabSelector<OptionListViewProps> = (state, ctx) => {
54
+ const items = ctx.itemsByTab[ctx.i] ?? [];
55
+ const focused = ctx.activeView === "options";
56
+ const confirmed = selectConfirmedIndicator(ctx.questions, state.currentTab, state.answers, items);
57
+ return {
58
+ selectedIndex: state.optionIndex,
59
+ focused,
60
+ inputBuffer: ctx.inputBuffer,
61
+ inputCursorOffset: ctx.inputCursorOffset,
62
+ ...(confirmed ? { confirmed } : {}),
63
+ };
64
+ };
65
+
66
+ export const selectSubmitPickerProps: GlobalSelector<SubmitPickerProps> = (state, ctx) => {
67
+ const focused = ctx.activeView === "submit";
68
+ return {
69
+ rows: [
70
+ { active: focused && state.submitChoiceIndex === 0 },
71
+ { active: focused && state.submitChoiceIndex === 1 },
72
+ ],
73
+ };
74
+ };
75
+
76
+ export const selectPreviewPaneProps: PerTabSelector<PreviewPaneProps> = (state, ctx) => ({
77
+ notesVisible: state.notesVisible,
78
+ selectedIndex: state.optionIndex,
79
+ focused: ctx.activeView === "options",
80
+ inputMode: state.inputMode,
81
+ });
82
+
83
+ export const selectTabBarProps: GlobalSelector<TabBarProps> = (state, ctx) => {
84
+ const tabs = ctx.questions.map((q, i) => ({
85
+ label: q.header && q.header.length > 0 ? q.header : `Q${i + 1}`,
86
+ answered: state.answers.has(i),
87
+ active: i === state.currentTab,
88
+ }));
89
+ return {
90
+ tabs,
91
+ submit: {
92
+ active: state.currentTab === ctx.questions.length,
93
+ allAnswered: state.answers.size === ctx.questions.length && ctx.questions.length > 0,
94
+ },
95
+ };
96
+ };
97
+
98
+ export const selectDialogProps: GlobalSelector<DialogProps> = (state, ctx) => ({
99
+ state,
100
+ activePreviewPane: ctx.activePreviewPane,
101
+ });
@@ -0,0 +1,292 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/state-reducer.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import type { QuestionAnswer, QuestionData, QuestionnaireResult } from "../tool/types.js";
4
+ import type { WrappingSelectItem } from "../view/components/wrapping-select.js";
5
+ import type { QuestionnaireAction } from "./key-router.js";
6
+ import { ROW_INTENT_META } from "./row-intent.js";
7
+ import type { QuestionnaireState } from "./state.js";
8
+
9
+ /** Session-lifetime constants. No live-component reads — peripheral values live on canonical state. */
10
+ export interface ApplyContext {
11
+ questions: readonly QuestionData[];
12
+ itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;
13
+ }
14
+
15
+ /**
16
+ * Declarative side-effects emitted by `reduce`. The runtime executes them after
17
+ * committing the new state, then asks the props-adapter to re-project. Closed set —
18
+ * adding an effect requires updating both the union AND the runtime's `runEffect` switch
19
+ * (compiler-enforced exhaustive). No string-keyed escape hatch.
20
+ */
21
+ export type Effect =
22
+ | { kind: "set_input_buffer"; value: string }
23
+ | { kind: "clear_input_buffer" }
24
+ | { kind: "set_notes_value"; value: string }
25
+ | { kind: "set_notes_focused"; focused: boolean }
26
+ | { kind: "forward_notes_keystroke"; data: string }
27
+ /**
28
+ * Tell the session to hide or show its underlying overlay. Emitted by the
29
+ * `toggle_collapsed` action so the runtime can call `OverlayHandle.setHidden(...)`,
30
+ * which lets other overlay-aware consumers (e.g. `pi-station`) see the questionnaire
31
+ * as truly hidden and resume normal chat scroll while the user reads the transcript
32
+ * behind the modal.
33
+ */
34
+ | { kind: "set_overlay_hidden"; hidden: boolean }
35
+ | { kind: "done"; result: QuestionnaireResult };
36
+
37
+ export interface ApplyResult {
38
+ state: QuestionnaireState;
39
+ effects: readonly Effect[];
40
+ }
41
+
42
+ function orderedAnswers(state: QuestionnaireState, questions: readonly QuestionData[]): QuestionAnswer[] {
43
+ const out: QuestionAnswer[] = [];
44
+ for (let i = 0; i < questions.length; i++) {
45
+ const a = state.answers.get(i);
46
+ if (a) out.push(a);
47
+ }
48
+ return out;
49
+ }
50
+
51
+ function syncMultiSelectFromAnswers(
52
+ answers: ReadonlyMap<number, QuestionAnswer>,
53
+ questions: readonly QuestionData[],
54
+ tab: number,
55
+ ): ReadonlySet<number> {
56
+ const q = questions[tab];
57
+ if (!q?.multiSelect) return new Set();
58
+ const saved = answers.get(tab);
59
+ const labels = saved?.selected ?? [];
60
+ const indices = new Set<number>();
61
+ for (let i = 0; i < q.options.length; i++) {
62
+ if (labels.includes(q.options[i]!.label)) indices.add(i);
63
+ }
64
+ return indices;
65
+ }
66
+
67
+ function persistMultiSelectAnswer(state: QuestionnaireState, ctx: ApplyContext): ReadonlyMap<number, QuestionAnswer> {
68
+ const q = ctx.questions[state.currentTab];
69
+ if (!q?.multiSelect) return state.answers;
70
+ const selected: string[] = [];
71
+ for (let i = 0; i < q.options.length; i++) {
72
+ if (state.multiSelectChecked.has(i)) selected.push(q.options[i]!.label);
73
+ }
74
+ const out = new Map(state.answers);
75
+ if (selected.length === 0) {
76
+ out.delete(state.currentTab);
77
+ return out;
78
+ }
79
+ const pendingNotes = state.notesByTab.get(state.currentTab);
80
+ out.set(state.currentTab, {
81
+ questionIndex: state.currentTab,
82
+ question: q.question,
83
+ kind: "multi",
84
+ answer: null,
85
+ selected,
86
+ ...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),
87
+ });
88
+ return out;
89
+ }
90
+
91
+ /**
92
+ * Note text to seed the editor/draft with for a tab. The in-flight side-band store
93
+ * (`notesByTab`) is authoritative — before an option is confirmed the note lives ONLY
94
+ * there; `answer.notes` is a mirror written on exit/confirm.
95
+ */
96
+ function notesValueFor(state: QuestionnaireState, tab: number): string {
97
+ return state.notesByTab.get(tab) ?? state.answers.get(tab)?.notes ?? "";
98
+ }
99
+
100
+ function switchTabResult(state: QuestionnaireState, nextTab: number, ctx: ApplyContext): ApplyResult {
101
+ const notesValue = notesValueFor(state, nextTab);
102
+ const transitioned: QuestionnaireState = {
103
+ ...state,
104
+ currentTab: nextTab,
105
+ optionIndex: 0,
106
+ inputMode: false,
107
+ notesVisible: false,
108
+ submitChoiceIndex: 0,
109
+ multiSelectChecked: syncMultiSelectFromAnswers(state.answers, ctx.questions, nextTab),
110
+ notesDraft: notesValue,
111
+ };
112
+ return {
113
+ state: transitioned,
114
+ effects: [
115
+ { kind: "set_notes_focused", focused: false },
116
+ { kind: "set_notes_value", value: notesValue },
117
+ ],
118
+ };
119
+ }
120
+
121
+ function doneFor(state: QuestionnaireState, ctx: ApplyContext, cancelled: boolean): ApplyResult {
122
+ const result: QuestionnaireResult = { answers: orderedAnswers(state, ctx.questions), cancelled };
123
+ return { state, effects: [{ kind: "done", result }] };
124
+ }
125
+
126
+ /**
127
+ * Per-kind handler signature: action payload narrows to the matching union member
128
+ * via `Extract`, so handlers consume fully-typed actions without `as` casts.
129
+ */
130
+ type Handler<K extends QuestionnaireAction["kind"]> = (
131
+ state: QuestionnaireState,
132
+ action: Extract<QuestionnaireAction, { kind: K }>,
133
+ ctx: ApplyContext,
134
+ ) => ApplyResult;
135
+
136
+ const navHandler: Handler<"nav"> = (state, action, ctx) => {
137
+ const items = ctx.itemsByTab[state.currentTab] ?? [];
138
+ const item = items[action.nextIndex];
139
+ const inputMode = item ? ROW_INTENT_META[item.kind].activatesInputMode : false;
140
+ const next = { ...state, optionIndex: action.nextIndex, inputMode };
141
+ if (!inputMode) {
142
+ return { state: next, effects: [{ kind: "clear_input_buffer" }] };
143
+ }
144
+ const prior = state.answers.get(state.currentTab);
145
+ if (prior?.kind === "custom" && typeof prior.answer === "string") {
146
+ return { state: next, effects: [{ kind: "set_input_buffer", value: prior.answer }] };
147
+ }
148
+ return { state: next, effects: [] };
149
+ };
150
+
151
+ const tabSwitchHandler: Handler<"tab_switch"> = (state, action, ctx) => switchTabResult(state, action.nextTab, ctx);
152
+
153
+ const confirmHandler: Handler<"confirm"> = (state, action, ctx) => {
154
+ let answer = action.answer;
155
+ if (answer.kind === "option" && answer.answer) {
156
+ const q = ctx.questions[answer.questionIndex];
157
+ const matched = q?.options.find((o) => o.label === answer.answer);
158
+ if (matched?.preview && matched.preview.length > 0) {
159
+ answer = { ...answer, preview: matched.preview };
160
+ }
161
+ }
162
+ const pendingNotes = state.notesByTab.get(answer.questionIndex);
163
+ if (pendingNotes && pendingNotes.length > 0) {
164
+ answer = { ...answer, notes: pendingNotes };
165
+ }
166
+ const answers = new Map(state.answers);
167
+ answers.set(answer.questionIndex, answer);
168
+ // Custom free-text on a multi-select tab is mutually exclusive with checkbox selections:
169
+ // clear the checked set immediately so [✔] glyphs vanish on Enter. (A custom answer
170
+ // carries no `selected` array, so syncMultiSelectFromAnswers keeps it empty on tab-back.)
171
+ const isCustomMulti = answer.kind === "custom" && ctx.questions[answer.questionIndex]?.multiSelect === true;
172
+ const next: QuestionnaireState = {
173
+ ...state,
174
+ answers,
175
+ ...(isCustomMulti ? { multiSelectChecked: new Set<number>() } : {}),
176
+ };
177
+ if (action.autoAdvanceTab !== undefined) return switchTabResult(next, action.autoAdvanceTab, ctx);
178
+ return doneFor(next, ctx, false);
179
+ };
180
+
181
+ const toggleHandler: Handler<"toggle"> = (state, action, ctx) => {
182
+ const checked = new Set(state.multiSelectChecked);
183
+ if (checked.has(action.index)) checked.delete(action.index);
184
+ else checked.add(action.index);
185
+ const intermediate: QuestionnaireState = { ...state, multiSelectChecked: checked };
186
+ const answers = persistMultiSelectAnswer(intermediate, ctx);
187
+ return { state: { ...intermediate, answers }, effects: [] };
188
+ };
189
+
190
+ const multiConfirmHandler: Handler<"multi_confirm"> = (state, action, ctx) => {
191
+ const q = ctx.questions[state.currentTab];
192
+ if (!q) return { state, effects: [] };
193
+ const pendingNotes = state.notesByTab.get(state.currentTab);
194
+ const answers = new Map(state.answers);
195
+ answers.set(state.currentTab, {
196
+ questionIndex: state.currentTab,
197
+ question: q.question,
198
+ kind: "multi",
199
+ answer: null,
200
+ selected: action.selected,
201
+ ...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),
202
+ });
203
+ const synced: QuestionnaireState = {
204
+ ...state,
205
+ answers,
206
+ multiSelectChecked: syncMultiSelectFromAnswers(answers, ctx.questions, state.currentTab),
207
+ };
208
+ if (action.autoAdvanceTab !== undefined) return switchTabResult(synced, action.autoAdvanceTab, ctx);
209
+ return doneFor(synced, ctx, false);
210
+ };
211
+
212
+ const notesEnterHandler: Handler<"notes_enter"> = (state, _action, _ctx) => {
213
+ const value = notesValueFor(state, state.currentTab);
214
+ return {
215
+ state: { ...state, notesVisible: true, notesDraft: value },
216
+ effects: [
217
+ { kind: "set_notes_value", value },
218
+ { kind: "set_notes_focused", focused: true },
219
+ ],
220
+ };
221
+ };
222
+
223
+ const notesExitHandler: Handler<"notes_exit"> = (state, _action, _ctx) => {
224
+ const trimmed = state.notesDraft.trim();
225
+ const notes = new Map(state.notesByTab);
226
+ const answers = new Map(state.answers);
227
+ if (trimmed.length === 0) {
228
+ notes.delete(state.currentTab);
229
+ const prev = answers.get(state.currentTab);
230
+ if (prev?.notes) {
231
+ const stripped = { ...prev };
232
+ delete (stripped as { notes?: string }).notes;
233
+ answers.set(state.currentTab, stripped);
234
+ }
235
+ } else {
236
+ notes.set(state.currentTab, trimmed);
237
+ const prev = answers.get(state.currentTab);
238
+ if (prev) answers.set(state.currentTab, { ...prev, notes: trimmed });
239
+ }
240
+ return {
241
+ state: { ...state, notesByTab: notes, answers, notesVisible: false },
242
+ effects: [{ kind: "set_notes_focused", focused: false }],
243
+ };
244
+ };
245
+
246
+ const cancelHandler: Handler<"cancel"> = (s, _a, c) => doneFor(s, c, true);
247
+ const submitHandler: Handler<"submit"> = (s, _a, c) => doneFor(s, c, false);
248
+ const submitNavHandler: Handler<"submit_nav"> = (s, a, _c) => ({
249
+ state: { ...s, submitChoiceIndex: a.nextIndex },
250
+ effects: [],
251
+ });
252
+ const notesForwardHandler: Handler<"notes_forward"> = (s, a, _c) => ({
253
+ state: s,
254
+ effects: [{ kind: "forward_notes_keystroke", data: a.data }],
255
+ });
256
+ const toggleCollapsedHandler: Handler<"toggle_collapsed"> = (s, _a, _c) => ({
257
+ state: { ...s, collapsed: !s.collapsed },
258
+ effects: [{ kind: "set_overlay_hidden", hidden: !s.collapsed }],
259
+ });
260
+ const ignoreHandler: Handler<"ignore"> = (s, _a, _c) => ({ state: s, effects: [] });
261
+
262
+ /**
263
+ * Compile-time-exhaustive dispatch table. `{ [K in Kind]: Handler<K> }` requires
264
+ * an entry per union member — adding a new `QuestionnaireAction` variant fails to
265
+ * compile here until a handler is registered, mirroring the `Record<RowKind, …>`
266
+ * pattern used by `ROW_INTENT_META`.
267
+ */
268
+ const HANDLERS: { [K in QuestionnaireAction["kind"]]: Handler<K> } = {
269
+ nav: navHandler,
270
+ tab_switch: tabSwitchHandler,
271
+ confirm: confirmHandler,
272
+ toggle: toggleHandler,
273
+ multi_confirm: multiConfirmHandler,
274
+ cancel: cancelHandler,
275
+ notes_enter: notesEnterHandler,
276
+ notes_exit: notesExitHandler,
277
+ notes_forward: notesForwardHandler,
278
+ submit: submitHandler,
279
+ submit_nav: submitNavHandler,
280
+ toggle_collapsed: toggleCollapsedHandler,
281
+ ignore: ignoreHandler,
282
+ };
283
+
284
+ /**
285
+ * Pure reducer: (state, action, ctx) → (state, Effect[]). Mirrors `rpiv-todo`'s `applyTaskMutation`.
286
+ * Delegates to `HANDLERS` — per-kind handlers above are pure, named, and individually testable.
287
+ * `ignore` is also handled outside the reducer by `handleIgnoreInline` in the runtime fast path.
288
+ */
289
+ export function reduce(state: QuestionnaireState, action: QuestionnaireAction, ctx: ApplyContext): ApplyResult {
290
+ const handler = HANDLERS[action.kind] as Handler<typeof action.kind>;
291
+ return handler(state, action as never, ctx);
292
+ }
@@ -0,0 +1,55 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/state/state.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import type { QuestionAnswer, QuestionData } from "../tool/types.js";
4
+ import type { WrappingSelectItem } from "../view/components/wrapping-select.js";
5
+
6
+ /**
7
+ * Canonical state for the questionnaire dialog. Single source of truth — both the
8
+ * dispatcher (`routeKey`) and the view layer read this same shape.
9
+ */
10
+ export interface QuestionnaireState {
11
+ currentTab: number;
12
+ optionIndex: number;
13
+ inputMode: boolean;
14
+ notesVisible: boolean;
15
+ answers: ReadonlyMap<number, QuestionAnswer>;
16
+ multiSelectChecked: ReadonlySet<number>;
17
+ /**
18
+ * Pre-answer notes side-band, keyed by tab index. Decoupled from `answers` so adding
19
+ * notes does NOT mark a question answered (the Submit-tab missing-check would falsely
20
+ * pass otherwise). Merged into the answer at confirm time.
21
+ */
22
+ notesByTab: ReadonlyMap<number, string>;
23
+ /** Focused row in the Submit-tab picker (0 = Submit, 1 = Cancel). Reset on tab switch. */
24
+ submitChoiceIndex: number;
25
+ /** Canonical mirror of the in-flight notes editor; runtime mirrors after `forward_notes_keystroke`. */
26
+ notesDraft: string;
27
+ /**
28
+ * Collapsed mode: the questionnaire shrinks to a single hint row so the agent transcript
29
+ * behind the bottom-anchored overlay becomes readable. Toggled by `Ctrl+]` from any state;
30
+ * while true, every keystroke except cancel is swallowed (see `routeKey`). The overlay
31
+ * stays focused so the expand key never falls through to an underlying overlay (e.g.
32
+ * `/btw`) — that focus-restore property is the entire reason we render a collapsed row
33
+ * instead of calling `OverlayHandle.setHidden(true)`.
34
+ */
35
+ collapsed: boolean;
36
+ }
37
+
38
+ /**
39
+ * Per-tick context the dispatcher needs alongside canonical state. Held separately
40
+ * because `keybindings` / `inputBuffer` must never reach view setProps consumers.
41
+ */
42
+ export interface QuestionnaireRuntime {
43
+ keybindings: { matches(data: string, name: string): boolean };
44
+ inputBuffer: string;
45
+ questions: readonly QuestionData[];
46
+ isMulti: boolean;
47
+ currentItem: WrappingSelectItem | undefined;
48
+ items: readonly WrappingSelectItem[];
49
+ /**
50
+ * Key spec for the collapse/expand shortcut, e.g. `"ctrl+]"` or `"alt+o"`. Resolved
51
+ * from `AskUserQuestionConfig.collapseKey` (or the package default). When `"off"`,
52
+ * the collapse shortcut is disabled.
53
+ */
54
+ collapseKey: string;
55
+ }
@@ -0,0 +1,31 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/tool/format-answer.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import type { QuestionAnswer } from "./types.js";
4
+
5
+ /**
6
+ * Placeholder for empty / null answer text. Used uniformly across both variants — the
7
+ * earlier `(no answer)` fallback in the dialog summary was accidental drift; tests pin
8
+ * `(no input)` only.
9
+ */
10
+ export const NO_INPUT_PLACEHOLDER = "(no input)";
11
+
12
+ export type FormatAnswerVariant = "summary" | "envelope";
13
+
14
+ /**
15
+ * Format a `QuestionAnswer` to its scalar string form. `variant` is currently unused
16
+ * across all branches (the chat branch that once distinguished `envelope` from
17
+ * `summary` has been removed); it is retained on the signature for stability.
18
+ * The `kind: "custom"` empty-string handling and the option fallback both unify on
19
+ * `NO_INPUT_PLACEHOLDER`. Switch is exhaustive — non-`void` return enforces every
20
+ * variant is handled.
21
+ */
22
+ export function formatAnswerScalar(a: QuestionAnswer, _variant: FormatAnswerVariant): string {
23
+ switch (a.kind) {
24
+ case "multi":
25
+ return a.selected && a.selected.length > 0 ? a.selected.join(", ") : NO_INPUT_PLACEHOLDER;
26
+ case "custom":
27
+ return a.answer && a.answer.length > 0 ? a.answer : NO_INPUT_PLACEHOLDER;
28
+ case "option":
29
+ return a.answer ?? NO_INPUT_PLACEHOLDER;
30
+ }
31
+ }
@@ -0,0 +1,49 @@
1
+ // GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/tool/response-envelope.ts
2
+ // Regenerate with: python3 scripts/build-adapters.py
3
+ import { formatAnswerScalar } from "./format-answer.js";
4
+ import type { QuestionAnswer, QuestionnaireResult, QuestionParams } from "./types.js";
5
+
6
+ export const DECLINE_MESSAGE = "User declined to answer questions";
7
+ export const ENVELOPE_PREFIX = "User has answered your questions:";
8
+ export const ENVELOPE_SUFFIX = "You can now continue with the user's answers in mind.";
9
+
10
+ /**
11
+ * Map a `QuestionnaireResult` (or null/cancelled) to the LLM-facing tool envelope.
12
+ * Pure of `(result, params)`; cancelled and "no segments" both fall to `DECLINE_MESSAGE`
13
+ * so the model sees a single canonical "didn't answer" signal regardless of why.
14
+ */
15
+ export function buildQuestionnaireResponse(result: QuestionnaireResult | null | undefined, params: QuestionParams) {
16
+ if (!result || result.cancelled) {
17
+ return buildToolResult(DECLINE_MESSAGE, {
18
+ answers: result?.answers ?? [],
19
+ cancelled: true,
20
+ });
21
+ }
22
+ const segments: string[] = [];
23
+ for (let i = 0; i < params.questions.length; i++) {
24
+ const a = result.answers.find((x) => x.questionIndex === i);
25
+ if (a) segments.push(buildAnswerSegment(a));
26
+ }
27
+ if (segments.length === 0) {
28
+ return buildToolResult(DECLINE_MESSAGE, { answers: result.answers, cancelled: true });
29
+ }
30
+ return buildToolResult(`${ENVELOPE_PREFIX} ${segments.join(" ")} ${ENVELOPE_SUFFIX}`, result);
31
+ }
32
+
33
+ /**
34
+ * Format a single answer segment for the envelope. Pure of `a`. The `"Q"="A"` shape and
35
+ * the optional `selected preview:` / `user notes:` suffixes are pinned by envelope tests.
36
+ */
37
+ export function buildAnswerSegment(a: QuestionAnswer): string {
38
+ const parts: string[] = [`"${a.question}"="${formatAnswerScalar(a, "envelope")}"`];
39
+ if (a.preview && a.preview.length > 0) parts.push(`selected preview: ${a.preview}`);
40
+ if (a.notes && a.notes.length > 0) parts.push(`user notes: ${a.notes}`);
41
+ return `${parts.join(". ")}.`;
42
+ }
43
+
44
+ export function buildToolResult(text: string, details: QuestionnaireResult) {
45
+ return {
46
+ content: [{ type: "text" as const, text }],
47
+ details,
48
+ };
49
+ }