@sun-asterisk/sungen 3.2.2-beta.9 → 3.2.3-beta.3

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 (346) hide show
  1. package/dist/capabilities/discover.d.ts.map +1 -1
  2. package/dist/capabilities/discover.js +26 -3
  3. package/dist/capabilities/discover.js.map +1 -1
  4. package/dist/cli/commands/add-flow.d.ts.map +1 -1
  5. package/dist/cli/commands/add-flow.js +2 -0
  6. package/dist/cli/commands/add-flow.js.map +1 -1
  7. package/dist/cli/commands/add.d.ts.map +1 -1
  8. package/dist/cli/commands/add.js +4 -0
  9. package/dist/cli/commands/add.js.map +1 -1
  10. package/dist/cli/commands/capability.d.ts.map +1 -1
  11. package/dist/cli/commands/capability.js +61 -0
  12. package/dist/cli/commands/capability.js.map +1 -1
  13. package/dist/cli/commands/delivery.d.ts.map +1 -1
  14. package/dist/cli/commands/delivery.js +89 -2
  15. package/dist/cli/commands/delivery.js.map +1 -1
  16. package/dist/cli/commands/eval.js +3 -3
  17. package/dist/cli/commands/eval.js.map +1 -1
  18. package/dist/cli/commands/generate.d.ts.map +1 -1
  19. package/dist/cli/commands/generate.js +2 -1
  20. package/dist/cli/commands/generate.js.map +1 -1
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/init.js +32 -2
  23. package/dist/cli/commands/init.js.map +1 -1
  24. package/dist/cli/commands/journey.d.ts.map +1 -1
  25. package/dist/cli/commands/journey.js +24 -2
  26. package/dist/cli/commands/journey.js.map +1 -1
  27. package/dist/cli/commands/update.d.ts.map +1 -1
  28. package/dist/cli/commands/update.js +10 -3
  29. package/dist/cli/commands/update.js.map +1 -1
  30. package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
  31. package/dist/dashboard/snapshot-builder.js +10 -1
  32. package/dist/dashboard/snapshot-builder.js.map +1 -1
  33. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  34. package/dist/exporters/csv-exporter.js +3 -1
  35. package/dist/exporters/csv-exporter.js.map +1 -1
  36. package/dist/exporters/json-exporter.js +3 -1
  37. package/dist/exporters/json-exporter.js.map +1 -1
  38. package/dist/exporters/mobile-app-id.d.ts +22 -0
  39. package/dist/exporters/mobile-app-id.d.ts.map +1 -0
  40. package/dist/exporters/mobile-app-id.js +37 -0
  41. package/dist/exporters/mobile-app-id.js.map +1 -0
  42. package/dist/exporters/spec-parser.d.ts.map +1 -1
  43. package/dist/exporters/spec-parser.js +6 -2
  44. package/dist/exporters/spec-parser.js.map +1 -1
  45. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +3 -0
  46. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  47. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +7 -4
  48. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +20 -3
  49. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +4 -1
  50. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +4 -1
  51. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +3 -0
  52. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  53. package/dist/generators/test-generator/code-generator.js +48 -9
  54. package/dist/generators/test-generator/code-generator.js.map +1 -1
  55. package/dist/generators/test-generator/template-engine.d.ts +3 -0
  56. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  57. package/dist/generators/test-generator/template-engine.js +11 -1
  58. package/dist/generators/test-generator/template-engine.js.map +1 -1
  59. package/dist/generators/test-generator/utils/selector-resolver.d.ts +11 -1
  60. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  61. package/dist/generators/test-generator/utils/selector-resolver.js +33 -0
  62. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  63. package/dist/harness/audit.d.ts.map +1 -1
  64. package/dist/harness/audit.js +8 -1
  65. package/dist/harness/audit.js.map +1 -1
  66. package/dist/harness/catalog/universal-viewpoints.yaml +15 -0
  67. package/dist/harness/eval/skill-lint.d.ts +3 -3
  68. package/dist/harness/eval/skill-lint.d.ts.map +1 -1
  69. package/dist/harness/eval/skill-lint.js +88 -68
  70. package/dist/harness/eval/skill-lint.js.map +1 -1
  71. package/dist/harness/journey.d.ts +37 -1
  72. package/dist/harness/journey.d.ts.map +1 -1
  73. package/dist/harness/journey.js +146 -19
  74. package/dist/harness/journey.js.map +1 -1
  75. package/dist/harness/quality-gates.d.ts +13 -0
  76. package/dist/harness/quality-gates.d.ts.map +1 -1
  77. package/dist/harness/quality-gates.js +23 -0
  78. package/dist/harness/quality-gates.js.map +1 -1
  79. package/dist/harness/sensors.d.ts +1 -1
  80. package/dist/harness/sensors.d.ts.map +1 -1
  81. package/dist/harness/sensors.js +32 -9
  82. package/dist/harness/sensors.js.map +1 -1
  83. package/dist/orchestrator/ai-rules-updater.d.ts +11 -3
  84. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  85. package/dist/orchestrator/ai-rules-updater.js +44 -180
  86. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  87. package/dist/orchestrator/ai-skills/adapters.d.ts +28 -0
  88. package/dist/orchestrator/ai-skills/adapters.d.ts.map +1 -0
  89. package/dist/orchestrator/ai-skills/adapters.js +34 -0
  90. package/dist/orchestrator/ai-skills/adapters.js.map +1 -0
  91. package/dist/orchestrator/ai-skills/build.d.ts +10 -0
  92. package/dist/orchestrator/ai-skills/build.d.ts.map +1 -0
  93. package/dist/orchestrator/ai-skills/build.js +128 -0
  94. package/dist/orchestrator/ai-skills/build.js.map +1 -0
  95. package/dist/orchestrator/ai-skills/cap-stripper.d.ts +27 -0
  96. package/dist/orchestrator/ai-skills/cap-stripper.d.ts.map +1 -0
  97. package/dist/orchestrator/ai-skills/cap-stripper.js +78 -0
  98. package/dist/orchestrator/ai-skills/cap-stripper.js.map +1 -0
  99. package/dist/orchestrator/ai-skills/commands.d.ts +22 -0
  100. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -0
  101. package/dist/orchestrator/ai-skills/commands.js +109 -0
  102. package/dist/orchestrator/ai-skills/commands.js.map +1 -0
  103. package/dist/orchestrator/ai-skills/generator.d.ts +23 -0
  104. package/dist/orchestrator/ai-skills/generator.d.ts.map +1 -0
  105. package/dist/orchestrator/ai-skills/generator.js +79 -0
  106. package/dist/orchestrator/ai-skills/generator.js.map +1 -0
  107. package/dist/orchestrator/ai-skills/golden-skills.test.d.ts +2 -0
  108. package/dist/orchestrator/ai-skills/golden-skills.test.d.ts.map +1 -0
  109. package/dist/orchestrator/ai-skills/golden-skills.test.js +183 -0
  110. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -0
  111. package/dist/orchestrator/ai-skills/resolve-assistants.d.ts +8 -0
  112. package/dist/orchestrator/ai-skills/resolve-assistants.d.ts.map +1 -0
  113. package/dist/orchestrator/ai-skills/resolve-assistants.js +28 -0
  114. package/dist/orchestrator/ai-skills/resolve-assistants.js.map +1 -0
  115. package/dist/orchestrator/ai-skills/writer.d.ts +15 -0
  116. package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -0
  117. package/dist/orchestrator/ai-skills/writer.js +129 -0
  118. package/dist/orchestrator/ai-skills/writer.js.map +1 -0
  119. package/dist/orchestrator/flow-manager.d.ts +11 -0
  120. package/dist/orchestrator/flow-manager.d.ts.map +1 -1
  121. package/dist/orchestrator/flow-manager.js +147 -29
  122. package/dist/orchestrator/flow-manager.js.map +1 -1
  123. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +59 -0
  124. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -0
  125. package/dist/orchestrator/mobile-runtime-scaffolder.js +298 -0
  126. package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -0
  127. package/dist/orchestrator/project-initializer.d.ts +44 -7
  128. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  129. package/dist/orchestrator/project-initializer.js +147 -79
  130. package/dist/orchestrator/project-initializer.js.map +1 -1
  131. package/dist/orchestrator/screen-manager.d.ts +30 -0
  132. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  133. package/dist/orchestrator/screen-manager.js +205 -22
  134. package/dist/orchestrator/screen-manager.js.map +1 -1
  135. package/dist/orchestrator/templates/ai-src/README.md +58 -0
  136. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-add-flow.md → ai-src/commands/add-flow.md} +63 -2
  137. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-add-screen.md → ai-src/commands/add-screen.md} +171 -6
  138. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-create-test.md → ai-src/commands/create-test.md} +118 -5
  139. package/{src/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md → dist/orchestrator/templates/ai-src/commands/dashboard.md} +25 -3
  140. package/{src/orchestrator/templates/ai-instructions/claude-cmd-delivery.md → dist/orchestrator/templates/ai-src/commands/delivery.md} +67 -3
  141. package/dist/orchestrator/templates/ai-src/commands/design.md +29 -0
  142. package/{src/orchestrator/templates/ai-instructions/claude-cmd-feedback.md → dist/orchestrator/templates/ai-src/commands/feedback.md} +34 -3
  143. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-locale.md → ai-src/commands/locale.md} +56 -4
  144. package/{src/orchestrator/templates/ai-instructions/claude-cmd-review.md → dist/orchestrator/templates/ai-src/commands/review.md} +57 -2
  145. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-run-test.md → ai-src/commands/run-test.md} +116 -8
  146. package/dist/orchestrator/templates/ai-src/config/codex.md +8 -0
  147. package/{src/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md → dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md} +11 -0
  148. package/dist/orchestrator/templates/{ai-instructions/github-skill-sungen-error-mapping.md → ai-src/skills/sungen-error-mapping/SKILL.md} +31 -10
  149. package/{src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md → dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md} +5 -5
  150. package/{src/orchestrator/templates/ai-instructions/claude-skill-locale.md → dist/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md} +275 -3
  151. package/{src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md → dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md} +17 -0
  152. package/{src/orchestrator/templates/ai-instructions/claude-skill-selector-fix-mobile.md → dist/orchestrator/templates/ai-src/skills/sungen-selector-fix-mobile/SKILL.md} +5 -0
  153. package/dist/orchestrator/templates/{ai-instructions/claude-skill-tc-generation.md → ai-src/skills/sungen-tc-generation/SKILL.md} +5 -0
  154. package/dist/orchestrator/templates/playwright.config.ts +7 -4
  155. package/package.json +3 -3
  156. package/src/capabilities/discover.ts +29 -3
  157. package/src/cli/commands/add-flow.ts +2 -0
  158. package/src/cli/commands/add.ts +4 -0
  159. package/src/cli/commands/capability.ts +64 -1
  160. package/src/cli/commands/delivery.ts +89 -2
  161. package/src/cli/commands/eval.ts +3 -3
  162. package/src/cli/commands/generate.ts +2 -1
  163. package/src/cli/commands/init.ts +50 -3
  164. package/src/cli/commands/journey.ts +20 -3
  165. package/src/cli/commands/update.ts +14 -4
  166. package/src/dashboard/snapshot-builder.ts +10 -1
  167. package/src/exporters/csv-exporter.ts +3 -1
  168. package/src/exporters/json-exporter.ts +3 -1
  169. package/src/exporters/mobile-app-id.ts +34 -0
  170. package/src/exporters/spec-parser.ts +6 -2
  171. package/src/generators/test-generator/adapters/adapter-interface.ts +3 -0
  172. package/src/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +7 -4
  173. package/src/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +20 -3
  174. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +4 -1
  175. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +4 -1
  176. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +3 -0
  177. package/src/generators/test-generator/code-generator.ts +49 -9
  178. package/src/generators/test-generator/template-engine.ts +14 -1
  179. package/src/generators/test-generator/utils/selector-resolver.ts +51 -1
  180. package/src/harness/audit.ts +9 -2
  181. package/src/harness/catalog/universal-viewpoints.yaml +15 -0
  182. package/src/harness/eval/skill-lint.ts +71 -60
  183. package/src/harness/journey.ts +167 -19
  184. package/src/harness/quality-gates.ts +20 -0
  185. package/src/harness/sensors.ts +25 -8
  186. package/src/orchestrator/ai-rules-updater.ts +35 -197
  187. package/src/orchestrator/ai-skills/adapters.ts +52 -0
  188. package/src/orchestrator/ai-skills/build.ts +103 -0
  189. package/src/orchestrator/ai-skills/cap-stripper.ts +85 -0
  190. package/src/orchestrator/ai-skills/commands.ts +112 -0
  191. package/src/orchestrator/ai-skills/generator.ts +47 -0
  192. package/src/orchestrator/ai-skills/golden-skills.test.ts +124 -0
  193. package/src/orchestrator/ai-skills/resolve-assistants.ts +18 -0
  194. package/src/orchestrator/ai-skills/writer.ts +92 -0
  195. package/src/orchestrator/flow-manager.ts +165 -29
  196. package/src/orchestrator/mobile-runtime-scaffolder.ts +277 -0
  197. package/src/orchestrator/project-initializer.ts +159 -86
  198. package/src/orchestrator/screen-manager.ts +237 -22
  199. package/src/orchestrator/templates/ai-src/README.md +58 -0
  200. package/src/orchestrator/templates/{ai-instructions/claude-cmd-add-flow.md → ai-src/commands/add-flow.md} +63 -2
  201. package/src/orchestrator/templates/{ai-instructions/claude-cmd-add-screen.md → ai-src/commands/add-screen.md} +171 -6
  202. package/src/orchestrator/templates/{ai-instructions/claude-cmd-create-test.md → ai-src/commands/create-test.md} +118 -5
  203. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md → src/orchestrator/templates/ai-src/commands/dashboard.md} +25 -3
  204. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-delivery.md → src/orchestrator/templates/ai-src/commands/delivery.md} +67 -3
  205. package/src/orchestrator/templates/ai-src/commands/design.md +29 -0
  206. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-feedback.md → src/orchestrator/templates/ai-src/commands/feedback.md} +34 -3
  207. package/src/orchestrator/templates/{ai-instructions/claude-cmd-locale.md → ai-src/commands/locale.md} +56 -4
  208. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-review.md → src/orchestrator/templates/ai-src/commands/review.md} +57 -2
  209. package/src/orchestrator/templates/{ai-instructions/claude-cmd-run-test.md → ai-src/commands/run-test.md} +116 -8
  210. package/src/orchestrator/templates/ai-src/config/codex.md +8 -0
  211. package/{dist/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md → src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md} +11 -0
  212. package/{dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md → src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md} +31 -10
  213. package/{dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md → src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md} +5 -5
  214. package/{dist/orchestrator/templates/ai-instructions/claude-skill-locale.md → src/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md} +275 -3
  215. package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md → src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md} +17 -0
  216. package/{dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix-mobile.md → src/orchestrator/templates/ai-src/skills/sungen-selector-fix-mobile/SKILL.md} +5 -0
  217. package/src/orchestrator/templates/{ai-instructions/claude-skill-tc-generation.md → ai-src/skills/sungen-tc-generation/SKILL.md} +5 -0
  218. package/src/orchestrator/templates/playwright.config.ts +7 -4
  219. package/dist/orchestrator/templates/ai-instructions/claude-cmd-design.md +0 -12
  220. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +0 -347
  221. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +0 -86
  222. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +0 -131
  223. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +0 -94
  224. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +0 -65
  225. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +0 -83
  226. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-design.md +0 -13
  227. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +0 -24
  228. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +0 -70
  229. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +0 -38
  230. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +0 -158
  231. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +0 -184
  232. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +0 -85
  233. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +0 -291
  234. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +0 -316
  235. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +0 -221
  236. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +0 -617
  237. package/src/orchestrator/templates/ai-instructions/claude-cmd-design.md +0 -12
  238. package/src/orchestrator/templates/ai-instructions/claude-skill-api-design.md +0 -62
  239. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +0 -82
  240. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-pat.md +0 -117
  241. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +0 -60
  242. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +0 -38
  243. package/src/orchestrator/templates/ai-instructions/claude-skill-capture.md +0 -35
  244. package/src/orchestrator/templates/ai-instructions/claude-skill-dashboard.md +0 -121
  245. package/src/orchestrator/templates/ai-instructions/claude-skill-delivery.md +0 -141
  246. package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +0 -176
  247. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +0 -446
  248. package/src/orchestrator/templates/ai-instructions/claude-skill-ingest-legacy.md +0 -79
  249. package/src/orchestrator/templates/ai-instructions/claude-skill-mobile-gestures.md +0 -109
  250. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +0 -347
  251. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +0 -320
  252. package/src/orchestrator/templates/ai-instructions/claude-skill-test-design-techniques.md +0 -118
  253. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-a-data-entry.md +0 -203
  254. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-b-data-ops.md +0 -179
  255. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-c-data-explore.md +0 -233
  256. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-d-display.md +0 -226
  257. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-e-identity.md +0 -177
  258. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +0 -118
  259. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +0 -86
  260. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +0 -131
  261. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +0 -94
  262. package/src/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +0 -65
  263. package/src/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +0 -83
  264. package/src/orchestrator/templates/ai-instructions/copilot-cmd-design.md +0 -13
  265. package/src/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +0 -24
  266. package/src/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +0 -70
  267. package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +0 -38
  268. package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +0 -158
  269. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md +0 -62
  270. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +0 -184
  271. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +0 -82
  272. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md +0 -117
  273. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +0 -60
  274. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +0 -38
  275. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +0 -35
  276. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-dashboard.md +0 -121
  277. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +0 -141
  278. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +0 -176
  279. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +0 -446
  280. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +0 -85
  281. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-ingest-legacy.md +0 -79
  282. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +0 -291
  283. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md +0 -109
  284. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +0 -316
  285. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +0 -221
  286. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +0 -617
  287. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +0 -320
  288. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md +0 -118
  289. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-a-data-entry.md +0 -203
  290. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-b-data-ops.md +0 -179
  291. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-c-data-explore.md +0 -233
  292. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-d-display.md +0 -226
  293. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-e-identity.md +0 -177
  294. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +0 -118
  295. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-challenge.md → ai-src/agents/challenge.md} +0 -0
  296. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-discovery.md → ai-src/agents/discovery.md} +0 -0
  297. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-generator.md → ai-src/agents/generator.md} +0 -0
  298. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-reviewer.md → ai-src/agents/reviewer.md} +0 -0
  299. /package/dist/orchestrator/templates/{ai-instructions/claude-config.md → ai-src/config/claude.md} +0 -0
  300. /package/dist/orchestrator/templates/{ai-instructions/copilot-config.md → ai-src/config/copilot.md} +0 -0
  301. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-api-design.md → ai-src/skills/sungen-api-design/SKILL.md} +0 -0
  302. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture.md → ai-src/skills/sungen-capture/SKILL.md} +0 -0
  303. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-figma-mcp.md → ai-src/skills/sungen-capture/mode-figma-mcp.md} +0 -0
  304. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-figma-pat.md → ai-src/skills/sungen-capture/mode-figma-pat.md} +0 -0
  305. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-live.md → ai-src/skills/sungen-capture/mode-live.md} +0 -0
  306. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-local.md → ai-src/skills/sungen-capture/mode-local.md} +0 -0
  307. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-dashboard.md → ai-src/skills/sungen-dashboard/SKILL.md} +0 -0
  308. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-delivery.md → ai-src/skills/sungen-delivery/SKILL.md} +0 -0
  309. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-gherkin-syntax.md → ai-src/skills/sungen-gherkin-syntax/SKILL.md} +0 -0
  310. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-ingest-legacy.md → ai-src/skills/sungen-ingest-legacy/SKILL.md} +0 -0
  311. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-mobile-gestures.md → ai-src/skills/sungen-mobile-gestures/SKILL.md} +0 -0
  312. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-selector-keys.md → ai-src/skills/sungen-selector-keys/SKILL.md} +0 -0
  313. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-tc-review.md → ai-src/skills/sungen-tc-review/SKILL.md} +0 -0
  314. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-test-design-techniques.md → ai-src/skills/sungen-test-design-techniques/SKILL.md} +0 -0
  315. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint.md → ai-src/skills/sungen-viewpoint/SKILL.md} +0 -0
  316. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-a-data-entry.md → ai-src/skills/sungen-viewpoint/group-a-data-entry.md} +0 -0
  317. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-b-data-ops.md → ai-src/skills/sungen-viewpoint/group-b-data-ops.md} +0 -0
  318. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-c-data-explore.md → ai-src/skills/sungen-viewpoint/group-c-data-explore.md} +0 -0
  319. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-d-display.md → ai-src/skills/sungen-viewpoint/group-d-display.md} +0 -0
  320. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-e-identity.md → ai-src/skills/sungen-viewpoint/group-e-identity.md} +0 -0
  321. /package/src/orchestrator/templates/{ai-instructions/claude-agent-challenge.md → ai-src/agents/challenge.md} +0 -0
  322. /package/src/orchestrator/templates/{ai-instructions/claude-agent-discovery.md → ai-src/agents/discovery.md} +0 -0
  323. /package/src/orchestrator/templates/{ai-instructions/claude-agent-generator.md → ai-src/agents/generator.md} +0 -0
  324. /package/src/orchestrator/templates/{ai-instructions/claude-agent-reviewer.md → ai-src/agents/reviewer.md} +0 -0
  325. /package/src/orchestrator/templates/{ai-instructions/claude-config.md → ai-src/config/claude.md} +0 -0
  326. /package/src/orchestrator/templates/{ai-instructions/copilot-config.md → ai-src/config/copilot.md} +0 -0
  327. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md → src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md} +0 -0
  328. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md → src/orchestrator/templates/ai-src/skills/sungen-capture/SKILL.md} +0 -0
  329. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-figma-mcp.md} +0 -0
  330. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-figma-pat.md} +0 -0
  331. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-live.md} +0 -0
  332. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md} +0 -0
  333. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-dashboard.md → src/orchestrator/templates/ai-src/skills/sungen-dashboard/SKILL.md} +0 -0
  334. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md → src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md} +0 -0
  335. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md → src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md} +0 -0
  336. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-ingest-legacy.md → src/orchestrator/templates/ai-src/skills/sungen-ingest-legacy/SKILL.md} +0 -0
  337. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md → src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md} +0 -0
  338. /package/src/orchestrator/templates/{ai-instructions/claude-skill-selector-keys.md → ai-src/skills/sungen-selector-keys/SKILL.md} +0 -0
  339. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md → src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md} +0 -0
  340. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md → src/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md} +0 -0
  341. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md} +0 -0
  342. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-a-data-entry.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-a-data-entry.md} +0 -0
  343. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-b-data-ops.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-b-data-ops.md} +0 -0
  344. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-c-data-explore.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-c-data-explore.md} +0 -0
  345. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-d-display.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-d-display.md} +0 -0
  346. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-e-identity.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-e-identity.md} +0 -0
