@sun-asterisk/sungen 2.7.0-beta.1 → 3.0.0-beta.72

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 (309) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/commands/add.js +3 -3
  3. package/dist/cli/commands/add.js.map +1 -1
  4. package/dist/cli/commands/audit.d.ts +3 -0
  5. package/dist/cli/commands/audit.d.ts.map +1 -0
  6. package/dist/cli/commands/audit.js +134 -0
  7. package/dist/cli/commands/audit.js.map +1 -0
  8. package/dist/cli/commands/blindspot.d.ts +3 -0
  9. package/dist/cli/commands/blindspot.d.ts.map +1 -0
  10. package/dist/cli/commands/blindspot.js +58 -0
  11. package/dist/cli/commands/blindspot.js.map +1 -0
  12. package/dist/cli/commands/capability.d.ts +3 -0
  13. package/dist/cli/commands/capability.d.ts.map +1 -0
  14. package/dist/cli/commands/capability.js +196 -0
  15. package/dist/cli/commands/capability.js.map +1 -0
  16. package/dist/cli/commands/challenge.d.ts +3 -0
  17. package/dist/cli/commands/challenge.d.ts.map +1 -0
  18. package/dist/cli/commands/challenge.js +102 -0
  19. package/dist/cli/commands/challenge.js.map +1 -0
  20. package/dist/cli/commands/feedback.d.ts +3 -0
  21. package/dist/cli/commands/feedback.d.ts.map +1 -0
  22. package/dist/cli/commands/feedback.js +72 -0
  23. package/dist/cli/commands/feedback.js.map +1 -0
  24. package/dist/cli/commands/flow-check.d.ts +3 -0
  25. package/dist/cli/commands/flow-check.d.ts.map +1 -0
  26. package/dist/cli/commands/flow-check.js +136 -0
  27. package/dist/cli/commands/flow-check.js.map +1 -0
  28. package/dist/cli/commands/generate.d.ts.map +1 -1
  29. package/dist/cli/commands/generate.js +50 -2
  30. package/dist/cli/commands/generate.js.map +1 -1
  31. package/dist/cli/commands/ledger.d.ts +3 -0
  32. package/dist/cli/commands/ledger.d.ts.map +1 -0
  33. package/dist/cli/commands/ledger.js +71 -0
  34. package/dist/cli/commands/ledger.js.map +1 -0
  35. package/dist/cli/commands/manifest.d.ts +3 -0
  36. package/dist/cli/commands/manifest.d.ts.map +1 -0
  37. package/dist/cli/commands/manifest.js +101 -0
  38. package/dist/cli/commands/manifest.js.map +1 -0
  39. package/dist/cli/commands/script-check.d.ts +3 -0
  40. package/dist/cli/commands/script-check.d.ts.map +1 -0
  41. package/dist/cli/commands/script-check.js +97 -0
  42. package/dist/cli/commands/script-check.js.map +1 -0
  43. package/dist/cli/commands/trace.d.ts +3 -0
  44. package/dist/cli/commands/trace.d.ts.map +1 -0
  45. package/dist/cli/commands/trace.js +110 -0
  46. package/dist/cli/commands/trace.js.map +1 -0
  47. package/dist/cli/commands/update.d.ts.map +1 -1
  48. package/dist/cli/commands/update.js +22 -9
  49. package/dist/cli/commands/update.js.map +1 -1
  50. package/dist/cli/index.js +20 -0
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +1 -0
  53. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  54. package/dist/generators/test-generator/adapters/adapter-registry.d.ts +13 -0
  55. package/dist/generators/test-generator/adapters/adapter-registry.d.ts.map +1 -1
  56. package/dist/generators/test-generator/adapters/adapter-registry.js +73 -1
  57. package/dist/generators/test-generator/adapters/adapter-registry.js.map +1 -1
  58. package/dist/generators/test-generator/adapters/index.d.ts +1 -1
  59. package/dist/generators/test-generator/adapters/index.d.ts.map +1 -1
  60. package/dist/generators/test-generator/adapters/index.js +5 -1
  61. package/dist/generators/test-generator/adapters/index.js.map +1 -1
  62. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
  63. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
  64. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +6 -0
  65. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  66. package/dist/generators/test-generator/code-generator.js +6 -2
  67. package/dist/generators/test-generator/code-generator.js.map +1 -1
  68. package/dist/generators/test-generator/patterns/capture-patterns.d.ts +16 -0
  69. package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +1 -0
  70. package/dist/generators/test-generator/patterns/capture-patterns.js +54 -0
  71. package/dist/generators/test-generator/patterns/capture-patterns.js.map +1 -0
  72. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  73. package/dist/generators/test-generator/patterns/form-patterns.js +3 -1
  74. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  75. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  76. package/dist/generators/test-generator/patterns/index.js +2 -0
  77. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  78. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  79. package/dist/generators/test-generator/step-mapper.js +1 -0
  80. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  81. package/dist/generators/test-generator/utils/data-resolver.d.ts +5 -0
  82. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  83. package/dist/generators/test-generator/utils/data-resolver.js +17 -0
  84. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  85. package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts.map +1 -1
  86. package/dist/generators/test-generator/utils/runtime-data-transformer.js +4 -0
  87. package/dist/generators/test-generator/utils/runtime-data-transformer.js.map +1 -1
  88. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  89. package/dist/generators/test-generator/utils/selector-resolver.js +12 -6
  90. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  91. package/dist/harness/audit.d.ts +24 -0
  92. package/dist/harness/audit.d.ts.map +1 -0
  93. package/dist/harness/audit.js +115 -0
  94. package/dist/harness/audit.js.map +1 -0
  95. package/dist/harness/blindspot.d.ts +15 -0
  96. package/dist/harness/blindspot.d.ts.map +1 -0
  97. package/dist/harness/blindspot.js +85 -0
  98. package/dist/harness/blindspot.js.map +1 -0
  99. package/dist/harness/capability-plan.d.ts +49 -0
  100. package/dist/harness/capability-plan.d.ts.map +1 -0
  101. package/dist/harness/capability-plan.js +215 -0
  102. package/dist/harness/capability-plan.js.map +1 -0
  103. package/dist/harness/capability.d.ts +23 -0
  104. package/dist/harness/capability.d.ts.map +1 -0
  105. package/dist/harness/capability.js +98 -0
  106. package/dist/harness/capability.js.map +1 -0
  107. package/dist/harness/catalog/drivers.yaml +57 -0
  108. package/dist/harness/catalog/universal-viewpoints.yaml +114 -0
  109. package/dist/harness/challenge.d.ts +21 -0
  110. package/dist/harness/challenge.d.ts.map +1 -0
  111. package/dist/harness/challenge.js +151 -0
  112. package/dist/harness/challenge.js.map +1 -0
  113. package/dist/harness/feedback.d.ts +29 -0
  114. package/dist/harness/feedback.d.ts.map +1 -0
  115. package/dist/harness/feedback.js +106 -0
  116. package/dist/harness/feedback.js.map +1 -0
  117. package/dist/harness/flow-check.d.ts +23 -0
  118. package/dist/harness/flow-check.d.ts.map +1 -0
  119. package/dist/harness/flow-check.js +132 -0
  120. package/dist/harness/flow-check.js.map +1 -0
  121. package/dist/harness/flow-plan.d.ts +23 -0
  122. package/dist/harness/flow-plan.d.ts.map +1 -0
  123. package/dist/harness/flow-plan.js +166 -0
  124. package/dist/harness/flow-plan.js.map +1 -0
  125. package/dist/harness/intent.d.ts +11 -0
  126. package/dist/harness/intent.d.ts.map +1 -0
  127. package/dist/harness/intent.js +86 -0
  128. package/dist/harness/intent.js.map +1 -0
  129. package/dist/harness/ledger.d.ts +42 -0
  130. package/dist/harness/ledger.d.ts.map +1 -0
  131. package/dist/harness/ledger.js +171 -0
  132. package/dist/harness/ledger.js.map +1 -0
  133. package/dist/harness/manifest.d.ts +42 -0
  134. package/dist/harness/manifest.d.ts.map +1 -0
  135. package/dist/harness/manifest.js +209 -0
  136. package/dist/harness/manifest.js.map +1 -0
  137. package/dist/harness/parse.d.ts +22 -0
  138. package/dist/harness/parse.d.ts.map +1 -0
  139. package/dist/harness/parse.js +163 -0
  140. package/dist/harness/parse.js.map +1 -0
  141. package/dist/harness/script-check.d.ts +39 -0
  142. package/dist/harness/script-check.d.ts.map +1 -0
  143. package/dist/harness/script-check.js +251 -0
  144. package/dist/harness/script-check.js.map +1 -0
  145. package/dist/harness/secret-scan.d.ts +8 -0
  146. package/dist/harness/secret-scan.d.ts.map +1 -0
  147. package/dist/harness/secret-scan.js +88 -0
  148. package/dist/harness/secret-scan.js.map +1 -0
  149. package/dist/harness/sensors.d.ts +88 -0
  150. package/dist/harness/sensors.d.ts.map +1 -0
  151. package/dist/harness/sensors.js +232 -0
  152. package/dist/harness/sensors.js.map +1 -0
  153. package/dist/harness/trace.d.ts +31 -0
  154. package/dist/harness/trace.d.ts.map +1 -0
  155. package/dist/harness/trace.js +173 -0
  156. package/dist/harness/trace.js.map +1 -0
  157. package/dist/orchestrator/ai-rules-updater.d.ts +1 -0
  158. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  159. package/dist/orchestrator/ai-rules-updater.js +55 -11
  160. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  161. package/dist/orchestrator/figma/spec-figma-renderer.d.ts +2 -2
  162. package/dist/orchestrator/figma/spec-figma-renderer.js +2 -2
  163. package/dist/orchestrator/figma/spec-figma-section-renderers.d.ts +1 -1
  164. package/dist/orchestrator/figma/spec-figma-section-renderers.js +1 -1
  165. package/dist/orchestrator/project-initializer.d.ts +5 -0
  166. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  167. package/dist/orchestrator/project-initializer.js +30 -6
  168. package/dist/orchestrator/project-initializer.js.map +1 -1
  169. package/dist/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
  170. package/dist/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
  171. package/dist/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
  172. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
  173. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  174. package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +36 -12
  175. package/dist/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
  176. package/dist/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
  177. package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
  178. package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +4 -1
  179. package/dist/orchestrator/templates/ai-instructions/claude-config.md +1 -4
  180. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
  181. package/dist/orchestrator/templates/ai-instructions/{github-skill-sungen-figma-source.md → claude-skill-capture-mode-figma-pat.md} +14 -48
  182. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
  183. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
  184. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
  185. package/dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
  186. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -1
  187. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
  188. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  189. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +18 -10
  190. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
  191. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
  192. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
  193. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +2 -1
  194. package/dist/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
  195. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
  196. package/{src/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
  197. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
  198. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
  199. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
  200. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
  201. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -1
  202. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
  203. package/dist/orchestrator/templates/specs-test-data.ts +20 -6
  204. package/dist/tools/figma/figma-auth.d.ts +5 -2
  205. package/dist/tools/figma/figma-auth.d.ts.map +1 -1
  206. package/dist/tools/figma/figma-auth.js +19 -9
  207. package/dist/tools/figma/figma-auth.js.map +1 -1
  208. package/docs/orchestration-spec.md +267 -0
  209. package/package.json +12 -7
  210. package/src/cli/commands/add.ts +3 -3
  211. package/src/cli/commands/audit.ts +92 -0
  212. package/src/cli/commands/blindspot.ts +48 -0
  213. package/src/cli/commands/capability.ts +160 -0
  214. package/src/cli/commands/challenge.ts +55 -0
  215. package/src/cli/commands/feedback.ts +65 -0
  216. package/src/cli/commands/flow-check.ts +97 -0
  217. package/src/cli/commands/generate.ts +47 -2
  218. package/src/cli/commands/ledger.ts +61 -0
  219. package/src/cli/commands/manifest.ts +55 -0
  220. package/src/cli/commands/script-check.ts +50 -0
  221. package/src/cli/commands/trace.ts +60 -0
  222. package/src/cli/commands/update.ts +30 -10
  223. package/src/cli/index.ts +20 -0
  224. package/src/generators/test-generator/adapters/adapter-interface.ts +1 -0
  225. package/src/generators/test-generator/adapters/adapter-registry.ts +37 -0
  226. package/src/generators/test-generator/adapters/index.ts +4 -1
  227. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
  228. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
  229. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +6 -0
  230. package/src/generators/test-generator/code-generator.ts +6 -2
  231. package/src/generators/test-generator/patterns/capture-patterns.ts +59 -0
  232. package/src/generators/test-generator/patterns/form-patterns.ts +3 -1
  233. package/src/generators/test-generator/patterns/index.ts +2 -0
  234. package/src/generators/test-generator/step-mapper.ts +1 -0
  235. package/src/generators/test-generator/utils/data-resolver.ts +20 -0
  236. package/src/generators/test-generator/utils/runtime-data-transformer.ts +8 -0
  237. package/src/generators/test-generator/utils/selector-resolver.ts +13 -6
  238. package/src/harness/audit.ts +112 -0
  239. package/src/harness/blindspot.ts +51 -0
  240. package/src/harness/capability-plan.ts +180 -0
  241. package/src/harness/capability.ts +75 -0
  242. package/src/harness/catalog/drivers.yaml +57 -0
  243. package/src/harness/catalog/universal-viewpoints.yaml +114 -0
  244. package/src/harness/challenge.ts +131 -0
  245. package/src/harness/feedback.ts +84 -0
  246. package/src/harness/flow-check.ts +99 -0
  247. package/src/harness/flow-plan.ts +135 -0
  248. package/src/harness/intent.ts +58 -0
  249. package/src/harness/ledger.ts +155 -0
  250. package/src/harness/manifest.ts +173 -0
  251. package/src/harness/parse.ts +145 -0
  252. package/src/harness/script-check.ts +222 -0
  253. package/src/harness/secret-scan.ts +51 -0
  254. package/src/harness/sensors.ts +279 -0
  255. package/src/harness/trace.ts +138 -0
  256. package/src/orchestrator/ai-rules-updater.ts +57 -10
  257. package/src/orchestrator/figma/spec-figma-renderer.ts +2 -2
  258. package/src/orchestrator/figma/spec-figma-section-renderers.ts +1 -1
  259. package/src/orchestrator/project-initializer.ts +33 -7
  260. package/src/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
  261. package/src/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
  262. package/src/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
  263. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
  264. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  265. package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +36 -12
  266. package/src/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
  267. package/src/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
  268. package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
  269. package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +4 -1
  270. package/src/orchestrator/templates/ai-instructions/claude-config.md +1 -4
  271. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
  272. package/{dist/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md → src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-pat.md} +14 -48
  273. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
  274. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
  275. package/src/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
  276. package/src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
  277. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -1
  278. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
  279. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  280. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +18 -10
  281. package/src/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
  282. package/src/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
  283. package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
  284. package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +2 -1
  285. package/src/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
  286. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
  287. package/{dist/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
  288. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
  289. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
  290. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
  291. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
  292. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -1
  293. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
  294. package/src/orchestrator/templates/specs-test-data.ts +20 -6
  295. package/src/tools/figma/figma-auth.ts +20 -9
  296. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
  297. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
  298. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
  299. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
  300. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
  301. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
  302. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
  303. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
  304. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
  305. package/src/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md +0 -151
  306. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
  307. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
  308. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
  309. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-figma-source.md +0 -151
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "2.7.0-beta.1",
3
+ "version": "3.0.0-beta.72",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,10 +9,11 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc && npm run copy-templates",
12
- "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html",
12
+ "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
13
13
  "build:dashboard": "cd dashboard && npm install --silent && npm run build && cd .. && cp dashboard/dist/index.html src/dashboard/templates/index.html",
14
14
  "dev": "tsx src/cli/index.ts",
15
- "test": "echo \"Error: no test specified\" && exit 1",
15
+ "test": "tsx tests/golden/run.ts",
16
+ "test:update": "tsx tests/golden/run.ts --update",
16
17
  "prepublishOnly": "npm run build:dashboard && npm run build"
17
18
  },
18
19
  "keywords": [
@@ -26,7 +27,7 @@
26
27
  "automation",
27
28
  "qa"
28
29
  ],
29
- "author": "ee team (engineer-excellence) — Sun Asterisk",
30
+ "author": "eqe team (engineer & quality) — Sun Asterisk",
30
31
  "license": "MIT",
31
32
  "engines": {
32
33
  "node": ">=18.0.0"
@@ -47,7 +48,7 @@
47
48
  "handlebars": "^4.7.8",
48
49
  "ora": "^9.0.0",
49
50
  "plop": "^4.0.4",
50
- "tsx": "^4.21.0",
51
+ "tsx": "^4.22.4",
51
52
  "yaml": "^2.8.2",
52
53
  "zod": "^4.1.13"
53
54
  },
@@ -67,11 +68,15 @@
67
68
  },
