android2harmony 0.1.6 → 0.1.8

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 (227) hide show
  1. package/agents/api-analyzer.md +113 -0
  2. package/agents/app-action.md +122 -0
  3. package/agents/code-reviewer.md +191 -295
  4. package/agents/coder.md +377 -0
  5. package/agents/self-tester.md +8 -8
  6. package/agents/spec-designer.md +56 -0
  7. package/package.json +1 -1
  8. package/skills/a2h-app-req-gen/SKILL.md +174 -0
  9. package/skills/a2h-app-req-gen/references/spec-handoff.md +99 -0
  10. package/skills/a2h-app-req-gen/references/subagent-render.md +115 -0
  11. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/build.gradle.kts +41 -0
  12. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.jar +0 -0
  13. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.properties +8 -0
  14. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle.properties +3 -0
  15. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew +251 -0
  16. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew.bat +94 -0
  17. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/settings.gradle.kts +17 -0
  18. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawAnalysisDetector.java +1911 -0
  19. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawIssueRegistry.java +37 -0
  20. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawJson.java +146 -0
  21. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawLocation.java +96 -0
  22. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawRecordWriter.java +227 -0
  23. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry +1 -0
  24. package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/dev/reqgen/analyzer/jvm/raw-record-v1.schema.json +93 -0
  25. package/skills/a2h-app-req-gen/scripts/reqgen/assets/dashboard.html +151 -0
  26. package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-lint.init.gradle +60 -0
  27. package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-model.init.gradle +204 -0
  28. package/skills/a2h-app-req-gen/scripts/reqgen/package.json +14 -0
  29. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/backend.js +323 -0
  30. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/cli.js +811 -0
  31. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/gap-audit-link.js +62 -0
  32. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/index.js +66 -0
  33. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/merge.js +605 -0
  34. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/name-trace.js +153 -0
  35. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/orchestrate.js +1324 -0
  36. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/output-parse.js +50 -0
  37. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/overview.js +10 -0
  38. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/pool.js +23 -0
  39. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/post-merge-fix.js +146 -0
  40. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt-loader.js +51 -0
  41. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt.js +967 -0
  42. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompts.md +301 -0
  43. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/serve.js +78 -0
  44. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/short-id.js +46 -0
  45. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/snapshot.js +267 -0
  46. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/types.js +1 -0
  47. package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/validate.js +122 -0
  48. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/callback-call-sites.js +62 -0
  49. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/build.js +1291 -0
  50. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/index.js +1 -0
  51. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/area-ownership.js +392 -0
  52. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/build.js +1995 -0
  53. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/callback-flow.js +277 -0
  54. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/component-variants.js +725 -0
  55. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/index.js +15 -0
  56. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/index.js +12 -0
  57. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/policy.js +469 -0
  58. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/traceability.js +468 -0
  59. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/build.js +1066 -0
  60. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/index.js +1 -0
  61. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android-interaction-registry.js +174 -0
  62. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android.js +1062 -0
  63. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/area-titles.js +678 -0
  64. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/builder.js +350 -0
  65. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-api-registry.js +395 -0
  66. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-call-sites.js +2008 -0
  67. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose.js +9059 -0
  68. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/dynamic-repeated-call-sites.js +529 -0
  69. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/index.js +1 -0
  70. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/interaction-effects.js +1149 -0
  71. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/jvm.js +6272 -0
  72. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/normalize.js +33 -0
  73. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/pending-intents.js +565 -0
  74. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/resource-id.js +42 -0
  75. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/route-state-variants.js +781 -0
  76. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/state.js +234 -0
  77. package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/types.js +1 -0
  78. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/extract.js +40 -0
  79. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/index.js +6 -0
  80. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/manifest.js +513 -0
  81. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/resources.js +398 -0
  82. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/types.js +1 -0
  83. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/views.js +447 -0
  84. package/skills/a2h-app-req-gen/scripts/reqgen/src/android/xml-parser.js +175 -0
  85. package/skills/a2h-app-req-gen/scripts/reqgen/src/cli.js +398 -0
  86. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/area-graph.js +1 -0
  87. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/artifact.js +1 -0
  88. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/common.js +16 -0
  89. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/diagnostic.js +10 -0
  90. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/gap-audit.js +14 -0
  91. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/index.js +11 -0
  92. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/name-traceability.js +26 -0
  93. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/raw-fact.js +40 -0
  94. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/req-model.js +1 -0
  95. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/run.js +19 -0
  96. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/semantic-completeness.js +11 -0
  97. package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/source-facts.js +1 -0
  98. package/skills/a2h-app-req-gen/scripts/reqgen/src/core/output-guard.js +41 -0
  99. package/skills/a2h-app-req-gen/scripts/reqgen/src/core/stable-id.js +92 -0
  100. package/skills/a2h-app-req-gen/scripts/reqgen/src/dashboard-cli.js +169 -0
  101. package/skills/a2h-app-req-gen/scripts/reqgen/src/diagnostics.js +104 -0
  102. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/dump.js +1028 -0
  103. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/index.js +1 -0
  104. package/skills/a2h-app-req-gen/scripts/reqgen/src/dump-cli.js +251 -0
  105. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/artifacts.js +49 -0
  106. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/index.js +18 -0
  107. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/quality-report.js +125 -0
  108. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/render.js +353 -0
  109. package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/types.js +2 -0
  110. package/skills/a2h-app-req-gen/scripts/reqgen/src/graph/paths.js +474 -0
  111. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/index.js +15 -0
  112. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/protocol.js +408 -0
  113. package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/runner.js +452 -0
  114. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/index.js +8 -0
  115. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/output.js +165 -0
  116. package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/run-stage1.js +891 -0
  117. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/discovery.js +299 -0
  118. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-model.js +358 -0
  119. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-runner.js +165 -0
  120. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/resource-profile.js +60 -0
  121. package/skills/a2h-app-req-gen/scripts/reqgen/src/project/types.js +1 -0
  122. package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff-cli.js +151 -0
  123. package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff.js +1004 -0
  124. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-cli.js +109 -0
  125. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-input.js +419 -0
  126. package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-name-trace-input.js +95 -0
  127. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/area-graph.js +444 -0
  128. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/index.js +7 -0
  129. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-model.js +580 -0
  130. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-text.js +195 -0
  131. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/semantic-area-graph.js +1431 -0
  132. package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/types.js +1 -0
  133. package/skills/a2h-app-req-gen/scripts/reqgen-audit.mjs +298 -0
  134. package/skills/a2h-app-req-gen/scripts/reqgen-selfcheck.mjs +555 -0
  135. package/skills/a2h-code-review/SKILL.md +379 -0
  136. package/skills/{hmos-integration-test → a2h-integration-test}/README.md +6 -6
  137. package/skills/{hmos-integration-test → a2h-integration-test}/SKILL.md +6 -6
  138. package/skills/{hmos-integration-test → a2h-integration-test}/scripts/report-tool.mjs +1 -1
  139. package/skills/a2h-spec-design/SKILL.md +330 -0
  140. package/skills/a2h-spec-design/scripts/inspect_spec_design_inputs.mjs +207 -0
  141. package/skills/a2h-spec-design/scripts/move_spec_technical_reference.mjs +198 -0
  142. package/skills/a2h-spec-design/scripts/validate_spec_design_output.mjs +256 -0
  143. package/skills/a2h-spec-generate/SKILL.md +696 -0
  144. package/skills/a2h-spec-generate/references/trace-template.md +115 -0
  145. package/skills/a2h-spec-generate/scripts/parse_requirements.mjs +537 -0
  146. package/skills/a2h-spec-generate/template/REQ.txt +22 -0
  147. package/skills/a2h-spec-generate/template/REQ.xlsx +0 -0
  148. package/skills/a2h-spec-implement/SKILL.md +548 -0
  149. package/skills/a2h-spec-implement/references/harmony-pitfalls.md +186 -0
  150. package/skills/a2h-spec-implement/references/l1-unit-test.md +185 -0
  151. package/skills/a2h-spec-implement/references/l3-e2e-uitest.md +167 -0
  152. package/skills/a2h-spec-implement/references/test-core.md +132 -0
  153. package/skills/a2h-spec-implement/rules/arkts/arkts-standards.md +184 -0
  154. package/skills/a2h-spec-implement/rules/arkts/conventions/coding-style.md +96 -0
  155. package/skills/a2h-spec-implement/rules/arkts/conventions/security.md +126 -0
  156. package/skills/a2h-spec-implement/rules/arkts/language/arkts-rules.md +650 -0
  157. package/skills/a2h-spec-implement/rules/arkts/language/arkui-structure-rules.md +173 -0
  158. package/skills/a2h-spec-implement/rules/arkts/language/ts-to-arkts-rewrites.md +58 -0
  159. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
  160. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
  161. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
  162. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
  163. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
  164. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
  165. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
  166. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
  167. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
  168. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
  169. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
  170. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
  171. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
  172. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md +216 -0
  173. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Computed/350/243/205/351/245/260/345/231/250/357/274/232/350/256/241/347/256/227/345/261/236/346/200/247.md +442 -0
  174. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Event/350/243/205/351/245/260/345/231/250/357/274/232/350/247/204/350/214/203/347/273/204/344/273/266/350/276/223/345/207/272.md +169 -0
  175. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Local/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/351/203/250/347/212/266/346/200/201.md +763 -0
  176. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Monitor/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/344/277/256/346/224/271/345/274/202/346/255/245/347/233/221/345/220/254.md +2088 -0
  177. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@ObservedV2/350/243/205/351/245/260/345/231/250/345/222/214@Trace/350/243/205/351/245/260/345/231/250/357/274/232/347/261/273/345/261/236/346/200/247/345/217/230/345/214/226/350/247/202/346/265/213.md +1258 -0
  178. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Once/357/274/232/345/210/235/345/247/213/345/214/226/345/220/214/346/255/245/344/270/200/346/254/241.md +175 -0
  179. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Param/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md +850 -0
  180. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Provider/350/243/205/351/245/260/345/231/250/345/222/214@Consumer/350/243/205/351/245/260/345/231/250/357/274/232/350/267/250/347/273/204/344/273/266/345/261/202/347/272/247/345/217/214/345/220/221/345/220/214/346/255/245.md +862 -0
  181. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Type/350/243/205/351/245/260/345/231/250/357/274/232/346/240/207/350/256/260/347/261/273/345/261/236/346/200/247/347/232/204/347/261/273/345/236/213.md +110 -0
  182. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/AppStorageV2/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200UI/347/212/266/346/200/201/345/255/230/345/202/250.md +301 -0
  183. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md +1411 -0
  184. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/PersistenceV2/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +1392 -0
  185. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/getTarget/346/216/245/345/217/243/357/274/232/350/216/267/345/217/226/347/212/266/346/200/201/347/256/241/347/220/206/346/241/206/346/236/266/344/273/243/347/220/206/345/211/215/347/232/204/345/216/237/345/247/213/345/257/271/350/261/241.md +288 -0
  186. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/makeObserved/346/216/245/345/217/243/357/274/232/345/260/206/351/235/236/350/247/202/345/257/237/346/225/260/346/215/256/345/217/230/344/270/272/345/217/257/350/247/202/345/257/237/346/225/260/346/215/256.md +768 -0
  187. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/222/214V2/346/267/267/347/224/250/346/214/207/345/257/274/357/274/210API version 19/345/217/212/344/271/213/345/220/216/357/274/211.md" +829 -0
  188. package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206/346/246/202/350/277/260.md +184 -0
  189. package/skills/a2h-spec-implement/rules/arkts/ui/component-cookbook.md +431 -0
  190. package/skills/a2h-spec-implement/rules/arkts/ui/state-management.md +152 -0
  191. package/skills/a2h-spec-implement/rules/arkts/ui/ui-quality.md +67 -0
  192. package/skills/a2h-spec-implement/rules/arkts/ui/ui-runtime-diagnosis.md +32 -0
  193. package/skills/a2h-spec-implement/rules/pipeline/build/package-set-collection.md +43 -0
  194. package/skills/a2h-spec-implement/scripts/_lib/common.mjs +97 -0
  195. package/skills/a2h-spec-implement/scripts/_lib/config.mjs +70 -0
  196. package/skills/a2h-spec-implement/scripts/_lib/lessons_cli.mjs +48 -0
  197. package/skills/a2h-spec-implement/scripts/build_lessons.mjs +24 -0
  198. package/skills/a2h-spec-implement/scripts/code_pattern_lessons.mjs +26 -0
  199. package/skills/a2h-spec-implement/scripts/device_ui.mjs +433 -0
  200. package/skills/a2h-spec-implement/scripts/device_ui_core.mjs +895 -0
  201. package/skills/a2h-spec-implement/scripts/ledger/aggregate_run.mjs +207 -0
  202. package/skills/a2h-spec-implement/scripts/manifest/emit_summary_line.mjs +37 -0
  203. package/skills/a2h-spec-implement/scripts/manifest/finalize_run.mjs +115 -0
  204. package/skills/a2h-spec-implement/scripts/manifest/render_final_summary.mjs +78 -0
  205. package/skills/a2h-spec-implement/scripts/manifest/root_mirror.mjs +137 -0
  206. package/skills/a2h-spec-implement/scripts/package.json +12 -0
  207. package/skills/a2h-spec-implement/scripts/state_propagation_check.mjs +676 -0
  208. package/skills/a2h-spec-implement/scripts/test/l2_run.mjs +244 -0
  209. package/skills/a2h-spec-implement/scripts/test/tp_coverage.mjs +231 -0
  210. package/skills/a2h-spec-implement/scripts/ui_memory.mjs +161 -0
  211. package/skills/a2h-spec-implement/scripts/utils/ensure_app.mjs +134 -0
  212. package/skills/a2h-ui-transfer/SKILL.md +20 -9
  213. package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +80 -0
  214. package/skills/hmos-convert-pipeline/SKILL.md +7 -5
  215. package/skills/hmos-incremental-ui-align/README.md +7 -7
  216. package/skills/hmos-incremental-ui-align/SKILL.md +1 -1
  217. package/skills/hmos-incremental-ui-align/page_align.md +1 -1
  218. package/skills/hmos-spec-generate/SKILL.md +2 -2
  219. package/skills/hmos-spec-generate/scripts/parse_requirements.mjs +537 -0
  220. package/skills/hmos-fix-build-errors/SKILL.md +0 -266
  221. package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +0 -219
  222. package/skills/hmos-fix-build-errors/references/known-patterns.md +0 -157
  223. package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +0 -131
  224. package/skills/hmos-spec-generate/scripts/parse_requirements.ts +0 -515
  225. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/resolve-metadata-tool.mjs +0 -0
  226. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/self-test-runner.mjs +0 -0
  227. /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/testcases-tool.mjs +0 -0