@@ -27,6 +27,10 @@ export interface Obligation {
27
27
  waivedReason?: string; // S2 — set when the QA explicitly waived this obligation
28
28
  }
29
29
 
30
+ /** S5 — a parsed anchor so an IDE can jump straight to what needs review. */
31
+ export interface Anchor { vpId?: string; file?: string; line?: number; }
32
+ export interface ReviewItem { text: string; anchor: Anchor; }
33
+
30
34
  export interface JourneyReport {
31
35
  unit: string;
32
36
  generatedFrom: string[]; // which artifacts were available
@@ -35,7 +39,7 @@ export interface JourneyReport {
35
39
  gateStatus: string | null;
36
40
  score: number | null;
37
41
  obligations: Obligation[];
38
- needsYou: string[]; // located findings / human-judgment items (the review queue seed)
42
+ needsYou: ReviewItem[]; // S5 — the review queue: human-judgment findings, each with an anchor
39
43
  nextSuggested: string;
40
44
  }
41
45
 
@@ -58,6 +62,13 @@ function isHumanFinding(f: string): boolean {
58
62
  return /@manual|MANUAL-|DEPTH-DEFERRED|UNSOURCEABLE|CAPABILITY-SUGGESTION|judgment|oracle|review/i.test(f);
59
63
  }
60
64
 
65
+ /** S5 — pull an IDE-jumpable anchor out of a finding (VP id, scenario, or `file.ext:line`). */
66
+ function extractAnchor(text: string): Anchor {
67
+ const vp = text.match(/\bVP-[A-Z0-9-]+\b/);
68
+ const fl = text.match(/([\w./-]+\.(?:md|feature|ts|yaml)):(\d+)/);
69
+ return { vpId: vp ? vp[0] : undefined, file: fl ? fl[1] : undefined, line: fl ? Number(fl[2]) : undefined };
70
+ }
71
+
61
72
  const SAT = 0.8; // axis at/above this = satisfied (below = needs-work)
62
73
 
63
74
  function computeFresh(projectRoot: string, unit: string): JourneyReport {
@@ -73,7 +84,7 @@ function computeFresh(projectRoot: string, unit: string): JourneyReport {
73
84
  const ran = (p: string) => phases.some((s) => s === p || s.startsWith(p));
74
85
 
75
86
  const obligations: Obligation[] = [];
76
- const needsYou: string[] = [];
87
+ const needsYou: ReviewItem[] = [];
77
88
 
78
89
  if (!audit) {
79
90
  // Nothing measured yet — the journey hasn't really begun.
@@ -105,19 +116,31 @@ function computeFresh(projectRoot: string, unit: string): JourneyReport {
105
116
  detail: autoPending ? `${audit.automatableManual.automatable} @manual scenario(s) a driver could automate` : 'no capability-pending automatable cases',
106
117
  });
107
118
 
108
- // Human sign-off: always pending until S5 explicit signoff; carries the review-queue count.
109
- const manualCount = (audit.manualOracle && audit.manualOracle.total) || 0;
119
+ // Needs-you: the review queue. Prefer PRECISE per-scenario anchors from audit's structured
120
+ // depth data — each shallow business-critical scenario carries its own VP id — over regex-parsing
121
+ // the aggregate "DEPTH:" finding string (which names no scenario, so points nowhere). Then
122
+ // supplement with the remaining human-judgment findings.
123
+ const shallowBC: { name: string; category?: string }[] = (audit.depth && audit.depth.shallowBusinessCritical) || [];
124
+ for (const s of shallowBC) {
125
+ needsYou.push({
126
+ text: `DEEPEN — "${s.name}": assert data, not just visibility (\`… with {{value}}\` / \`see all … contain {{v}}\`), or defer cross-screen with @manual + reason.`,
127
+ anchor: extractAnchor(s.name),
128
+ });
129
+ }
130
+ const haveStructuredDepth = shallowBC.length > 0;
131
+ for (const f of (audit.findings || [])) {
132
+ if (!isHumanFinding(f)) continue;
133
+ if (haveStructuredDepth && /^\s*DEPTH\b/i.test(f)) continue; // superseded by the per-scenario items above
134
+ needsYou.push({ text: f, anchor: extractAnchor(f) });
135
+ }
136
+
137
+ // Human sign-off: always pending until S5 explicit signoff; carries the now-final review-queue count.
138
+ const manualCount = (audit.manualOracle && (audit.manualOracle.manualTotal ?? audit.manualOracle.total)) || 0;
110
139
  obligations.push({
111
140
  id: 'OB-signoff', title: 'Human sign-off', status: 'pending',
112
- detail: `${needsYou.length} item(s) queued for your review (single sign-off at the end)`,
141
+ detail: `${needsYou.length} item(s) queued for your review Ā· ${manualCount} @manual scenario(s)`,
113
142
  });
114
143
 
115
- // Needs-you: human-judgment findings (the review-queue seed), with their anchors (titles) intact.
116
- for (const f of (audit.findings || [])) if (isHumanFinding(f)) needsYou.push(f);
117
- // Re-stamp the signoff detail now that needsYou is filled.
118
- const signoff = obligations.find((o) => o.id === 'OB-signoff');
119
- if (signoff) signoff.detail = `${needsYou.length} item(s) queued for your review Ā· ${manualCount} @manual scenario(s)`;
120
-
121
144
  // Next suggested = first non-satisfied obligation; if all satisfied but run-test not done → run.
122
145
  const firstGap = obligations.find((o) => o.status !== 'satisfied' && o.id !== 'OB-signoff');
123
146
  let nextSuggested: string;
@@ -135,7 +158,8 @@ function computeFresh(projectRoot: string, unit: string): JourneyReport {
135
158
  // ---------------- S2: writable lifecycle — persisted waivers + reconcile ----------------
136
159
 
137
160
  interface Waiver { reason: string; at: string; auditHashAtWaive: string; }
138
- interface JourneyState { unit: string; auditHash: string; waivers: Record<string, Waiver>; }
161
+ interface Signoff { by?: string; at: string; auditHashAtSignoff: string; } // S5
162
+ interface JourneyState { unit: string; auditHash: string; waivers: Record<string, Waiver>; signoff?: Signoff; }
139
163
 
140
164
  function statePath(projectRoot: string, slug: string): string {
141
165
  return path.join(projectRoot, '.sungen', 'journey', `${slug}.state.json`);
@@ -181,11 +205,22 @@ export function runJourney(projectRoot: string, unit: string): JourneyReport {
181
205
  ob.waivedReason = w.reason;
182
206
  ob.detail = `waived — ${w.reason}`;
183
207
  } else {
184
- report.needsYou.unshift(`āš ļø Waiver on "${ob.title}" is STALE (evidence changed since ${w.at}) — re-decide. Was: ${w.reason}`);
208
+ report.needsYou.unshift({ text: `āš ļø Waiver on "${ob.title}" is STALE (evidence changed since ${w.at}) — re-decide. Was: ${w.reason}`, anchor: { vpId: ob.id } });
209
+ }
210
+ }
211
+ // S5 — sign-off reconcile: a recorded sign-off satisfies OB-signoff while its evidence holds;
212
+ // if the audit changed since, the sign-off is STALE → re-surface for re-review (anti-amnesia).
213
+ const signoffOb = report.obligations.find((o) => o.id === 'OB-signoff');
214
+ if (signoffOb && state.signoff) {
215
+ if (state.signoff.auditHashAtSignoff === curHash) {
216
+ signoffOb.status = 'satisfied';
217
+ signoffOb.detail = `signed off${state.signoff.by ? ' by ' + state.signoff.by : ''} at ${state.signoff.at}`;
218
+ } else {
219
+ report.needsYou.unshift({ text: `āš ļø Sign-off is STALE (suite changed since ${state.signoff.at}) — re-review and sign off again.`, anchor: { vpId: 'OB-signoff' } });
185
220
  }
186
221
  }
187
222
  report.nextSuggested = computeNext(report, unit);
188
- saveState(sp, { unit, auditHash: curHash, waivers: state.waivers });
223
+ saveState(sp, { unit, auditHash: curHash, waivers: state.waivers, signoff: state.signoff });
189
224
  return report;
190
225
  }
191
226
 
@@ -210,6 +245,25 @@ export function waive(projectRoot: string, unit: string, obId: string, reason: s
210
245
  return runJourney(projectRoot, unit);
211
246
  }
212
247
 
248
+ /**
249
+ * Sign off — the single human gate (S5). Allowed ONLY when every other deliver-required
250
+ * obligation is satisfied or waived (you cannot sign off an unclean suite). Records who/when +
251
+ * the evidence cursor; reconcile invalidates it (stale → re-review) if the suite later changes.
252
+ */
253
+ export function signoff(projectRoot: string, unit: string, by?: string): JourneyReport {
254
+ const before = runJourney(projectRoot, unit);
255
+ const blockers = before.obligations.filter((o) => o.id !== 'OB-signoff' && o.status !== 'satisfied' && o.status !== 'waived');
256
+ if (blockers.length) {
257
+ throw new Error(`Cannot sign off — still open: ${blockers.map((b) => b.id).join(', ')}. Fix, or waive each with a reason, first.`);
258
+ }
259
+ const slug = reportSlug(unit);
260
+ const sp = statePath(projectRoot, slug);
261
+ const state = loadState(sp) || { unit, auditHash: '', waivers: {} };
262
+ state.signoff = { by, at: new Date().toISOString(), auditHashAtSignoff: auditHashOf(projectRoot, slug) };
263
+ saveState(sp, state);
264
+ return runJourney(projectRoot, unit);
265
+ }
266
+
213
267
  const ICON: Record<ObStatus, string> = { satisfied: 'āœ…', 'needs-work': 'āš ļø ', pending: 'ā³', waived: '🚫' };
214
268
 
215
269
  export function renderJourneyBoard(r: JourneyReport): string {
@@ -222,9 +276,14 @@ export function renderJourneyBoard(r: JourneyReport): string {
222
276
  L.push('## What must still be true');
223
277
  for (const o of r.obligations) L.push(`- ${ICON[o.status]} **${o.title}** — ${o.detail}`);
224
278
  L.push('');
225
- L.push(`## šŸ”Ž Needs your judgment (${r.needsYou.length})`);
226
- if (r.needsYou.length) for (const f of r.needsYou.slice(0, 20)) L.push(`- ${f}`);
227
- else L.push('- (nothing queued)');
279
+ L.push(`## šŸ”Ž Review queue — needs your judgment (${r.needsYou.length})`);
280
+ if (r.needsYou.length) {
281
+ for (const it of r.needsYou.slice(0, 20)) {
282
+ const a = it.anchor;
283
+ const tag = [a.vpId, a.file && `${a.file}${a.line ? ':' + a.line : ''}`].filter(Boolean).join(' Ā· ');
284
+ L.push(`- ${it.text}${tag ? ` 怔${tag}怕` : ''}`);
285
+ }
286
+ } else L.push('- (nothing queued)');
228
287
  L.push('');
229
288
  L.push(`## → Next`);
230
289
  L.push(`${r.nextSuggested}`);
@@ -255,8 +314,8 @@ const PHASE_REQUIRED: Record<GatePhase, string[]> = {
255
314
  create: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace'],
256
315
  // post-run: the design gates + automation coverage.
257
316
  run: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace', 'OB-automation'],
258
- // pre-delivery: everything automated (human sign-off is the separate S5 gate).
259
- deliver: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace', 'OB-automation'],
317
+ // pre-delivery: everything automated PLUS the single human sign-off (S5).
318
+ deliver: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace', 'OB-automation', 'OB-signoff'],
260
319
  };
261
320
 
262
321
  export function runGate(projectRoot: string, unit: string, phase: GatePhase): GateVerdict {
@@ -284,3 +343,92 @@ export function renderGate(v: GateVerdict): string {
284
343
  L.push('');
285
344
  return L.join('\n');
286
345
  }
346
+
347
+ // ---------------- #409: project-level roll-up across all units ----------------
348
+ //
349
+ // Multi-unit fan-out PARALLELISES generation but human review/sign-off must stay SERIAL — you can't
350
+ // judge N boards at once. This roll-up is that serial surface: one row per unit (gate Ā· score Ā· phase
351
+ // Ā· what it still needs), and a single "review this next" pointer. The per-unit board is unchanged;
352
+ // this only aggregates the same read-only `runJourney` synthesis.
353
+
354
+ export type UnitKind = 'screen' | 'flow' | 'api';
355
+ export interface UnitSummary {
356
+ unit: string;
357
+ kind: UnitKind;
358
+ score: number | null;
359
+ gateStatus: string | null;
360
+ youAreHere: string;
361
+ obligationsOpen: number; // non-satisfied, non-waived (excludes OB-signoff)
362
+ reviewQueue: number;
363
+ signedOff: boolean;
364
+ started: boolean; // has an audit report yet
365
+ }
366
+
367
+ /** Discover every unit under qa/screens, qa/flows, and qa/api top-level areas. */
368
+ export function listUnits(projectRoot: string): { unit: string; kind: UnitKind }[] {
369
+ const out: { unit: string; kind: UnitKind }[] = [];
370
+ const scan = (sub: string, kind: UnitKind) => {
371
+ const d = path.join(projectRoot, 'qa', sub);
372
+ if (!fs.existsSync(d)) return;
373
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
374
+ // 'flows' under qa/api is a container for api-flows (a nested tier) — not a unit itself.
375
+ if (e.isDirectory() && !(sub === 'api' && e.name === 'flows')) out.push({ unit: e.name, kind });
376
+ }
377
+ };
378
+ scan('screens', 'screen');
379
+ scan('flows', 'flow');
380
+ scan('api', 'api');
381
+ return out;
382
+ }
383
+
384
+ /** Roll up every unit's journey into one table of summaries (serial review surface). */
385
+ export function rollupJourney(projectRoot: string): UnitSummary[] {
386
+ return listUnits(projectRoot).map(({ unit, kind }) => {
387
+ const r = runJourney(projectRoot, unit);
388
+ const signedOff = r.obligations.find((o) => o.id === 'OB-signoff')?.status === 'satisfied';
389
+ const obligationsOpen = r.obligations.filter(
390
+ (o) => o.id !== 'OB-signoff' && o.status !== 'satisfied' && o.status !== 'waived',
391
+ ).length;
392
+ return {
393
+ unit, kind, score: r.score, gateStatus: r.gateStatus, youAreHere: r.youAreHere,
394
+ obligationsOpen, reviewQueue: r.needsYou.length, signedOff,
395
+ started: r.generatedFrom.includes('audit'),
396
+ };
397
+ });
398
+ }
399
+
400
+ function unitStatusLabel(r: UnitSummary): string {
401
+ if (!r.started) return 'ā—»ļø not started';
402
+ if (r.signedOff) return 'āœ… signed off';
403
+ if (r.obligationsOpen > 0) return `ā³ ${r.obligationsOpen} obligation(s) open`;
404
+ if (r.reviewQueue > 0) return `šŸ”Ž review ${r.reviewQueue} item(s)`;
405
+ return '🟔 ready to sign off';
406
+ }
407
+
408
+ export function renderRollupBoard(rows: UnitSummary[]): string {
409
+ const L: string[] = [];
410
+ L.push('# Journey roll-up — all units');
411
+ L.push('');
412
+ if (!rows.length) {
413
+ L.push('No units found under `qa/screens`, `qa/flows`, or `qa/api`.');
414
+ return L.join('\n');
415
+ }
416
+ L.push('| Unit | Kind | Score | Gate | Phase | Needs |');
417
+ L.push('|---|---|---|---|---|---|');
418
+ for (const r of rows) {
419
+ L.push(`| ${r.unit} | ${r.kind} | ${r.score ?? '—'} | ${r.gateStatus ?? '—'} | ${r.youAreHere} | ${unitStatusLabel(r)} |`);
420
+ }
421
+ L.push('');
422
+ const blocked = rows.filter((r) => r.started && (r.obligationsOpen > 0));
423
+ const toReview = rows.filter((r) => r.started && r.obligationsOpen === 0 && !r.signedOff && r.reviewQueue >= 0);
424
+ const notStarted = rows.filter((r) => !r.started);
425
+ L.push(`**${rows.filter((r) => r.signedOff).length}/${rows.length} signed off** Ā· ${blocked.length} with open obligations Ā· ${notStarted.length} not started.`);
426
+ L.push('');
427
+ L.push('## → Next (review is serial — one unit at a time)');
428
+ const next = blocked[0] || toReview[0] || notStarted[0];
429
+ if (!next) L.push('All units signed off — `sungen delivery` to export.');
430
+ else if (!next.started) L.push(`Generate **${next.unit}** — \`/sungen:create-test ${next.unit}\`.`);
431
+ else L.push(`Review **${next.unit}** (${unitStatusLabel(next)}) — \`sungen journey --screen ${next.unit}\`, then sign off.`);
432
+ L.push('');
433
+ return L.join('\n');
434
+ }
@@ -42,6 +42,26 @@ export function isolationRisk(featureText: string, stateful: boolean): boolean {
42
42
  return !/@cleanup:cart\b|@isolate\b|empty cart|fresh (?:browser )?context|new context/i.test(featureText);
43
43
  }
44
44
 
45
+ /**
46
+ * #414 — the complement of isolationRisk. Per-scenario isolation is the DEFAULT now, so untagged
47
+ * suites can't cascade. `@serial` opts INTO a shared-page `test.describe.serial` suite, whose
48
+ * Playwright semantics SKIP every test after the first failure. That is correct ONLY for a genuinely
49
+ * stateful ordered sequence; for INDEPENDENT scenarios — each re-establishing its own start state via
50
+ * a navigating Background — `@serial` buys no correctness, only a cascade that *masks* failures (one
51
+ * early red turns the rest into greens-by-omission). Returns the scenario count when `@serial` should
52
+ * be dropped (the default isolation is safer), else 0.
53
+ *
54
+ * Conservative on purpose — only fires on `@serial` suites whose every scenario provably restarts (a
55
+ * `Background` that navigates `on [..] page`), so a real stateful sequence keeps its `@serial`.
56
+ */
57
+ export function serialCascadeRisk(featureText: string, scenarioCount: number): number {
58
+ if (!/@serial\b/i.test(featureText)) return 0; // isolated by default → no cascade
59
+ if (scenarioCount < 8) return 0; // small blast radius — not worth the churn
60
+ const navBackground = /Background:[\s\S]*?\bon \[[^\]]+\] page\b/i.test(featureText);
61
+ if (!navBackground) return 0; // no per-scenario reset → genuinely stateful; @serial is right
62
+ return scenarioCount;
63
+ }
64
+
45
65
  export function downstreamScope(specText: string, scenarios: ScenarioInfo[]): DownstreamResult {
46
66
  const routes = downstreamRoutes(specText);
47
67
  const underCovered: { route: string; slug: string }[] = [];
@@ -59,9 +59,17 @@ export function loadCatalog(): Catalog {
59
59
  return parseYaml(fs.readFileSync(p, 'utf-8')) as Catalog;
60
60
  }
61
61
 
62
+ // Word-aware keyword match: `\b<kw>(s|es|ed|ing)?\b`, so a keyword matches whole words (plus
63
+ // simple plural/verb suffixes) but NOT as a substring of a larger word — e.g. `form` must not
64
+ // match inside `Format` or `@platform`, while `product` still matches `products`. Multi-word
65
+ // keywords ("add to cart", "is hidden") match the phrase with a boundary at each end.
66
+ const wordRe = (kw: string) => {
67
+ const esc = kw.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+ return new RegExp(`\\b${esc}(?:s|es|ed|ing)?\\b`);
69
+ };
62
70
  const has = (haystacks: string[], kw: string) => {
63
- const k = kw.toLowerCase();
64
- return haystacks.some((h) => h.includes(k));
71
+ const re = wordRe(kw);
72
+ return haystacks.some((h) => re.test(h));
65
73
  };
66
74
 
67
75
  // ---------- Sensor 1: Viewpoint Gate (catalog-driven) ----------
@@ -75,16 +83,21 @@ export interface GateResult {
75
83
  universalGaps: string[];
76
84
  }
77
85
 
78
- export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[], catalog: Catalog): GateResult {
86
+ export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[], catalog: Catalog, isMobile = false): GateResult {
79
87
  const haystacks = [
80
88
  ...scenarios.map((s) => s.haystack),
81
89
  ...viewpoints.map((v) => `${v.id} ${v.reason}`.toLowerCase()),
82
90
  ];
83
91
 
84
- // Detect page-type by detect_keywords hit count
92
+ // Detect page-type by detect_keywords hit count. `mobile-*` page-types are candidates ONLY for
93
+ // mobile targets; web targets never consider them. Mobile targets still consider the web types
94
+ // too, so a mobile commerce/form screen is selected as ecommerce-list/form by keyword fit —
95
+ // `mobile-home` does not crowd out a better-fitting type. With no type matching → pageType stays
96
+ // null → no themes required → the gate passes leniently rather than false-FAILing.
85
97
  let pageType: string | null = null;
86
98
  let best = 0;
87
99
  for (const [pt, def] of Object.entries(catalog.page_types)) {
100
+ if (pt.startsWith('mobile-') && !isMobile) continue;
88
101
  const hits = def.detect_keywords.filter((k) => has(haystacks, k)).length;
89
102
  if (hits > best) { best = hits; pageType = pt; }
90
103
  }
@@ -97,10 +110,14 @@ export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEn
97
110
  // Scenarios that cover this theme by keyword (include @manual — a manual
98
111
  // scenario with a real assertion still covers the viewpoint for design).
99
112
  const coverers = scenarios.filter((s) => t.keywords.some((k) => s.haystack.includes(k.toLowerCase())));
100
- const deep = coverers.some((s) => s.hasDataAssertion);
101
- if (deep) covered++;
102
- else if (coverers.length > 0) gaps.push({ theme: t.theme, keywords: t.keywords, status: 'shallow' });
103
- else gaps.push({ theme: t.theme, keywords: t.keywords, status: 'missing' });
113
+ if (coverers.length === 0) { gaps.push({ theme: t.theme, keywords: t.keywords, status: 'missing' }); continue; }
114
+ // A theme WITHOUT `depth:` is visibility/navigation — landing/seeing/leaving IS the
115
+ // assertion, so mere presence covers it. Only DATA-correctness themes (depth.requires:
116
+ // data-assertion) need a data assertion to count as deeply covered.
117
+ const needsDepth = t.depth?.requires === 'data-assertion';
118
+ if (!needsDepth) { covered++; continue; }
119
+ if (coverers.some((s) => s.hasDataAssertion)) covered++;
120
+ else gaps.push({ theme: t.theme, keywords: t.keywords, status: 'shallow' });
104
121
  }
105
122
  }
106
123
 
@@ -6,106 +6,15 @@
6
6
 
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
+ import { buildOutputs } from './ai-skills/build';
10
+ import { applyOutputs } from './ai-skills/writer';
11
+ import { AdapterKey } from './ai-skills/adapters';
9
12
 
10
- // File mapping: [templateFile, outputPath]
11
- // Shared with project-initializer.ts
12
- export const AI_RULES_FILE_MAPPING: [string, string][] = [
13
- // Config
14
- ['claude-config.md', 'CLAUDE.md'],
15
- ['copilot-config.md', '.github/copilot-instructions.md'],
16
-
17
- // Commands — Claude Code
18
- ['claude-cmd-add-screen.md', '.claude/commands/sungen/add-screen.md'],
19
- ['claude-cmd-add-flow.md', '.claude/commands/sungen/add-flow.md'],
20
- ['claude-cmd-create-test.md', '.claude/commands/sungen/create-test.md'],
21
- ['claude-cmd-design.md', '.claude/commands/sungen/design.md'],
22
- ['claude-cmd-run-test.md', '.claude/commands/sungen/run-test.md'],
23
- ['claude-cmd-review.md', '.claude/commands/sungen/review.md'],
24
- ['claude-cmd-delivery.md', '.claude/commands/sungen/delivery.md'],
25
- ['claude-cmd-dashboard.md', '.claude/commands/sungen/dashboard.md'],
26
- ['claude-cmd-locale.md', '.claude/commands/sungen/locale.md'],
27
- ['claude-cmd-feedback.md', '.claude/commands/sungen/feedback.md'],
28
-
29
- // Commands — GitHub Copilot
30
- ['copilot-cmd-add-screen.md', '.github/prompts/sungen-add-screen.prompt.md'],
31
- ['copilot-cmd-add-flow.md', '.github/prompts/sungen-add-flow.prompt.md'],
32
- ['copilot-cmd-create-test.md', '.github/prompts/sungen-create-test.prompt.md'],
33
- ['copilot-cmd-design.md', '.github/prompts/sungen-design.prompt.md'],
34
- ['copilot-cmd-run-test.md', '.github/prompts/sungen-run-test.prompt.md'],
35
- ['copilot-cmd-review.md', '.github/prompts/sungen-review.prompt.md'],
36
- ['copilot-cmd-delivery.md', '.github/prompts/sungen-delivery.prompt.md'],
37
- ['copilot-cmd-dashboard.md', '.github/prompts/sungen-dashboard.prompt.md'],
38
- ['copilot-cmd-locale.md', '.github/prompts/sungen-locale.prompt.md'],
39
- ['copilot-cmd-feedback.md', '.github/prompts/sungen-feedback.prompt.md'],
40
-
41
- // Skills — Claude Code
42
- ['claude-skill-gherkin-syntax.md', '.claude/skills/sungen-gherkin-syntax/SKILL.md'],
43
- ['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
44
- ['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
45
- ['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
46
- ['claude-skill-test-design-techniques.md', '.claude/skills/sungen-test-design-techniques/SKILL.md'],
47
- ['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
48
- ['claude-skill-tc-review.md', '.claude/skills/sungen-tc-review/SKILL.md'],
49
- ['claude-skill-harness-audit.md', '.claude/skills/sungen-harness-audit/SKILL.md'],
50
- ['claude-skill-api-design.md', '.claude/skills/sungen-api-design/SKILL.md'],
51
- ['claude-skill-ingest-legacy.md', '.claude/skills/sungen-ingest-legacy/SKILL.md'],
52
- ['claude-skill-viewpoint.md', '.claude/skills/sungen-viewpoint/SKILL.md'],
53
- ['claude-skill-viewpoint-group-a-data-entry.md', '.claude/skills/sungen-viewpoint/group-a-data-entry.md'],
54
- ['claude-skill-viewpoint-group-b-data-ops.md', '.claude/skills/sungen-viewpoint/group-b-data-ops.md'],
55
- ['claude-skill-viewpoint-group-c-data-explore.md', '.claude/skills/sungen-viewpoint/group-c-data-explore.md'],
56
- ['claude-skill-viewpoint-group-d-display.md', '.claude/skills/sungen-viewpoint/group-d-display.md'],
57
- ['claude-skill-viewpoint-group-e-identity.md', '.claude/skills/sungen-viewpoint/group-e-identity.md'],
58
- ['claude-skill-delivery.md', '.claude/skills/sungen-delivery/SKILL.md'],
59
- ['claude-skill-dashboard.md', '.claude/skills/sungen-dashboard/SKILL.md'],
60
- // sungen-capture — router skill (1 SKILL.md + 4 mode files). Replaces the former
61
- // capture-figma / capture-live / capture-local / figma-source standalone skills.
62
- ['claude-skill-capture.md', '.claude/skills/sungen-capture/SKILL.md'],
63
- ['claude-skill-capture-mode-figma-mcp.md', '.claude/skills/sungen-capture/mode-figma-mcp.md'],
64
- ['claude-skill-capture-mode-figma-pat.md', '.claude/skills/sungen-capture/mode-figma-pat.md'],
65
- ['claude-skill-capture-mode-live.md', '.claude/skills/sungen-capture/mode-live.md'],
66
- ['claude-skill-capture-mode-local.md', '.claude/skills/sungen-capture/mode-local.md'],
67
- ['claude-skill-locale.md', '.claude/skills/sungen-locale/SKILL.md'],
68
- // Mobile (Appium) skills — relevant for platform: mobile (MOB-5).
69
- ['claude-skill-mobile-gestures.md', '.claude/skills/sungen-mobile-gestures/SKILL.md'],
70
- ['claude-skill-capture-mobile.md', '.claude/skills/sungen-capture-mobile/SKILL.md'],
71
- ['claude-skill-selector-fix-mobile.md', '.claude/skills/sungen-selector-fix-mobile/SKILL.md'],
72
-
73
- // Agents — Claude Code sub-agents (isolated context). Copilot runs these inline.
74
- ['claude-agent-reviewer.md', '.claude/agents/sungen-reviewer.md'],
75
- ['claude-agent-discovery.md', '.claude/agents/sungen-discovery.md'],
76
- ['claude-agent-challenge.md', '.claude/agents/sungen-challenge.md'],
77
- ['claude-agent-generator.md', '.claude/agents/sungen-generator.md'],
13
+ function sungenVersion(): string {
14
+ try { return require(path.join(__dirname, '..', '..', 'package.json')).version || '0.0.0'; }
15
+ catch { return '0.0.0'; }
16
+ }
78
17
 
79
- // Skills — GitHub Copilot
80
- ['github-skill-sungen-gherkin-syntax.md', '.github/skills/sungen-gherkin-syntax/SKILL.md'],
81
- ['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
82
- ['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
83
- ['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
84
- ['github-skill-sungen-test-design-techniques.md', '.github/skills/sungen-test-design-techniques/SKILL.md'],
85
- ['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
86
- ['github-skill-sungen-tc-review.md', '.github/skills/sungen-tc-review/SKILL.md'],
87
- ['github-skill-sungen-harness-audit.md', '.github/skills/sungen-harness-audit/SKILL.md'],
88
- ['github-skill-sungen-api-design.md', '.github/skills/sungen-api-design/SKILL.md'],
89
- ['github-skill-sungen-ingest-legacy.md', '.github/skills/sungen-ingest-legacy/SKILL.md'],
90
- ['github-skill-sungen-viewpoint.md', '.github/skills/sungen-viewpoint/SKILL.md'],
91
- ['github-skill-sungen-viewpoint-group-a-data-entry.md', '.github/skills/sungen-viewpoint/group-a-data-entry.md'],
92
- ['github-skill-sungen-viewpoint-group-b-data-ops.md', '.github/skills/sungen-viewpoint/group-b-data-ops.md'],
93
- ['github-skill-sungen-viewpoint-group-c-data-explore.md', '.github/skills/sungen-viewpoint/group-c-data-explore.md'],
94
- ['github-skill-sungen-viewpoint-group-d-display.md', '.github/skills/sungen-viewpoint/group-d-display.md'],
95
- ['github-skill-sungen-viewpoint-group-e-identity.md', '.github/skills/sungen-viewpoint/group-e-identity.md'],
96
- ['github-skill-sungen-delivery.md', '.github/skills/sungen-delivery/SKILL.md'],
97
- ['github-skill-sungen-dashboard.md', '.github/skills/sungen-dashboard/SKILL.md'],
98
- ['github-skill-sungen-capture.md', '.github/skills/sungen-capture/SKILL.md'],
99
- ['github-skill-sungen-capture-mode-figma-mcp.md', '.github/skills/sungen-capture/mode-figma-mcp.md'],
100
- ['github-skill-sungen-capture-mode-figma-pat.md', '.github/skills/sungen-capture/mode-figma-pat.md'],
101
- ['github-skill-sungen-capture-mode-live.md', '.github/skills/sungen-capture/mode-live.md'],
102
- ['github-skill-sungen-capture-mode-local.md', '.github/skills/sungen-capture/mode-local.md'],
103
- ['github-skill-sungen-locale.md', '.github/skills/sungen-locale/SKILL.md'],
104
- // Mobile (Appium) skills — relevant for platform: mobile (MOB-5).
105
- ['github-skill-sungen-mobile-gestures.md', '.github/skills/sungen-mobile-gestures/SKILL.md'],
106
- ['github-skill-sungen-capture-mobile.md', '.github/skills/sungen-capture-mobile/SKILL.md'],
107
- ['github-skill-sungen-selector-fix-mobile.md', '.github/skills/sungen-selector-fix-mobile/SKILL.md'],
108
- ];
109
18
 
110
19
  // Skill/asset directories retired in a previous refactor. `sungen update` removes
111
20
  // these so already-initialized projects don't keep loading stale skill descriptions.
@@ -124,119 +33,48 @@ export const STALE_AI_RULES_PATHS: string[] = [
124
33
 
125
34
  export class AIRulesUpdater {
126
35
  private cwd: string;
127
- private aiTemplateDir: string;
128
36
 
129
37
  constructor(cwd: string) {
130
38
  this.cwd = cwd;
131
- this.aiTemplateDir = path.join(__dirname, 'templates', 'ai-instructions');
132
39
  }
133
40
 
134
- async update(dryRun: boolean): Promise<void> {
135
- console.log('šŸ”„ Updating AI rules, commands, and skills...\n');
136
-
137
- const updated: string[] = [];
138
- const created: string[] = [];
139
- const unchanged: string[] = [];
140
- const missing: string[] = [];
141
-
142
- for (const [templateFile, outputRelPath] of AI_RULES_FILE_MAPPING) {
143
- const templatePath = path.join(this.aiTemplateDir, templateFile);
144
- const outputPath = path.join(this.cwd, outputRelPath);
145
-
146
- if (!fs.existsSync(templatePath)) {
147
- missing.push(templateFile);
148
- continue;
149
- }
150
-
151
- const newContent = fs.readFileSync(templatePath, 'utf-8');
152
-
153
- if (fs.existsSync(outputPath)) {
154
- const currentContent = fs.readFileSync(outputPath, 'utf-8');
155
- if (currentContent === newContent) {
156
- unchanged.push(outputRelPath);
157
- continue;
158
- }
159
-
160
- if (!dryRun) {
161
- fs.writeFileSync(outputPath, newContent, 'utf-8');
162
- }
163
- updated.push(outputRelPath);
164
- } else {
165
- if (!dryRun) {
166
- const outputDir = path.dirname(outputPath);
167
- if (!fs.existsSync(outputDir)) {
168
- fs.mkdirSync(outputDir, { recursive: true });
169
- }
170
- fs.writeFileSync(outputPath, newContent, 'utf-8');
171
- }
172
- created.push(outputRelPath);
173
- }
174
- }
175
-
176
- // Remove retired skills/assets so stale descriptions stop loading.
41
+ /**
42
+ * Refresh AI rules/commands/skills from the single-source generator.
43
+ * @param dryRun show changes without writing
44
+ * @param opts.agents which assistants to install (default: claude — decision T5)
45
+ * @param opts.force overwrite user config files (CLAUDE.md/AGENTS.md/copilot-instructions)
46
+ */
47
+ async update(dryRun: boolean, opts: { agents?: AdapterKey[]; force?: boolean } = {}): Promise<void> {
48
+ const agents = opts.agents && opts.agents.length ? opts.agents : (['claude'] as AdapterKey[]);
49
+ console.log(`šŸ”„ Updating AI rules for: ${agents.join(', ')}${dryRun ? ' (dry-run)' : ''}\n`);
50
+
51
+ // One-time legacy cleanup for projects created before the manifest existed.
177
52
  const removed: string[] = [];
178
53
  for (const staleRelPath of STALE_AI_RULES_PATHS) {
179
54
  const stalePath = path.join(this.cwd, staleRelPath);
180
55
  if (fs.existsSync(stalePath)) {
181
- if (!dryRun) {
182
- fs.rmSync(stalePath, { recursive: true, force: true });
183
- }
56
+ if (!dryRun) fs.rmSync(stalePath, { recursive: true, force: true });
184
57
  removed.push(staleRelPath);
185
58
  }
186
59
  }
187
60
 
188
- // Print results
189
- if (dryRun) {
190
- console.log('šŸ“‹ Dry run — no files changed\n');
61
+ const outputs = buildOutputs(agents);
62
+ const r = applyOutputs(this.cwd, outputs, { version: sungenVersion(), dryRun, force: opts.force });
63
+
64
+ if (dryRun) console.log('šŸ“‹ Dry run — no files changed\n');
65
+ if (removed.length) console.log(`šŸ—‘ļø Removed retired assets (${removed.length})`);
66
+ if (r.deletedOrphans.length) console.log(`šŸ—‘ļø Removed orphans (${r.deletedOrphans.length}): ${r.deletedOrphans.join(', ')}`);
67
+ if (r.created.length) console.log(`✨ Created (${r.created.length})`);
68
+ if (r.overwritten.length) console.log(`āœļø Updated (${r.overwritten.length})`);
69
+ if (r.skippedConfig.length) console.log(`ā­ļø Config preserved (use --force to overwrite): ${r.skippedConfig.join(', ')}`);
70
+ if (r.handEditWarnings.length) {
71
+ console.log(`\nāš ļø Overwrote files that look hand-edited (sungen owns these — version-managed):`);
72
+ for (const f of r.handEditWarnings) console.log(` ${f}`);
191
73
  }
192
74
 
193
- if (removed.length > 0) {
194
- console.log(`šŸ—‘ļø Removed retired assets (${removed.length}):`);
195
- for (const f of removed) {
196
- console.log(` ${f}`);
197
- }
198
- console.log();
199
- }
200
-
201
- if (updated.length > 0) {
202
- console.log(`āœļø Updated (${updated.length}):`);
203
- for (const f of updated) {
204
- console.log(` ${f}`);
205
- }
206
- console.log();
207
- }
208
-
209
- if (created.length > 0) {
210
- console.log(`✨ Created (${created.length}):`);
211
- for (const f of created) {
212
- console.log(` ${f}`);
213
- }
214
- console.log();
215
- }
216
-
217
- if (unchanged.length > 0) {
218
- console.log(`āœ… Already up to date (${unchanged.length}):`);
219
- for (const f of unchanged) {
220
- console.log(` ${f}`);
221
- }
222
- console.log();
223
- }
224
-
225
- if (missing.length > 0) {
226
- console.log(`āš ļø Template not found (${missing.length}):`);
227
- for (const f of missing) {
228
- console.log(` ${f}`);
229
- }
230
- console.log();
231
- }
232
-
233
- const totalChanges = updated.length + created.length;
234
- if (totalChanges === 0) {
235
- console.log('All AI rules are up to date. No changes needed.');
236
- } else if (dryRun) {
237
- console.log(`${totalChanges} file(s) would be changed. Run without --dry-run to apply.`);
238
- } else {
239
- console.log(`${totalChanges} file(s) updated successfully.`);
240
- }
75
+ const total = r.created.length + r.overwritten.length;
76
+ if (total === 0 && !removed.length && !r.deletedOrphans.length) console.log('All AI rules are up to date. No changes needed.');
77
+ else if (dryRun) console.log(`\n${total} file(s) would change. Run without --dry-run to apply.`);
78
+ else console.log(`\n${total} file(s) updated for ${agents.join(', ')}.`);
241
79
  }
242
80
  }