68
69
  "overrides": {
69
70
  "brace-expansion": "^5.0.6",
70
- "uuid": "^11.1.1"
71
+ "uuid": "^11.1.1",
72
+ "tmp": "^0.2.6",
73
+ "esbuild": "^0.28.1"
71
74
  },
72
75
  "resolutions": {
73
76
  "brace-expansion": "^5.0.6",
74
- "uuid": "^11.1.1"
77
+ "uuid": "^11.1.1",
78
+ "tmp": "^0.2.6",
79
+ "esbuild": "^0.28.1"
75
80
  },
76
81
  "files": [
77
82
  "dist",
@@ -66,15 +66,15 @@ export function registerAddCommand(program: Command): void {
66
66
  console.log(` qa/screens/${options.screen}/requirements/spec.md (stub created)`);
67
67
  }
68
68
 
69
- // Loud banner: Tell the calling agent to
70
- // invoke the `sungen-figma-source` skill to synthesize the narrative sections in spec_figma.md, since
69
+ // Loud banner: Tell the calling agent to invoke the `sungen-capture` skill
70
+ // (mode figma-pat) to synthesize the narrative sections in spec_figma.md, since
71
71
  // the CLI cannot do this (synthesis is LLM-driven off the cached raw JSON).
72
72
  console.log('');
73
73
  console.log('==============================================================');
74
74
  console.log(' NEXT STEP (REQUIRED — DO NOT SKIP)');
75
75
  console.log('==============================================================');
76
76
  console.log(' spec_figma.md envelope is written but narrative is EMPTY.');
77
- console.log(' Invoke the `sungen-figma-source` skill NOW to synthesize');
77
+ console.log(' Invoke `sungen-capture` (mode figma-pat) NOW to synthesize');
78
78
  console.log(' the 7 sections (Purpose / ASCII Layout / Regions / Actions /');
79
79
  console.log(' Form Fields / Data Columns / Navigation) below the');
80
80
  console.log(' `<!-- SYNTHESIS-BELOW -->` marker.');
@@ -0,0 +1,92 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { runAudit, AuditReport } from '../../harness/audit';
5
+
6
+ function findScreenDir(name: string): string | null {
7
+ const screen = path.join(process.cwd(), 'qa', 'screens', name);
8
+ if (fs.existsSync(screen)) return screen;
9
+ const flow = path.join(process.cwd(), 'qa', 'flows', name);
10
+ if (fs.existsSync(flow)) return flow;
11
+ return null;
12
+ }
13
+
14
+ function bar(ratio: number, width = 20): string {
15
+ const n = Math.round(ratio * width);
16
+ return '█'.repeat(n) + '░'.repeat(width - n);
17
+ }
18
+
19
+ function render(r: AuditReport): void {
20
+ const L = console.log;
21
+ L('');
22
+ L(`━━━ Harness Audit: ${r.screen} (${r.scenarioCount} scenarios) ━━━`);
23
+ L('');
24
+ L(` Quality score (business-weighted): ${r.score.overall}/10 [Viewpoint Gate: ${r.gateStatus}]`);
25
+ L(` Intent: focus=${r.intent.focus} · depth threshold ${r.depth.threshold.toFixed(2)} (${r.intent.source})`);
26
+ L(` coverage ${bar(r.score.coverage)} ${(r.score.coverage * 100).toFixed(0)}%`);
27
+ L(` businessDepth ${bar(r.score.businessDepth)} ${(r.score.businessDepth * 100).toFixed(0)}% [${r.depth.verdict.toUpperCase()}]`);
28
+ L(` balance ${bar(r.score.balance)} ${(r.score.balance * 100).toFixed(0)}%`);
29
+ L(` traceability ${bar(r.score.traceability)} ${(r.score.traceability * 100).toFixed(0)}%`);
30
+ L(` (${r.score.formula})`);
31
+ L('');
32
+ L(` ① Viewpoint Gate — page-type: ${r.gate.pageType ?? 'unknown'} — themes ${r.gate.themesCovered}/${r.gate.themesTotal} covered`);
33
+ if (r.gate.gaps.length) for (const g of r.gate.gaps) L(` ✗ ${g.status === 'shallow' ? 'SHALLOW' : 'MISSING'}: ${g.theme}`);
34
+ else L(' ✓ all critical themes covered (with data assertions)');
35
+ L('');
36
+ L(` ② Assertion depth — ${r.depth.businessCriticalShallow}/${r.depth.businessCriticalTotal} business-critical scenarios are shallow (visibility/nav only)`);
37
+ for (const s of r.depth.shallowBusinessCritical.slice(0, 8)) L(` ⚠ ${s.category}: ${s.name}`);
38
+ if (r.depth.shallowBusinessCritical.length > 8) L(` … +${r.depth.shallowBusinessCritical.length - 8} more`);
39
+ L('');
40
+ L(` ③ Coverage balance — ${r.balance.imbalanced ? '⚠ IMBALANCED' : '✓ balanced'}`);
41
+ L(` buckets: ${Object.entries(r.balance.byBucket).map(([k, v]) => `${k}=${v}`).join(' ')}`);
42
+ L('');
43
+ L(` ④ Duplicate — ${r.duplicates.clusters.length} same-shape cluster(s), ${r.duplicates.exactDuplicateCount} likely exact dup(s)`);
44
+ for (const c of r.duplicates.clusters.slice(0, 3)) {
45
+ L(` ${c.sameDataLikely ? '✗ exact' : '○ EP/data family'} (${c.scenarios.length}): ${c.scenarios.slice(0, 3).join(' | ')}${c.scenarios.length > 3 ? ' …' : ''}`);
46
+ }
47
+ L('');
48
+ L(` ⑤ Traceability — ${(r.trace.mappedRatio * 100).toFixed(0)}% scenarios linked to viewpoint-overview`);
49
+ L(` ${r.trace.note}`);
50
+ L('');
51
+ L(' ── Findings (Repair targets) ──');
52
+ if (r.findings.length === 0) L(' ✓ none — output passes the harness');
53
+ for (const f of r.findings) L(` • ${f}`);
54
+ L('');
55
+ }
56
+
57
+ export function registerAuditCommand(program: Command): void {
58
+ program
59
+ .command('audit')
60
+ .description('Harness: measure test-design quality (viewpoint gate, depth, balance, duplicates, traceability)')
61
+ .option('-s, --screen <name>', 'Screen or flow name to audit')
62
+ .option('--json', 'Output the raw JSON report')
63
+ .action((options) => {
64
+ try {
65
+ const name = options.screen;
66
+ if (!name) throw new Error('Provide --screen <name>');
67
+ const dir = findScreenDir(name);
68
+ if (!dir) throw new Error(`Screen/flow not found: qa/screens/${name} or qa/flows/${name}`);
69
+
70
+ const report = runAudit(dir, name);
71
+
72
+ // Persist report under .sungen/reports/
73
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
74
+ fs.mkdirSync(outDir, { recursive: true });
75
+ const outPath = path.join(outDir, `${name}-audit.json`);
76
+ fs.writeFileSync(outPath, JSON.stringify(report, null, 2), 'utf-8');
77
+
78
+ if (options.json) {
79
+ console.log(JSON.stringify(report, null, 2));
80
+ } else {
81
+ render(report);
82
+ console.log(` Report: ${path.relative(process.cwd(), outPath)}`);
83
+ console.log('');
84
+ }
85
+ // Non-zero exit when the gate fails — usable in CI / repair loop.
86
+ process.exit(report.gateStatus === 'FAIL' ? 2 : 0);
87
+ } catch (error) {
88
+ console.error('Error:', error instanceof Error ? error.message : error);
89
+ process.exit(1);
90
+ }
91
+ });
92
+ }
@@ -0,0 +1,48 @@
1
+ import { Command } from 'commander';
2
+ import { addBlindspot, listBlindspots, blindspotsForPrompt } from '../../harness/blindspot';
3
+
4
+ export function registerBlindspotCommand(program: Command): void {
5
+ const bs = program
6
+ .command('blindspot')
7
+ .description('Blind-Spot Memory: reusable patterns so the harness stops repeating a known miss');
8
+
9
+ bs
10
+ .command('add')
11
+ .description('Record a blind-spot pattern (promote a recurring miss to a reusable rule)')
12
+ .requiredOption('--pattern <name>', 'Short pattern name, e.g. "add-action-no-duplicate-check"')
13
+ .requiredOption('--rule <text>', 'The generalised rule to apply next time')
14
+ .option('--example <text>', 'A concrete instance that motivated it')
15
+ .option('--screen <name>', 'Where it was first noticed')
16
+ .option('--source <who>', 'qa | challenge | feedback', 'qa')
17
+ .action((o) => {
18
+ try {
19
+ addBlindspot({ pattern: o.pattern, rule: o.rule, example: o.example, screen: o.screen, source: o.source });
20
+ console.log(`✓ blindspot recorded: [${o.pattern}]`);
21
+ } catch (e) {
22
+ console.error('Error:', e instanceof Error ? e.message : e);
23
+ process.exit(1);
24
+ }
25
+ });
26
+
27
+ bs
28
+ .command('list')
29
+ .description('List recorded blind-spot patterns')
30
+ .option('--prompt', 'Output the compact bullet form used in generator/critic prompts')
31
+ .action((o) => {
32
+ try {
33
+ if (o.prompt) { const s = blindspotsForPrompt(); console.log(s || '(none)'); return; }
34
+ const all = listBlindspots();
35
+ if (!all.length) { console.log('No blind-spots recorded yet. Add one with `sungen blindspot add`.'); return; }
36
+ console.log(`\n━━━ Blind-Spot Memory (${all.length}) ━━━`);
37
+ for (const b of all) {
38
+ console.log(` • [${b.pattern}]${b.screen ? ` (${b.screen})` : ''}`);
39
+ console.log(` rule: ${b.rule}`);
40
+ if (b.example) console.log(` e.g.: ${b.example}`);
41
+ }
42
+ console.log('');
43
+ } catch (e) {
44
+ console.error('Error:', e instanceof Error ? e.message : e);
45
+ process.exit(1);
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,160 @@
1
+ import { Command } from 'commander';
2
+ import { spawnSync } from 'child_process';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import {
6
+ readCapabilities, writeCapabilities, loadDriverCatalog, driverMeta,
7
+ } from '../../harness/capability';
8
+ import { buildPlan, MANUAL_REASONS } from '../../harness/capability-plan';
9
+ import { adapterRegistry } from '../../generators/test-generator/adapters';
10
+
11
+ function findScreenDir(name: string): string | null {
12
+ const s = path.join(process.cwd(), 'qa', 'screens', name);
13
+ if (fs.existsSync(s)) return s;
14
+ const f = path.join(process.cwd(), 'qa', 'flows', name);
15
+ if (fs.existsSync(f)) return f;
16
+ return null;
17
+ }
18
+
19
+ export function registerCapabilityCommand(program: Command): void {
20
+ const cap = program
21
+ .command('capability')
22
+ .description('Manage execution drivers (platform + capability) — opt-in, installed only on consent');
23
+
24
+ // ── list ────────────────────────────────────────────────────────────────
25
+ cap
26
+ .command('list')
27
+ .description('Show the project platform + enabled drivers + the available driver catalog')
28
+ .action(() => {
29
+ try {
30
+ const cwd = process.cwd();
31
+ const profile = readCapabilities(cwd);
32
+ const catalog = loadDriverCatalog();
33
+ console.log('\n━━━ Capabilities ━━━');
34
+ console.log(` platform: ${profile.platform ?? '(none — run `sungen capability add web|mobile`)'}`);
35
+ console.log(` enabled: ${profile.enabled.length ? profile.enabled.join(', ') : '(none)'} [${profile.source}]`);
36
+ console.log('\n Available drivers (install with `sungen capability add <id>`):');
37
+ for (const d of Object.values(catalog)) {
38
+ const on = profile.enabled.includes(d.id) ? '●' : '○';
39
+ const unblocks = d.unblocks?.length ? ` unblocks ${d.unblocks.join('/')}` : '';
40
+ console.log(` ${on} ${d.id.padEnd(13)} [${d.kind}] ${d.package}${unblocks}`);
41
+ }
42
+ console.log('');
43
+ } catch (e) {
44
+ console.error('Error:', e instanceof Error ? e.message : e);
45
+ process.exit(1);
46
+ }
47
+ });
48
+
49
+ // ── plan (Planner — recommend-only, no install) ──────────────────────────
50
+ cap
51
+ .command('plan')
52
+ .description('Capability Planner: classify execution modes + @manual reasons (M1–M9) + recommend drivers (no install)')
53
+ .requiredOption('-s, --screen <name>', 'Screen or flow name')
54
+ .option('--json', 'Output the raw plan JSON')
55
+ .action((o: { screen: string; json?: boolean }) => {
56
+ try {
57
+ const dir = findScreenDir(o.screen);
58
+ if (!dir) throw new Error(`Screen/flow not found: qa/screens/${o.screen} or qa/flows/${o.screen}`);
59
+ const plan = buildPlan(dir, o.screen);
60
+
61
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
62
+ fs.mkdirSync(outDir, { recursive: true });
63
+ const outPath = path.join(outDir, `${o.screen}-plan.json`);
64
+ fs.writeFileSync(outPath, JSON.stringify(plan, null, 2), 'utf-8');
65
+ if (o.json) { console.log(JSON.stringify(plan, null, 2)); return; }
66
+
67
+ const L = console.log;
68
+ L(`\n━━━ Capability Plan: ${plan.screen} (${plan.total} scenarios) ━━━`);
69
+ L(` Execution modes: ${Object.entries(plan.modes).map(([m, n]) => `${m} ${n}`).join(' · ')}`);
70
+ const reasons = Object.entries(plan.byReason).sort().map(([c, n]) => `${c}×${n}`).join(' ') || '(no manual)';
71
+ L(` Manual by reason: ${reasons}`);
72
+ if (plan.modes['manual']) {
73
+ L(` capability-manual: ${plan.capabilityManual} (${plan.capabilityManualPct}%) judgment-manual: ${plan.judgmentManual} cross-screen→flow: ${plan.crossScreen}`);
74
+ }
75
+ if (plan.crossScreen) {
76
+ L(` Cross-screen (XS×${plan.crossScreen}): automate via a flow — \`/sungen:add-flow\` (not a single-screen driver gap).`);
77
+ }
78
+ if (plan.recommendations.length) {
79
+ L(' Recommendations (recommend-only — install with `sungen capability add`):');
80
+ for (const r of plan.recommendations) {
81
+ L(` • ${r.count} @manual:${r.reason} → enable \`${r.driver}\` (${r.pkg}) → would unblock ${r.count}`);
82
+ }
83
+ }
84
+ if (plan.keep.length) {
85
+ L(` Keep manual (judgment floor — do NOT automate): ${plan.keep.map((k) => `${k.code}×${k.count} (${MANUAL_REASONS[k.code].label})`).join(', ')}`);
86
+ }
87
+ if (plan.unclassified.length) {
88
+ L(` ⚠ Unclassified (add @manual:Mx or a clearer reason): ${plan.unclassified.length}`);
89
+ for (const u of plan.unclassified.slice(0, 8)) L(` - ${u.name}`);
90
+ }
91
+ L(`\n Report: ${path.relative(process.cwd(), outPath)}\n`);
92
+ } catch (e) {
93
+ console.error('Error:', e instanceof Error ? e.message : e);
94
+ process.exit(1);
95
+ }
96
+ });
97
+
98
+ // ── add (the consent action) ─────────────────────────────────────────────
99
+ cap
100
+ .command('add <driver>')
101
+ .description('Enable a driver: install its package (if external) + record it in qa/capabilities.yaml')
102
+ .option('--skip-install', 'Only record in the profile; do not run npm install')
103
+ .action((driver: string, o: { skipInstall?: boolean }) => {
104
+ try {
105
+ const cwd = process.cwd();
106
+ const meta = driverMeta(driver);
107
+ if (!meta) {
108
+ throw new Error(`Unknown driver "${driver}". Known: ${Object.keys(loadDriverCatalog()).join(', ')}`);
109
+ }
110
+ const adapterName = meta.adapter || meta.id;
111
+ const bundled = adapterRegistry.hasAdapter(adapterName); // e.g. `web` is bundled in Phase 2a
112
+
113
+ if (!bundled && !o.skipInstall) {
114
+ console.log(`📦 Installing ${meta.package} (dev dependency)...`);
115
+ const r = spawnSync('npm', ['install', '-D', meta.package], { stdio: 'inherit', shell: true });
116
+ if (r.status !== 0) throw new Error(`npm install -D ${meta.package} failed.`);
117
+ } else if (bundled) {
118
+ console.log(`✓ ${driver} is built-in (no install needed).`);
119
+ }
120
+
121
+ // Verify it can load (skip for bundled — already registered).
122
+ if (!bundled) {
123
+ adapterRegistry.loadDriver(adapterName, meta.package, cwd); // throws if not loadable
124
+ }
125
+
126
+ const profile = readCapabilities(cwd);
127
+ const enabled = Array.from(new Set([...profile.enabled, driver]));
128
+ const platform = meta.kind === 'platform' ? driver : profile.platform;
129
+ writeCapabilities(cwd, { platform, enabled });
130
+ console.log(`✓ capability "${driver}" enabled${meta.kind === 'platform' ? ` (platform=${driver})` : ''}.`);
131
+ console.log('');
132
+ } catch (e) {
133
+ console.error('Error:', e instanceof Error ? e.message : e);
134
+ process.exit(1);
135
+ }
136
+ });
137
+
138
+ // ── remove ─────────────────────────────────────────────────────────────
139
+ cap
140
+ .command('remove <driver>')
141
+ .description('Disable a driver in qa/capabilities.yaml (optionally uninstall the package)')
142
+ .option('--uninstall', 'Also run npm uninstall for the driver package')
143
+ .action((driver: string, o: { uninstall?: boolean }) => {
144
+ try {
145
+ const cwd = process.cwd();
146
+ const profile = readCapabilities(cwd);
147
+ const enabled = profile.enabled.filter((d) => d !== driver);
148
+ const platform = profile.platform === driver ? undefined : profile.platform;
149
+ writeCapabilities(cwd, { platform, enabled });
150
+ const meta = driverMeta(driver);
151
+ if (o.uninstall && meta && !adapterRegistry.hasAdapter(meta.adapter || meta.id)) {
152
+ spawnSync('npm', ['uninstall', meta.package], { stdio: 'inherit', shell: true });
153
+ }
154
+ console.log(`✓ capability "${driver}" disabled.`);
155
+ } catch (e) {
156
+ console.error('Error:', e instanceof Error ? e.message : e);
157
+ process.exit(1);
158
+ }
159
+ });
160
+ }
@@ -0,0 +1,55 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { buildChallenge, renderChallengeMarkdown } from '../../harness/challenge';
5
+
6
+ function findScreenDir(name: string): string | null {
7
+ const screen = path.join(process.cwd(), 'qa', 'screens', name);
8
+ if (fs.existsSync(screen)) return screen;
9
+ const flow = path.join(process.cwd(), 'qa', 'flows', name);
10
+ if (fs.existsSync(flow)) return flow;
11
+ return null;
12
+ }
13
+
14
+ export function registerChallengeCommand(program: Command): void {
15
+ program
16
+ .command('challenge')
17
+ .description('Exploration mode (Loop 2): attack the existing suite for blind spots — advisory, never auto-merges')
18
+ .requiredOption('-s, --screen <name>', 'Screen or flow name')
19
+ .option('--json', 'Output the raw JSON report')
20
+ .action((o) => {
21
+ try {
22
+ const dir = findScreenDir(o.screen);
23
+ if (!dir) throw new Error(`Screen/flow not found: qa/screens/${o.screen} or qa/flows/${o.screen}`);
24
+ const report = buildChallenge(dir, o.screen);
25
+
26
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
27
+ fs.mkdirSync(outDir, { recursive: true });
28
+ const md = renderChallengeMarkdown(report);
29
+ const outPath = path.join(outDir, `${o.screen}-challenge.md`);
30
+ fs.writeFileSync(outPath, md, 'utf-8');
31
+
32
+ if (o.json) { console.log(JSON.stringify(report, null, 2)); return; }
33
+
34
+ const L = console.log;
35
+ L(`\n━━━ Challenge (exploration mode): ${report.screen} ━━━`);
36
+ L(' Advisory — does NOT change the official suite; surfaces what production missed.\n');
37
+ L(' ① Depth — title claims a collection but asserts a single element');
38
+ if (report.collectionClaimSingular.length) {
39
+ for (const f of report.collectionClaimSingular) L(` ⚠ ${f.scenario}\n → ${f.suggestion}`);
40
+ } else L(' ✓ none');
41
+ L(' ② Coverage — over-covered / shallow');
42
+ if (report.overCovered.length) for (const o2 of report.overCovered) L(` • ${o2.bucket}: ${o2.note}`);
43
+ if (report.shallowThemes.length) L(` • shallow themes: ${report.shallowThemes.join(', ')}`);
44
+ if (!report.overCovered.length && !report.shallowThemes.length) L(' ✓ balanced');
45
+ L(' ③ Novelty — prompts for the `sungen-challenge` agent (≤20% of official, no auto-merge)');
46
+ for (const p of report.noveltyPrompts) L(` • ${p}`);
47
+ L(' ── Exploration readiness ──');
48
+ for (const e of report.explorationReadiness) L(` • ${e}`);
49
+ L(`\n Report: ${path.relative(process.cwd(), outPath)}\n`);
50
+ } catch (error) {
51
+ console.error('Error:', error instanceof Error ? error.message : error);
52
+ process.exit(1);
53
+ }
54
+ });
55
+ }
@@ -0,0 +1,65 @@
1
+ import { Command } from 'commander';
2
+ import { recordFeedback, summarize, FeedbackType, FeedbackDecision } from '../../harness/feedback';
3
+
4
+ const TYPES = ['test-design', 'product', 'other'];
5
+ const DECISIONS = ['accept', 'reject', 'edit', 'add', 'none'];
6
+
7
+ export function registerFeedbackCommand(program: Command): void {
8
+ const fb = program
9
+ .command('feedback')
10
+ .description('Local-first QA feedback (test-design knowledge + product telemetry). Synced later when a server exists.');
11
+
12
+ fb
13
+ .command('record')
14
+ .description('Record a feedback entry')
15
+ .requiredOption('--message <text>', 'The feedback')
16
+ .option('--type <t>', `Feedback type (${TYPES.join('|')})`, 'test-design')
17
+ .option('-s, --screen <name>', 'Screen/flow the feedback relates to')
18
+ .option('--target <ref>', 'What it is about (viewpoint id / scenario / command / artifact)')
19
+ .option('--decision <d>', `QA decision (${DECISIONS.join('|')})`, 'none')
20
+ .option('--reason <text>', 'Why')
21
+ .option('--source <who>', 'Who gave the feedback', 'qa')
22
+ .action((o) => {
23
+ try {
24
+ if (!TYPES.includes(o.type)) throw new Error(`--type must be one of ${TYPES.join(', ')}`);
25
+ if (!DECISIONS.includes(o.decision)) throw new Error(`--decision must be one of ${DECISIONS.join(', ')}`);
26
+ const p = recordFeedback({
27
+ type: o.type as FeedbackType,
28
+ screen: o.screen,
29
+ target: o.target,
30
+ decision: o.decision as FeedbackDecision,
31
+ message: o.message,
32
+ reason: o.reason,
33
+ source: o.source,
34
+ });
35
+ console.log(`✓ feedback recorded (${o.type}${o.screen ? ' · ' + o.screen : ''}) → ${p}`);
36
+ } catch (e) {
37
+ console.error('Error:', e instanceof Error ? e.message : e);
38
+ process.exit(1);
39
+ }
40
+ });
41
+
42
+ fb
43
+ .command('list')
44
+ .description('List / summarise recorded feedback')
45
+ .option('-s, --screen <name>', 'Filter by screen')
46
+ .option('--type <t>', 'Filter by type')
47
+ .option('--json', 'Output raw JSON')
48
+ .action((o) => {
49
+ try {
50
+ const s = summarize({ screen: o.screen, type: o.type });
51
+ if (o.json) { console.log(JSON.stringify(s, null, 2)); return; }
52
+ console.log(`\n━━━ Feedback (${s.total}) ━━━`);
53
+ console.log(` by type: ${Object.entries(s.byType).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}`);
54
+ console.log(` by decision: ${Object.entries(s.byDecision).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}\n`);
55
+ for (const e of s.entries.slice(-20)) {
56
+ console.log(` [${e.type}${e.decision && e.decision !== 'none' ? '/' + e.decision : ''}] ${e.screen ? e.screen + ' · ' : ''}${e.target ? e.target + ' — ' : ''}${e.message}`);
57
+ if (e.reason) console.log(` reason: ${e.reason}`);
58
+ }
59
+ console.log('');
60
+ } catch (e) {
61
+ console.error('Error:', e instanceof Error ? e.message : e);
62
+ process.exit(1);
63
+ }
64
+ });
65
+ }
@@ -0,0 +1,97 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { buildFlowCheck } from '../../harness/flow-check';
5
+ import { buildFlowPlan } from '../../harness/flow-plan';
6
+
7
+ export function registerFlowCheckCommand(program: Command): void {
8
+ registerFlowPlan(program);
9
+ program
10
+ .command('flow-check')
11
+ .description('Flow first-class: verify screen→flow deferrals are really covered + surface the run-test contract')
12
+ .option('--flow <name>', 'Limit the run-test contract checks to one flow')
13
+ .option('--json', 'Output the raw JSON report')
14
+ .action((o: { flow?: string; json?: boolean }) => {
15
+ try {
16
+ const cwd = process.cwd();
17
+ const r = buildFlowCheck(cwd, o.flow);
18
+
19
+ const outDir = path.join(cwd, '.sungen', 'reports');
20
+ fs.mkdirSync(outDir, { recursive: true });
21
+ fs.writeFileSync(path.join(outDir, 'flow-check.json'), JSON.stringify(r, null, 2), 'utf-8');
22
+ if (o.json) { console.log(JSON.stringify(r, null, 2)); return; }
23
+
24
+ const L = console.log;
25
+ L('\n━━━ Flow Check ━━━');
26
+ L(` screens: ${r.screens.join(', ') || '(none)'} flows: ${r.flows.join(', ') || '(none)'}`);
27
+
28
+ L('\n ① Deferral integrity (screen → flow)');
29
+ if (!r.deferrals.length) L(' (no "deferred to a flow" @manual scenarios)');
30
+ for (const d of r.deferrals) {
31
+ const icon = d.verdict === 'covered' ? '✓' : d.verdict === 'shallow' ? '◐' : '✗';
32
+ const note = d.verdict === 'missing'
33
+ ? `NO flow covers "${d.targets.join('/')}" → false deferral (real manual gap)`
34
+ : `${d.verdict} by ${d.via}`;
35
+ L(` ${icon} ${d.screen} · ${d.scenario.slice(0, 60)} → ${note}`);
36
+ }
37
+
38
+ L('\n ② Run-test contract (cross-screen proofs)');
39
+ if (!r.contracts.length) L(' (no contract risks detected)');
40
+ for (const c of r.contracts) {
41
+ L(` ⚠ ${c.kind}${c.scenario ? ` · ${c.scenario.slice(0, 55)}` : ` · ${c.flow}`}\n ${c.message}`);
42
+ }
43
+
44
+ L(`\n Report: ${path.relative(cwd, path.join(outDir, 'flow-check.json'))}`);
45
+ if (r.missing > 0) {
46
+ L(`\n ✗ ${r.missing} false deferral(s) — these XS scenarios are NOT actually flow-covered. Add a flow or re-classify.\n`);
47
+ process.exit(2);
48
+ }
49
+ L('');
50
+ } catch (e) {
51
+ console.error('Error:', e instanceof Error ? e.message : e);
52
+ process.exit(1);
53
+ }
54
+ });
55
+ }
56
+
57
+ function registerFlowPlan(program: Command): void {
58
+ program
59
+ .command('flow-plan')
60
+ .description('Flow Capability Planner: decompose a flow into legs + selector readiness + contract + run-test plan')
61
+ .requiredOption('--flow <name>', 'Flow name')
62
+ .option('--json', 'Output the raw JSON plan')
63
+ .action((o: { flow: string; json?: boolean }) => {
64
+ try {
65
+ const cwd = process.cwd();
66
+ if (!fs.existsSync(path.join(cwd, 'qa', 'flows', o.flow))) throw new Error(`Flow not found: qa/flows/${o.flow}`);
67
+ const p = buildFlowPlan(cwd, o.flow);
68
+
69
+ const outDir = path.join(cwd, '.sungen', 'reports');
70
+ fs.mkdirSync(outDir, { recursive: true });
71
+ const outPath = path.join(outDir, `${o.flow}-flow-plan.json`);
72
+ fs.writeFileSync(outPath, JSON.stringify(p, null, 2), 'utf-8');
73
+ if (o.json) { console.log(JSON.stringify(p, null, 2)); return; }
74
+
75
+ const L = console.log;
76
+ L(`\n━━━ Flow Plan: ${p.flow} (${p.total} scenarios) ━━━`);
77
+ L(' Legs (screen → selector readiness):');
78
+ for (const leg of p.legs) {
79
+ const icon = leg.readiness === 'ready' ? '✓' : leg.readiness === 'partial' ? '◐' : '✗';
80
+ L(` ${icon} ${leg.screen.padEnd(10)} ${leg.scenarios} scenarios · ${leg.refs.length} refs · selectors ${leg.readiness.toUpperCase()} (${leg.haveKeys}/${leg.refs.length})`);
81
+ }
82
+ const reasons = Object.entries(p.byReason).sort().map(([c, n]) => `${c}×${n}`).join(' ') || '(none)';
83
+ L(` Manual by reason: ${reasons} capability-manual ${p.capabilityManual} judgment ${p.judgmentManual}`);
84
+ if (p.contracts.length) {
85
+ L(' Run-test contract:');
86
+ for (const c of p.contracts) L(` ⚠ ${c.kind}${c.scenario ? ` · ${c.scenario.slice(0, 50)}` : ''}`);
87
+ }
88
+ L(` Readiness: ${p.readiness === 'ready' ? '✓ READY' : '✗ NOT READY' + (p.missingLegs.length ? ` — missing: ${p.missingLegs.join(', ')}` : '')}`);
89
+ L(' Plan:');
90
+ p.plan.forEach((s, i) => L(` ${i + 1}. ${s}`));
91
+ L(`\n Report: ${path.relative(cwd, outPath)}\n`);
92
+ } catch (e) {
93
+ console.error('Error:', e instanceof Error ? e.message : e);
94
+ process.exit(1);
95
+ }
96
+ });
97
+ }