@@ -0,0 +1,605 @@
1
+ import {
2
+ buildAreaContentTxt,
3
+ renderedPath,
4
+ serializeReqMarkdown,
5
+ serializeReqTxtAreaContent,
6
+ } from "../generation/index.js";
7
+ import { expandRenderItems } from "./orchestrate.js";
8
+ // Verbatim port: models sometimes repeat the title on the first body line
9
+ // even though the prompt forbids it.
10
+ export function stripLeadingTitle(body, title) {
11
+ const trimmed = body.replace(/^\s+/, "");
12
+ if (!trimmed.startsWith(title)) return body;
13
+ return trimmed.slice(title.length).replace(/^\s*\n+/, "");
14
+ }
15
+ /** Feature ids `filterRenderableAreas` kept for this run (i.e. actually
16
+ * eligible to be sent to the Agent). A Feature outside this set was never
17
+ * attempted — it is omitted from the merged output rather than falling back
18
+ * to the Stage 1 draft, which is reserved for Features the Agent attempted
19
+ * and failed. */
20
+ function collectFeatureIds(reqModel) {
21
+ return new Set(
22
+ reqModel.areas.flatMap((area) =>
23
+ area.features.map((feature) => feature.id),
24
+ ),
25
+ );
26
+ }
27
+ function selectEntry(
28
+ item,
29
+ results,
30
+ templateTitle,
31
+ templateBody,
32
+ renderableFeatureIds,
33
+ ) {
34
+ if (!renderableFeatureIds.has(item.feature.id)) return undefined;
35
+ const result = results.get(item.feature.id);
36
+ if (result?.status !== "ok" || result.body === undefined) {
37
+ return { title: templateTitle, body: templateBody };
38
+ }
39
+ const title =
40
+ result.titleValid === true && result.title !== undefined
41
+ ? result.title
42
+ : templateTitle;
43
+ return { title, body: result.body };
44
+ }
45
+ function selectEntryFromArea(
46
+ featureId,
47
+ areaResults,
48
+ templateTitle,
49
+ templateBody,
50
+ renderableFeatureIds,
51
+ ) {
52
+ if (!renderableFeatureIds.has(featureId)) return undefined;
53
+ // Find the feature result across all area results
54
+ for (const areaResult of areaResults.values()) {
55
+ if (areaResult.status !== "ok") continue;
56
+ const featureResult = areaResult.features.get(featureId);
57
+ if (featureResult === undefined) continue;
58
+ if (featureResult.status !== "ok" || featureResult.body === undefined) {
59
+ return { title: templateTitle, body: templateBody };
60
+ }
61
+ const title =
62
+ featureResult.titleValid === true && featureResult.title !== undefined
63
+ ? featureResult.title
64
+ : templateTitle;
65
+ return { title, body: featureResult.body };
66
+ }
67
+ return { title: templateTitle, body: templateBody };
68
+ }
69
+ function findFeatureAttemptResult(featureId, areaResults) {
70
+ for (const areaResult of areaResults.values()) {
71
+ if (areaResult.status !== "ok") continue;
72
+ const featureResult = areaResult.features.get(featureId);
73
+ if (featureResult !== undefined) return featureResult;
74
+ }
75
+ return undefined;
76
+ }
77
+ function addedEntriesForArea(areaId, areaResults) {
78
+ const result = areaResults.get(areaId);
79
+ if (result?.status !== "ok") return [];
80
+ return (result.addedFeatures ?? []).flatMap((feature) =>
81
+ feature.status === "ok" &&
82
+ feature.title !== undefined &&
83
+ feature.body !== undefined
84
+ ? [{ title: feature.title, body: feature.body }]
85
+ : [],
86
+ );
87
+ }
88
+ /** Feature ids whose own template slot must be skipped (never emitted) —
89
+ * every non-earliest member of a merge group the render Agent reported (see
90
+ * FeatureAttemptResult.mergedFeatureIds). A merge group's content is
91
+ * rendered exactly once, at the position of whichever member appears first
92
+ * in Area.features order; later members' 3-line slots are still consumed
93
+ * from the template (cursor still advances) but push nothing. Standalone
94
+ * (non-merged) Features are never in this set — selectEntryFromArea still
95
+ * decides per its own rules (draft fallback / renderableFeatureIds) whether
96
+ * they actually emit. */
97
+ function planSkippedMergeSlots(area, areaResults) {
98
+ const skip = new Set();
99
+ const seenGroups = new Set();
100
+ for (const feature of area.features) {
101
+ const result = findFeatureAttemptResult(feature.id, areaResults);
102
+ const mergedIds =
103
+ result?.status === "ok" ? result.mergedFeatureIds : undefined;
104
+ if (mergedIds === undefined || mergedIds.length <= 1) continue;
105
+ const groupKey = [...mergedIds].sort().join(" ");
106
+ if (seenGroups.has(groupKey)) {
107
+ skip.add(feature.id);
108
+ } else {
109
+ seenGroups.add(groupKey);
110
+ }
111
+ }
112
+ return skip;
113
+ }
114
+ function assertTemplateShape(lines, index, areaId) {
115
+ if (lines[index] === undefined) {
116
+ throw new Error(`template REQ text ended unexpectedly at Area ${areaId}`);
117
+ }
118
+ }
119
+ /** Area heads are taken verbatim from the Stage 1 deterministic draft
120
+ * (renderReqDraft output, recomputed by the caller rather than read from
121
+ * req.md); Feature entries use the Agent title+body when the render
122
+ * succeeded, the draft when the Agent attempted and failed, and are omitted
123
+ * entirely when `renderableReqModel` never sent the Feature to the Agent in
124
+ * the first place (e.g. a navigation-only Feature dropped by
125
+ * filterRenderableAreas) — a Feature the Agent never saw does not get its
126
+ * draft text pasted in as if it had been considered. */
127
+ export function mergeReqText(
128
+ fullReqModel,
129
+ renderableReqModel,
130
+ results,
131
+ templateText,
132
+ ) {
133
+ const lines = templateText.split("\n");
134
+ const items = expandRenderItems(fullReqModel);
135
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
136
+ const out = [];
137
+ let cursor = 0;
138
+ let itemIndex = 0;
139
+ fullReqModel.areas.forEach((area, areaIndex) => {
140
+ while (!lines[cursor].startsWith("区域说明:")) {
141
+ assertTemplateShape(lines, cursor, area.id);
142
+ out.push(lines[cursor]);
143
+ cursor += 1;
144
+ }
145
+ out.push(lines[cursor]);
146
+ cursor += 1;
147
+ for (const feature of area.features) {
148
+ const item = items[itemIndex];
149
+ itemIndex += 1;
150
+ const templateTitle = lines[cursor + 1];
151
+ const templateBody = lines[cursor + 2];
152
+ assertTemplateShape(lines, cursor + 2, feature.id);
153
+ const entry = selectEntry(
154
+ item,
155
+ results,
156
+ templateTitle,
157
+ templateBody,
158
+ renderableFeatureIds,
159
+ );
160
+ if (entry !== undefined) out.push("", entry.title, entry.body);
161
+ cursor += 3;
162
+ }
163
+ if (areaIndex < fullReqModel.areas.length - 1) {
164
+ out.push("");
165
+ cursor += 1;
166
+ }
167
+ });
168
+ return `${out.join("\n")}\n`;
169
+ }
170
+ /** Same walk as mergeReqText, but returns the Area-blocked Markdown form
171
+ * (req.md) directly from the structured {title, body} entries, instead of
172
+ * flattening into a single string and re-parsing it — an Agent-rendered
173
+ * body may legitimately contain multiple lines or embedded blank lines
174
+ * (e.g. Law 4's 枚举行 per `options` entry in prompt.ts), which a re-parse
175
+ * of flat text cannot reliably un-flatten.
176
+ *
177
+ * Per-feature mode has no per-Area Agent reachability judgment, so
178
+ * isAgentJudgedUnreachable falls back to static analysis
179
+ * (canonicalPath.status === "unreachable") — the same default-drop
180
+ * behaviour as mergeAreaReqMarkdown, controlled by the same
181
+ * MergeAreaOptions.includeUnreachableAreas flag. */
182
+ export function mergeReqMarkdown(
183
+ fullReqModel,
184
+ renderableReqModel,
185
+ results,
186
+ templateText,
187
+ options = {},
188
+ ) {
189
+ const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
190
+ const lines = templateText.split("\n");
191
+ const items = expandRenderItems(fullReqModel);
192
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
193
+ const blocks = [];
194
+ let cursor = 0;
195
+ let itemIndex = 0;
196
+ fullReqModel.areas.forEach((area, areaIndex) => {
197
+ const headerLines = [];
198
+ while (!lines[cursor].startsWith("区域说明:")) {
199
+ assertTemplateShape(lines, cursor, area.id);
200
+ headerLines.push(lines[cursor]);
201
+ cursor += 1;
202
+ }
203
+ headerLines.push(lines[cursor]);
204
+ cursor += 1;
205
+ const features = [];
206
+ for (const feature of area.features) {
207
+ const item = items[itemIndex];
208
+ itemIndex += 1;
209
+ const templateTitle = lines[cursor + 1];
210
+ const templateBody = lines[cursor + 2];
211
+ assertTemplateShape(lines, cursor + 2, feature.id);
212
+ const entry = selectEntry(
213
+ item,
214
+ results,
215
+ templateTitle,
216
+ templateBody,
217
+ renderableFeatureIds,
218
+ );
219
+ if (entry !== undefined) features.push(entry);
220
+ cursor += 3;
221
+ }
222
+ blocks.push({ headerLines, features });
223
+ if (areaIndex < fullReqModel.areas.length - 1) cursor += 1;
224
+ });
225
+ // Per-feature mode has no AreaAttemptResult — pass an empty Map so
226
+ // isAgentJudgedUnreachable falls back to static analysis.
227
+ const ordered = partitionAreaBlocks(
228
+ fullReqModel.areas,
229
+ blocks,
230
+ new Map(),
231
+ includeUnreachableAreas,
232
+ );
233
+ return serializeReqMarkdown(ordered);
234
+ }
235
+ function selectAreaDescriptionLine(areaId, areaResults, templateLine) {
236
+ const result = areaResults.get(areaId);
237
+ const description =
238
+ result?.status === "ok" ? result.areaDescription?.trim() : undefined;
239
+ return description === undefined || description.length === 0
240
+ ? templateLine
241
+ : `区域说明:${description}`;
242
+ }
243
+ /** The Agent owns the final source-grounded 访问路径 for every Area. Retain
244
+ * the deterministic draft only when that Agent response has no accessPath. */
245
+ function selectAccessPathLine(area, areaResults, templateLine) {
246
+ const result = areaResults.get(area.id);
247
+ const accessPath =
248
+ result?.status === "ok" ? result.accessPath?.trim() : undefined;
249
+ return accessPath === undefined || accessPath.length === 0
250
+ ? templateLine
251
+ : `访问路径:${accessPath}`;
252
+ }
253
+ /** True for an Area that should sink to the end of req.md: it was
254
+ * statically unreachable AND the Agent either never confirmed a real access
255
+ * path (no result, a failed attempt, or an explicit "confirmed unreachable"
256
+ * judgment) — conservative default when there is no better information.
257
+ * Areas the Agent judged reachable (reachable: true, alongside a found
258
+ * accessPath) join the reachable group instead, ahead of any Area still
259
+ * judged unreachable. Never affects Areas that were reachable by static
260
+ * analysis in the first place — those are always in the reachable group. */
261
+ function isAgentJudgedUnreachable(area, areaResults) {
262
+ if (area.canonicalPath.status !== "unreachable") return false;
263
+ const result = areaResults.get(area.id);
264
+ return !(result?.status === "ok" && result.reachable === true);
265
+ }
266
+ /** Stable partition of already-assembled per-Area blocks: Areas judged
267
+ * reachable first (original relative order), then — only when
268
+ * includeUnreachableAreas is true — Areas judged unreachable (original
269
+ * relative order) appended after them; otherwise those blocks are dropped
270
+ * — see isAgentJudgedUnreachable. Runs only after both the deterministic
271
+ * template and the per-Area merge walk have already matched every block to
272
+ * its source Area positionally, so it cannot desync that walk (it only
273
+ * reorders/drops whole, already-built blocks). */
274
+ function partitionAreaBlocks(
275
+ originalAreas,
276
+ blocks,
277
+ areaResults,
278
+ includeUnreachableAreas,
279
+ ) {
280
+ const reachable = [];
281
+ const unreachable = [];
282
+ originalAreas.forEach((area, index) => {
283
+ (isAgentJudgedUnreachable(area, areaResults)
284
+ ? unreachable
285
+ : reachable
286
+ ).push(blocks[index]);
287
+ });
288
+ return includeUnreachableAreas ? [...reachable, ...unreachable] : reachable;
289
+ }
290
+ /** Merge per-Area rendering results into the Stage 1 deterministic draft
291
+ * (renderReqDraft output, recomputed by the caller rather than read from
292
+ * req.md): Area heads verbatim except 区域说明 (Agent-produced when the
293
+ * session succeeded, else draft fallback) and 访问路径 for unreachable Areas
294
+ * (Agent-produced access finding/confirmation when available, else draft
295
+ * fallback); Feature entries from Agent results when available, the draft
296
+ * when the Agent attempted and failed, and omitted entirely when
297
+ * `renderableReqModel` never sent the Feature to the Agent at all (see
298
+ * mergeReqText). Areas the Agent judges unreachable (see
299
+ * isAgentJudgedUnreachable) are dropped by default, or moved after every
300
+ * Area it judges reachable when `options.includeUnreachableAreas` is set
301
+ * (see MergeAreaOptions). */
302
+ export function mergeAreaReqText(
303
+ fullReqModel,
304
+ renderableReqModel,
305
+ areaResults,
306
+ templateText,
307
+ options = {},
308
+ ) {
309
+ const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
310
+ const lines = templateText.split("\n");
311
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
312
+ const blocks = [];
313
+ let cursor = 0;
314
+ fullReqModel.areas.forEach((area) => {
315
+ const block = [];
316
+ while (!lines[cursor].startsWith("区域说明:")) {
317
+ assertTemplateShape(lines, cursor, area.id);
318
+ block.push(
319
+ lines[cursor].startsWith("访问路径:")
320
+ ? selectAccessPathLine(area, areaResults, lines[cursor])
321
+ : lines[cursor],
322
+ );
323
+ cursor += 1;
324
+ }
325
+ block.push(selectAreaDescriptionLine(area.id, areaResults, lines[cursor]));
326
+ cursor += 1;
327
+ const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
328
+ for (const feature of area.features) {
329
+ const templateTitle = lines[cursor + 1];
330
+ const templateBody = lines[cursor + 2];
331
+ assertTemplateShape(lines, cursor + 2, feature.id);
332
+ const entry = skippedMergeSlots.has(feature.id)
333
+ ? undefined
334
+ : selectEntryFromArea(
335
+ feature.id,
336
+ areaResults,
337
+ templateTitle,
338
+ templateBody,
339
+ renderableFeatureIds,
340
+ );
341
+ if (entry !== undefined) block.push("", entry.title, entry.body);
342
+ cursor += 3;
343
+ }
344
+ for (const entry of addedEntriesForArea(area.id, areaResults)) {
345
+ block.push("", entry.title, entry.body);
346
+ }
347
+ blocks.push(block);
348
+ });
349
+ const ordered = partitionAreaBlocks(
350
+ fullReqModel.areas,
351
+ blocks,
352
+ areaResults,
353
+ includeUnreachableAreas,
354
+ );
355
+ return `${ordered.map((block) => block.join("\n")).join("\n\n")}\n`;
356
+ }
357
+ /** Same walk as mergeAreaReqText, but returns the Area-blocked Markdown form
358
+ * directly from structured entries — see mergeReqMarkdown for why this
359
+ * avoids re-parsing a flattened, potentially multi-line Agent body. Areas
360
+ * the Agent judges unreachable are dropped by default, or moved after every
361
+ * Area it judges reachable when `options.includeUnreachableAreas` is set
362
+ * (see MergeAreaOptions, same as mergeAreaReqText). */
363
+ export function mergeAreaReqMarkdown(
364
+ fullReqModel,
365
+ renderableReqModel,
366
+ areaResults,
367
+ templateText,
368
+ options = {},
369
+ ) {
370
+ const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
371
+ const lines = templateText.split("\n");
372
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
373
+ const blocks = [];
374
+ let cursor = 0;
375
+ fullReqModel.areas.forEach((area, areaIndex) => {
376
+ const headerLines = [];
377
+ while (!lines[cursor].startsWith("区域说明:")) {
378
+ assertTemplateShape(lines, cursor, area.id);
379
+ headerLines.push(
380
+ lines[cursor].startsWith("访问路径:")
381
+ ? selectAccessPathLine(area, areaResults, lines[cursor])
382
+ : lines[cursor],
383
+ );
384
+ cursor += 1;
385
+ }
386
+ headerLines.push(
387
+ selectAreaDescriptionLine(area.id, areaResults, lines[cursor]),
388
+ );
389
+ cursor += 1;
390
+ const features = [];
391
+ const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
392
+ for (const feature of area.features) {
393
+ const templateTitle = lines[cursor + 1];
394
+ const templateBody = lines[cursor + 2];
395
+ assertTemplateShape(lines, cursor + 2, feature.id);
396
+ const entry = skippedMergeSlots.has(feature.id)
397
+ ? undefined
398
+ : selectEntryFromArea(
399
+ feature.id,
400
+ areaResults,
401
+ templateTitle,
402
+ templateBody,
403
+ renderableFeatureIds,
404
+ );
405
+ if (entry !== undefined) features.push(entry);
406
+ cursor += 3;
407
+ }
408
+ features.push(...addedEntriesForArea(area.id, areaResults));
409
+ blocks.push({ headerLines, features });
410
+ if (areaIndex < fullReqModel.areas.length - 1) cursor += 1;
411
+ });
412
+ const ordered = partitionAreaBlocks(
413
+ fullReqModel.areas,
414
+ blocks,
415
+ areaResults,
416
+ includeUnreachableAreas,
417
+ );
418
+ return serializeReqMarkdown(ordered);
419
+ }
420
+ /** Build the suffix part of a txt-format title (without the access path
421
+ * prefix). The template title is `{path} - {areaName} - {controlName}`;
422
+ * this returns `{areaName} - {controlName}`. */
423
+ function titleSuffix(templateTitle, path) {
424
+ const prefix = `${path} - `;
425
+ return templateTitle.startsWith(prefix)
426
+ ? templateTitle.slice(prefix.length)
427
+ : templateTitle;
428
+ }
429
+ /** Txt-format equivalent of selectEntryFromArea: returns the merged
430
+ * {title, body} for one Feature, preferring Agent output over the template.
431
+ * Agent titles are already complete navigation titles, so they are not
432
+ * prefixed with an independently rendered access path. */
433
+ function selectEntryFromAreaTxt(
434
+ featureId,
435
+ areaResults,
436
+ templateTitle,
437
+ templateBody,
438
+ accessPath,
439
+ renderableFeatureIds,
440
+ ) {
441
+ if (!renderableFeatureIds.has(featureId)) return undefined;
442
+ for (const areaResult of areaResults.values()) {
443
+ if (areaResult.status !== "ok") continue;
444
+ const featureResult = areaResult.features.get(featureId);
445
+ if (featureResult === undefined) continue;
446
+ if (featureResult.status !== "ok" || featureResult.body === undefined) {
447
+ return { title: templateTitle, body: templateBody };
448
+ }
449
+ const agentTitle =
450
+ featureResult.titleValid === true && featureResult.title !== undefined
451
+ ? featureResult.title
452
+ : titleSuffix(templateTitle, accessPath);
453
+ return {
454
+ title:
455
+ featureResult.titleValid === true && featureResult.title !== undefined
456
+ ? agentTitle
457
+ : `${accessPath} - ${agentTitle}`,
458
+ body: featureResult.body,
459
+ };
460
+ }
461
+ return { title: templateTitle, body: templateBody };
462
+ }
463
+ /** Merge per-Area rendering results into per-area plain-text strings (one
464
+ * per Area). Operates on structured data (buildAreaContentTxt) rather
465
+ * than walking template text. Areas the Agent judges unreachable are
466
+ * dropped by default; pass includeUnreachableAreas to keep them. */
467
+ export function mergeAreaReqTxt(
468
+ fullReqModel,
469
+ renderableReqModel,
470
+ areaResults,
471
+ graph,
472
+ options = {},
473
+ ) {
474
+ return mergeAreaReqContents(
475
+ fullReqModel,
476
+ renderableReqModel,
477
+ areaResults,
478
+ graph,
479
+ options,
480
+ ).map(({ content }) => serializeReqTxtAreaContent(content));
481
+ }
482
+ /**
483
+ * Merge per-Area rendering results without flattening them to text. This is
484
+ * the canonical source for downstream adapters; `mergeAreaReqTxt` delegates
485
+ * to it so both paths have exactly the same fallback, reachability and
486
+ * Agent-added Feature semantics.
487
+ */
488
+ export function mergeAreaReqContents(
489
+ fullReqModel,
490
+ renderableReqModel,
491
+ areaResults,
492
+ graph,
493
+ options = {},
494
+ ) {
495
+ const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
496
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
497
+ const contents = fullReqModel.areas.map((area) => {
498
+ const result = areaResults.get(area.id);
499
+ const template = buildAreaContentTxt(area, graph);
500
+ const detPath = renderedPath(area, graph);
501
+ // Use the Agent's complete source-grounded access path when available,
502
+ // otherwise keep the deterministic fallback path.
503
+ const accessPath =
504
+ (result?.status === "ok" && result.accessPath?.trim()) || detPath;
505
+ // Rebuild headerLines (preconditions only — area description is dropped).
506
+ const headerLines = template.headerLines;
507
+ // Features: prefer Agent output, fall back to template.
508
+ const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
509
+ const features = [];
510
+ for (let i = 0; i < area.features.length; i++) {
511
+ const feature = area.features[i];
512
+ if (skippedMergeSlots.has(feature.id)) continue;
513
+ const templateFeature = template.features[i];
514
+ // Rebuild template title with the (possibly overridden) access path.
515
+ const tplTitle = `${accessPath} - ${titleSuffix(templateFeature.title, detPath)}`;
516
+ const entry = selectEntryFromAreaTxt(
517
+ feature.id,
518
+ areaResults,
519
+ tplTitle,
520
+ templateFeature.body,
521
+ accessPath,
522
+ renderableFeatureIds,
523
+ );
524
+ if (entry !== undefined) features.push(entry);
525
+ }
526
+ features.push(...addedEntriesForArea(area.id, areaResults));
527
+ return { areaId: area.id, content: { headerLines, features } };
528
+ });
529
+ const ordered = partitionAreaBlocks(
530
+ fullReqModel.areas,
531
+ contents,
532
+ areaResults,
533
+ includeUnreachableAreas,
534
+ );
535
+ return ordered.filter((entry) => entry.content.features.length > 0);
536
+ }
537
+ /** Merge per-Feature rendering results into per-area plain-text strings.
538
+ * Like mergeAreaReqTxt but results are keyed by Feature id (per-feature
539
+ * rendering mode). Unreachable Areas are dropped by default (same as
540
+ * mergeAreaReqTxt), controlled by MergeAreaOptions.includeUnreachableAreas. */
541
+ export function mergeReqTxt(
542
+ fullReqModel,
543
+ renderableReqModel,
544
+ results,
545
+ graph,
546
+ options = {},
547
+ ) {
548
+ return mergeReqContents(
549
+ fullReqModel,
550
+ renderableReqModel,
551
+ results,
552
+ graph,
553
+ options,
554
+ ).map(({ content }) => serializeReqTxtAreaContent(content));
555
+ }
556
+ /** Structured counterpart of `mergeReqTxt` for per-Feature rendering. */
557
+ export function mergeReqContents(
558
+ fullReqModel,
559
+ renderableReqModel,
560
+ results,
561
+ graph,
562
+ options = {},
563
+ ) {
564
+ const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
565
+ const renderableFeatureIds = collectFeatureIds(renderableReqModel);
566
+ const contents = fullReqModel.areas.map((area) => {
567
+ const template = buildAreaContentTxt(area, graph);
568
+ const detPath = renderedPath(area, graph);
569
+ const accessPath = detPath;
570
+ const headerLines = template.headerLines;
571
+ const features = [];
572
+ for (let i = 0; i < area.features.length; i++) {
573
+ const feature = area.features[i];
574
+ if (!renderableFeatureIds.has(feature.id)) continue;
575
+ const templateFeature = template.features[i];
576
+ const result = results.get(feature.id);
577
+ const tplTitle = `${accessPath} - ${titleSuffix(templateFeature.title, detPath)}`;
578
+ if (result?.status === "ok" && result.body !== undefined) {
579
+ const agentTitle =
580
+ result.titleValid === true && result.title !== undefined
581
+ ? result.title
582
+ : titleSuffix(templateFeature.title, accessPath);
583
+ features.push({
584
+ title:
585
+ result.titleValid === true && result.title !== undefined
586
+ ? agentTitle
587
+ : `${accessPath} - ${agentTitle}`,
588
+ body: result.body,
589
+ });
590
+ } else {
591
+ features.push({ title: tplTitle, body: templateFeature.body });
592
+ }
593
+ }
594
+ return { areaId: area.id, content: { headerLines, features } };
595
+ });
596
+ // Per-feature mode has no AreaAttemptResult — pass an empty Map so
597
+ // isAgentJudgedUnreachable falls back to static analysis.
598
+ const ordered = partitionAreaBlocks(
599
+ fullReqModel.areas,
600
+ contents,
601
+ new Map(),
602
+ includeUnreachableAreas,
603
+ );
604
+ return ordered.filter((entry) => entry.content.features.length > 0);
605
+ }