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,452 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readdirSync,
5
+ rmSync,
6
+ statSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { hardFailure } from "../diagnostics.js";
12
+ import { runGradle } from "../project/gradle-runner.js";
13
+ import { parseJvmNdjson, readJvmNdjsonFile } from "./protocol.js";
14
+ export const RAW_JVM_FILE_NAME = "raw-jvm.ndjson";
15
+ const DEFAULT_LINT_INIT_SCRIPT = resolve(
16
+ dirname(fileURLToPath(import.meta.url)),
17
+ "..",
18
+ "..",
19
+ "gradle",
20
+ "reqgen-lint.init.gradle",
21
+ );
22
+ export const jvmRunnerErrorCodes = {
23
+ analyzerBuildFailed: "JVM_ANALYZER_BUILD_FAILED",
24
+ analyzerJarMissing: "JVM_ANALYZER_JAR_MISSING",
25
+ analyzerJarAmbiguous: "JVM_ANALYZER_JAR_AMBIGUOUS",
26
+ invalidTarget: "JVM_TARGET_INVALID",
27
+ pathInaccessible: "JVM_PATH_INACCESSIBLE",
28
+ outputPrepareFailed: "JVM_OUTPUT_PREPARE_FAILED",
29
+ lintFailed: "JVM_LINT_FAILED",
30
+ outputMissing: "JVM_OUTPUT_MISSING",
31
+ mergeIncompatible: "JVM_MERGE_INCOMPATIBLE",
32
+ mergeIdConflict: "JVM_MERGE_ID_CONFLICT",
33
+ mergeWriteFailed: "JVM_MERGE_WRITE_FAILED",
34
+ };
35
+ const DIAGNOSTIC_LOG_LIMIT = 16_000;
36
+ function buildFailure(code, message, details) {
37
+ throw hardFailure(code, "uast", message, undefined, details);
38
+ }
39
+ function findAnalyzerJar(analyzerProjectRoot, artifactPath) {
40
+ if (artifactPath) {
41
+ const explicit = resolve(analyzerProjectRoot, artifactPath);
42
+ if (!existsSync(explicit) || !statSync(explicit).isFile()) {
43
+ return buildFailure(
44
+ jvmRunnerErrorCodes.analyzerJarMissing,
45
+ `JVM analyzer Jar is missing: ${explicit}`,
46
+ { path: explicit },
47
+ );
48
+ }
49
+ return explicit;
50
+ }
51
+ const libraryDirectory = join(analyzerProjectRoot, "build", "libs");
52
+ const candidates = existsSync(libraryDirectory)
53
+ ? readdirSync(libraryDirectory, { withFileTypes: true })
54
+ .filter(
55
+ (entry) =>
56
+ entry.isFile() &&
57
+ entry.name.endsWith(".jar") &&
58
+ !/-(?:sources|javadoc|tests)\.jar$/u.test(entry.name),
59
+ )
60
+ .map((entry) => resolve(libraryDirectory, entry.name))
61
+ .sort()
62
+ : [];
63
+ if (candidates.length === 0) {
64
+ return buildFailure(
65
+ jvmRunnerErrorCodes.analyzerJarMissing,
66
+ "Gradle completed without producing the JVM analyzer Jar.",
67
+ { libraryDirectory },
68
+ );
69
+ }
70
+ if (candidates.length !== 1) {
71
+ return buildFailure(
72
+ jvmRunnerErrorCodes.analyzerJarAmbiguous,
73
+ "Gradle produced more than one candidate JVM analyzer Jar.",
74
+ { candidates },
75
+ );
76
+ }
77
+ return candidates[0];
78
+ }
79
+ export async function buildJvmAnalyzer(options) {
80
+ const analyzerProjectRoot = resolve(options.analyzerProjectRoot);
81
+ const executeGradle = options.executeGradle ?? runGradle;
82
+ const gradleResult = await executeGradle(
83
+ analyzerProjectRoot,
84
+ ["--no-daemon", "--console=plain", "jar"],
85
+ options.gradle,
86
+ );
87
+ if (gradleResult.exitCode !== 0) {
88
+ return buildFailure(
89
+ jvmRunnerErrorCodes.analyzerBuildFailed,
90
+ "Gradle could not build the JVM analyzer Jar.",
91
+ {
92
+ command: gradleResult.execution.displayCommand,
93
+ stdoutTail: gradleResult.stdout.slice(-DIAGNOSTIC_LOG_LIMIT),
94
+ stderrTail: gradleResult.stderr.slice(-DIAGNOSTIC_LOG_LIMIT),
95
+ },
96
+ );
97
+ }
98
+ return {
99
+ jarPath: findAnalyzerJar(analyzerProjectRoot, options.artifactPath),
100
+ gradleResult,
101
+ };
102
+ }
103
+ function mapGradlePath(path, mapper) {
104
+ const absolutePath = resolve(path);
105
+ try {
106
+ return mapper(absolutePath);
107
+ } catch (error) {
108
+ return buildFailure(
109
+ jvmRunnerErrorCodes.pathInaccessible,
110
+ `Gradle cannot access required JVM analysis path: ${absolutePath}`,
111
+ {
112
+ path: absolutePath,
113
+ cause: error instanceof Error ? error.message : String(error),
114
+ },
115
+ );
116
+ }
117
+ }
118
+ function validateTarget(target) {
119
+ const validModule =
120
+ target.applicationModulePath === ":" ||
121
+ /^:(?:[A-Za-z0-9_.-]+)(?::[A-Za-z0-9_.-]+)*$/u.test(
122
+ target.applicationModulePath,
123
+ );
124
+ if (
125
+ !validModule ||
126
+ target.variantName.trim().length === 0 ||
127
+ /[\s:]/u.test(target.variantName)
128
+ ) {
129
+ return buildFailure(
130
+ jvmRunnerErrorCodes.invalidTarget,
131
+ "JVM analysis requires a valid Gradle application module path and variant.",
132
+ {
133
+ applicationModulePath: target.applicationModulePath,
134
+ variantName: target.variantName,
135
+ },
136
+ );
137
+ }
138
+ }
139
+ function upperCamel(value) {
140
+ return `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}`;
141
+ }
142
+ export function jvmLintTask(applicationModulePath, variantName) {
143
+ validateTarget({
144
+ projectRoot: ".",
145
+ applicationModulePath,
146
+ variantName,
147
+ });
148
+ const prefix = applicationModulePath === ":" ? "" : applicationModulePath;
149
+ return `${prefix}:lintAnalyze${upperCamel(variantName)}`;
150
+ }
151
+ async function runSingleJvmAnalysis(options) {
152
+ validateTarget(options.target);
153
+ const projectRoot = resolve(options.target.projectRoot);
154
+ const analyzerJarPath = resolve(options.analyzerJarPath);
155
+ if (!existsSync(analyzerJarPath) || !statSync(analyzerJarPath).isFile()) {
156
+ return buildFailure(
157
+ jvmRunnerErrorCodes.analyzerJarMissing,
158
+ `JVM analyzer Jar is missing: ${analyzerJarPath}`,
159
+ { path: analyzerJarPath },
160
+ );
161
+ }
162
+ const outputPath = resolve(options.outputDirectory, RAW_JVM_FILE_NAME);
163
+ try {
164
+ mkdirSync(resolve(options.outputDirectory), { recursive: true });
165
+ rmSync(outputPath, { force: true });
166
+ } catch (error) {
167
+ return buildFailure(
168
+ jvmRunnerErrorCodes.outputPrepareFailed,
169
+ `Unable to prepare raw JVM output: ${outputPath}`,
170
+ {
171
+ cause: error instanceof Error ? error.message : String(error),
172
+ },
173
+ );
174
+ }
175
+ const mapper = options.mapPathForGradle ?? ((path) => path);
176
+ const gradleOutputPath = mapGradlePath(outputPath, mapper);
177
+ const gradleSourceRoot = mapGradlePath(
178
+ options.target.sourceRoot ?? projectRoot,
179
+ mapper,
180
+ );
181
+ const gradleAnalyzerJar = mapGradlePath(analyzerJarPath, mapper);
182
+ const gradleInitScript = mapGradlePath(
183
+ options.initScriptPath ?? DEFAULT_LINT_INIT_SCRIPT,
184
+ mapper,
185
+ );
186
+ const lintTask = jvmLintTask(
187
+ options.target.applicationModulePath,
188
+ options.target.variantName,
189
+ );
190
+ const args = [
191
+ "--no-daemon",
192
+ "--console=plain",
193
+ // Lint analyzes source and does not package the target bytecode. Some
194
+ // otherwise analyzable Android builds contain library modules whose Java
195
+ // and Kotlin target declarations differ; Kotlin's default error mode would
196
+ // prevent Lint from starting at all. Keep the diagnostic visible while
197
+ // allowing static extraction to proceed.
198
+ "-Pkotlin.jvm.target.validation.mode=warning",
199
+ "--init-script",
200
+ gradleInitScript,
201
+ `-Dreqgen.lint.jar=${gradleAnalyzerJar}`,
202
+ `-Dreqgen.application.module=${options.target.applicationModulePath}`,
203
+ `-Dreqgen.lint.task=${lintTask}`,
204
+ `-Dreqgen.raw.output=${gradleOutputPath}`,
205
+ `-Dreqgen.source.root=${gradleSourceRoot}`,
206
+ lintTask,
207
+ ];
208
+ const environment = {
209
+ ...options.gradle?.environment,
210
+ ANDROID_LINT_JARS: gradleAnalyzerJar,
211
+ REQGEN_RAW_OUTPUT: gradleOutputPath,
212
+ REQGEN_SOURCE_ROOT: gradleSourceRoot,
213
+ };
214
+ const executeGradle = options.executeGradle ?? runGradle;
215
+ const gradleResult = await executeGradle(projectRoot, args, {
216
+ ...options.gradle,
217
+ environment,
218
+ });
219
+ if (gradleResult.exitCode !== 0) {
220
+ return buildFailure(
221
+ jvmRunnerErrorCodes.lintFailed,
222
+ `Gradle task ${lintTask} failed during JVM analysis.`,
223
+ {
224
+ command: gradleResult.execution.displayCommand,
225
+ stdoutTail: gradleResult.stdout.slice(-DIAGNOSTIC_LOG_LIMIT),
226
+ stderrTail: gradleResult.stderr.slice(-DIAGNOSTIC_LOG_LIMIT),
227
+ },
228
+ );
229
+ }
230
+ if (!existsSync(outputPath) || !statSync(outputPath).isFile()) {
231
+ return buildFailure(
232
+ jvmRunnerErrorCodes.outputMissing,
233
+ `Gradle task ${lintTask} did not produce ${RAW_JVM_FILE_NAME}.`,
234
+ { outputPath },
235
+ );
236
+ }
237
+ return {
238
+ outputPath,
239
+ lintTask,
240
+ document: readJvmNdjsonFile(outputPath),
241
+ gradleResult,
242
+ };
243
+ }
244
+ function targetKey(target) {
245
+ return `${target.applicationModulePath}\u0000${target.variantName}`;
246
+ }
247
+ function orderedModuleTargets(options) {
248
+ if (options.moduleTargets === undefined) return [options.target];
249
+ if (options.moduleTargets.length === 0) {
250
+ return buildFailure(
251
+ jvmRunnerErrorCodes.invalidTarget,
252
+ "JVM module analysis requires at least one target.",
253
+ );
254
+ }
255
+ const projectRoot = resolve(options.target.projectRoot);
256
+ const sourceRoot = resolve(
257
+ options.target.sourceRoot ?? options.target.projectRoot,
258
+ );
259
+ const seen = new Set();
260
+ const targets = [...options.moduleTargets].sort(
261
+ (left, right) =>
262
+ left.applicationModulePath.localeCompare(
263
+ right.applicationModulePath,
264
+ "en",
265
+ ) || left.variantName.localeCompare(right.variantName, "en"),
266
+ );
267
+ for (const target of targets) {
268
+ validateTarget(target);
269
+ const key = targetKey(target);
270
+ if (seen.has(key)) {
271
+ return buildFailure(
272
+ jvmRunnerErrorCodes.invalidTarget,
273
+ `JVM module target is duplicated: ${target.applicationModulePath}/${target.variantName}.`,
274
+ {
275
+ applicationModulePath: target.applicationModulePath,
276
+ variantName: target.variantName,
277
+ },
278
+ );
279
+ }
280
+ seen.add(key);
281
+ const targetProjectRoot = resolve(target.projectRoot);
282
+ const targetSourceRoot = resolve(target.sourceRoot ?? target.projectRoot);
283
+ if (targetProjectRoot !== projectRoot || targetSourceRoot !== sourceRoot) {
284
+ return buildFailure(
285
+ jvmRunnerErrorCodes.invalidTarget,
286
+ "All JVM module targets must share one Gradle project and source root.",
287
+ {
288
+ expectedProjectRoot: projectRoot,
289
+ actualProjectRoot: targetProjectRoot,
290
+ expectedSourceRoot: sourceRoot,
291
+ actualSourceRoot: targetSourceRoot,
292
+ applicationModulePath: target.applicationModulePath,
293
+ },
294
+ );
295
+ }
296
+ }
297
+ if (!seen.has(targetKey(options.target))) {
298
+ return buildFailure(
299
+ jvmRunnerErrorCodes.invalidTarget,
300
+ "The primary JVM target is absent from moduleTargets.",
301
+ {
302
+ applicationModulePath: options.target.applicationModulePath,
303
+ variantName: options.target.variantName,
304
+ },
305
+ );
306
+ }
307
+ return targets;
308
+ }
309
+ function moduleOutputDirectory(outputDirectory, target) {
310
+ const token = Buffer.from(targetKey(target), "utf8").toString("base64url");
311
+ return resolve(outputDirectory, "raw-jvm-modules", token);
312
+ }
313
+ function recordSignature(record) {
314
+ return JSON.stringify(record);
315
+ }
316
+ function mergedJvmDocument(runs, primary) {
317
+ const engineVersions = new Set(
318
+ runs.map((run) => run.document.header.data.engineVersion),
319
+ );
320
+ if (engineVersions.size !== 1) {
321
+ return buildFailure(
322
+ jvmRunnerErrorCodes.mergeIncompatible,
323
+ "JVM module outputs use different analyzer engine versions.",
324
+ { engineVersions: [...engineVersions].sort() },
325
+ );
326
+ }
327
+ const body = [];
328
+ const byId = new Map();
329
+ for (const run of runs) {
330
+ for (const record of run.document.body) {
331
+ const signature = recordSignature(record);
332
+ const existing = byId.get(record.id);
333
+ if (existing === undefined) {
334
+ byId.set(record.id, signature);
335
+ body.push(record);
336
+ continue;
337
+ }
338
+ if (existing !== signature) {
339
+ return buildFailure(
340
+ jvmRunnerErrorCodes.mergeIdConflict,
341
+ `JVM module outputs disagree for record ID ${record.id}.`,
342
+ {
343
+ id: record.id,
344
+ applicationModulePath: run.target.applicationModulePath,
345
+ variantName: run.target.variantName,
346
+ },
347
+ );
348
+ }
349
+ }
350
+ }
351
+ const countsByKind = {};
352
+ for (const record of body) {
353
+ countsByKind[record.kind] = (countsByKind[record.kind] ?? 0) + 1;
354
+ }
355
+ const orderedCounts = Object.fromEntries(
356
+ Object.entries(countsByKind).sort(([left], [right]) =>
357
+ left.localeCompare(right, "en"),
358
+ ),
359
+ );
360
+ const header = {
361
+ ...primary.document.header,
362
+ id: "header:android-lint-uast:merged",
363
+ data: {
364
+ ...primary.document.header.data,
365
+ project: primary.document.header.data.project,
366
+ },
367
+ };
368
+ const footer = {
369
+ ...primary.document.footer,
370
+ id: "footer:android-lint-uast:merged",
371
+ data: {
372
+ ...primary.document.footer.data,
373
+ complete: true,
374
+ recordCount: body.length,
375
+ countsByKind: orderedCounts,
376
+ },
377
+ };
378
+ const controlIds = new Set([header.id, footer.id]);
379
+ const controlCollision = body.find((record) => controlIds.has(record.id));
380
+ if (controlCollision) {
381
+ return buildFailure(
382
+ jvmRunnerErrorCodes.mergeIdConflict,
383
+ `JVM body record collides with merged control ID ${controlCollision.id}.`,
384
+ { id: controlCollision.id },
385
+ );
386
+ }
387
+ const records = [header, ...body, footer];
388
+ const ndjson =
389
+ records.map((record) => JSON.stringify(record)).join("\n") + "\n";
390
+ return {
391
+ document: parseJvmNdjson(ndjson),
392
+ ndjson,
393
+ };
394
+ }
395
+ export async function runJvmAnalysis(options) {
396
+ const targets = orderedModuleTargets(options);
397
+ if (targets.length === 1) {
398
+ return runSingleJvmAnalysis({
399
+ ...options,
400
+ target: targets[0],
401
+ moduleTargets: undefined,
402
+ });
403
+ }
404
+ const outputDirectory = resolve(options.outputDirectory);
405
+ const moduleRoot = resolve(outputDirectory, "raw-jvm-modules");
406
+ const outputPath = resolve(outputDirectory, RAW_JVM_FILE_NAME);
407
+ try {
408
+ mkdirSync(outputDirectory, { recursive: true });
409
+ rmSync(moduleRoot, { recursive: true, force: true });
410
+ rmSync(outputPath, { force: true });
411
+ } catch (error) {
412
+ return buildFailure(
413
+ jvmRunnerErrorCodes.outputPrepareFailed,
414
+ `Unable to prepare multi-module JVM output: ${outputDirectory}`,
415
+ {
416
+ cause: error instanceof Error ? error.message : String(error),
417
+ },
418
+ );
419
+ }
420
+ const moduleRuns = [];
421
+ for (const target of targets) {
422
+ const run = await runSingleJvmAnalysis({
423
+ ...options,
424
+ target,
425
+ moduleTargets: undefined,
426
+ outputDirectory: moduleOutputDirectory(outputDirectory, target),
427
+ });
428
+ moduleRuns.push({ ...run, target });
429
+ }
430
+ const primary = moduleRuns.find(
431
+ (run) => targetKey(run.target) === targetKey(options.target),
432
+ );
433
+ const merged = mergedJvmDocument(moduleRuns, primary);
434
+ try {
435
+ writeFileSync(outputPath, merged.ndjson, "utf8");
436
+ } catch (error) {
437
+ return buildFailure(
438
+ jvmRunnerErrorCodes.mergeWriteFailed,
439
+ `Unable to write merged raw JVM output: ${outputPath}`,
440
+ {
441
+ cause: error instanceof Error ? error.message : String(error),
442
+ },
443
+ );
444
+ }
445
+ return {
446
+ outputPath,
447
+ lintTask: primary.lintTask,
448
+ document: merged.document,
449
+ gradleResult: primary.gradleResult,
450
+ moduleRuns,
451
+ };
452
+ }
@@ -0,0 +1,8 @@
1
+ export { DEFAULT_ANALYSIS_LIMITS, runStage1 } from "./run-stage1.js";
2
+ export {
3
+ publishStagedArtifacts,
4
+ serializeArtifact,
5
+ sha256Text,
6
+ writeJsonArtifact,
7
+ writeTextArtifact,
8
+ } from "./output.js";
@@ -0,0 +1,165 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstat, mkdir, mkdtemp, rename, rm, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
+ function posixRelative(root, path) {
5
+ return relative(root, path).split(sep).join("/");
6
+ }
7
+ function assertChildPath(outputDirectory, filename) {
8
+ if (
9
+ filename.length === 0 ||
10
+ filename === "." ||
11
+ filename === ".." ||
12
+ filename.includes("/") ||
13
+ filename.includes("\\") ||
14
+ basename(filename) !== filename
15
+ ) {
16
+ throw new Error(`Artifact filename must be a basename: ${filename}`);
17
+ }
18
+ const root = resolve(outputDirectory);
19
+ const target = resolve(root, filename);
20
+ if (dirname(target) !== root) {
21
+ throw new Error(`Artifact escapes output directory: ${filename}`);
22
+ }
23
+ return target;
24
+ }
25
+ export function serializeArtifact(value) {
26
+ return `${JSON.stringify(value, null, 2)}\n`;
27
+ }
28
+ export function sha256Text(value) {
29
+ return createHash("sha256").update(value, "utf8").digest("hex");
30
+ }
31
+ /**
32
+ * Write one complete artifact atomically. A failed process leaves at most a
33
+ * hidden temporary file; it never exposes a truncated final JSON document.
34
+ */
35
+ export async function writeJsonArtifact(
36
+ outputDirectory,
37
+ filename,
38
+ kind,
39
+ value,
40
+ ) {
41
+ const root = resolve(outputDirectory);
42
+ const target = assertChildPath(root, filename);
43
+ await mkdir(root, { recursive: true });
44
+ const text = serializeArtifact(value);
45
+ const temporary = join(root, `.${filename}.${process.pid}.${Date.now()}.tmp`);
46
+ try {
47
+ await writeFile(temporary, text, {
48
+ encoding: "utf8",
49
+ flag: "wx",
50
+ });
51
+ await rename(temporary, target);
52
+ } catch (error) {
53
+ await rm(temporary, { force: true });
54
+ throw error;
55
+ }
56
+ return {
57
+ kind,
58
+ relativePath: posixRelative(root, target),
59
+ sha256: sha256Text(text),
60
+ };
61
+ }
62
+ export async function writeTextArtifact(outputDirectory, filename, kind, text) {
63
+ const root = resolve(outputDirectory);
64
+ const target = assertChildPath(root, filename);
65
+ await mkdir(root, { recursive: true });
66
+ const normalized = text.endsWith("\n") ? text : `${text}\n`;
67
+ const temporary = join(root, `.${filename}.${process.pid}.${Date.now()}.tmp`);
68
+ try {
69
+ await writeFile(temporary, normalized, {
70
+ encoding: "utf8",
71
+ flag: "wx",
72
+ });
73
+ await rename(temporary, target);
74
+ } catch (error) {
75
+ await rm(temporary, { force: true });
76
+ throw error;
77
+ }
78
+ return {
79
+ kind,
80
+ relativePath: posixRelative(root, target),
81
+ sha256: sha256Text(normalized),
82
+ };
83
+ }
84
+ async function pathExists(path) {
85
+ try {
86
+ await lstat(path);
87
+ return true;
88
+ } catch (error) {
89
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
90
+ return false;
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+ /**
96
+ * Publish a validated set of top-level artifact files as one transaction.
97
+ *
98
+ * Artifacts are fully written in `stagingDirectory` before this function is
99
+ * called. Existing artifacts are moved aside and restored if any rename
100
+ * fails, so a caught publication error does not expose a mixture of runs.
101
+ * The report file should be the last filename: it is the commit marker readers
102
+ * use to decide whether the run is publishable.
103
+ */
104
+ export async function publishStagedArtifacts(
105
+ stagingDirectory,
106
+ outputDirectory,
107
+ filenames,
108
+ removeFilenames = [],
109
+ ) {
110
+ const staging = resolve(stagingDirectory);
111
+ const output = resolve(outputDirectory);
112
+ if (staging === output) {
113
+ throw new Error("Artifact staging and output directories must differ");
114
+ }
115
+ const allFilenames = [...filenames, ...removeFilenames];
116
+ const unique = new Set(allFilenames);
117
+ if (unique.size !== allFilenames.length) {
118
+ throw new Error("Artifact publication filenames must be unique");
119
+ }
120
+ allFilenames.forEach((filename) => assertChildPath(output, filename));
121
+ await mkdir(output, { recursive: true });
122
+ for (const filename of filenames) {
123
+ const source = assertChildPath(staging, filename);
124
+ if (!(await pathExists(source))) {
125
+ throw new Error(`Staged artifact is missing: ${filename}`);
126
+ }
127
+ }
128
+ const backup = await mkdtemp(join(output, ".reqgen-backup-"));
129
+ const backedUp = [];
130
+ const published = [];
131
+ try {
132
+ // A tombstone is part of the same rollback boundary. This is used by a
133
+ // failed run to remove successful three-layer artifacts from an earlier
134
+ // run in the same output directory.
135
+ for (const filename of removeFilenames) {
136
+ const target = assertChildPath(output, filename);
137
+ if (await pathExists(target)) {
138
+ await rename(target, assertChildPath(backup, filename));
139
+ backedUp.push(filename);
140
+ }
141
+ }
142
+ for (const filename of filenames) {
143
+ const target = assertChildPath(output, filename);
144
+ if (await pathExists(target)) {
145
+ await rename(target, assertChildPath(backup, filename));
146
+ backedUp.push(filename);
147
+ }
148
+ await rename(assertChildPath(staging, filename), target);
149
+ published.push(filename);
150
+ }
151
+ } catch (error) {
152
+ for (const filename of [...published].reverse()) {
153
+ await rm(assertChildPath(output, filename), { force: true });
154
+ }
155
+ for (const filename of [...backedUp].reverse()) {
156
+ const saved = assertChildPath(backup, filename);
157
+ if (await pathExists(saved)) {
158
+ await rename(saved, assertChildPath(output, filename));
159
+ }
160
+ }
161
+ throw error;
162
+ } finally {
163
+ await rm(backup, { recursive: true, force: true });
164
+ }
165
+ }