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.
- package/agents/api-analyzer.md +113 -0
- package/agents/app-action.md +122 -0
- package/agents/code-reviewer.md +191 -295
- package/agents/coder.md +377 -0
- package/agents/self-tester.md +8 -8
- package/agents/spec-designer.md +56 -0
- package/package.json +1 -1
- package/skills/a2h-app-req-gen/SKILL.md +174 -0
- package/skills/a2h-app-req-gen/references/spec-handoff.md +99 -0
- package/skills/a2h-app-req-gen/references/subagent-render.md +115 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/build.gradle.kts +41 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle.properties +3 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew +251 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew.bat +94 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/settings.gradle.kts +17 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawAnalysisDetector.java +1911 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawIssueRegistry.java +37 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawJson.java +146 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawLocation.java +96 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawRecordWriter.java +227 -0
- 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
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/dev/reqgen/analyzer/jvm/raw-record-v1.schema.json +93 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/assets/dashboard.html +151 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-lint.init.gradle +60 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-model.init.gradle +204 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/package.json +14 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/backend.js +323 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/cli.js +811 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/gap-audit-link.js +62 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/index.js +66 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/merge.js +605 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/name-trace.js +153 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/orchestrate.js +1324 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/output-parse.js +50 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/overview.js +10 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/pool.js +23 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/post-merge-fix.js +146 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt-loader.js +51 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt.js +967 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompts.md +301 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/serve.js +78 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/short-id.js +46 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/snapshot.js +267 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/validate.js +122 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/callback-call-sites.js +62 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/build.js +1291 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/area-ownership.js +392 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/build.js +1995 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/callback-flow.js +277 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/component-variants.js +725 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/index.js +15 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/index.js +12 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/policy.js +469 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/traceability.js +468 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/build.js +1066 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android-interaction-registry.js +174 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android.js +1062 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/area-titles.js +678 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/builder.js +350 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-api-registry.js +395 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-call-sites.js +2008 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose.js +9059 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/dynamic-repeated-call-sites.js +529 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/interaction-effects.js +1149 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/jvm.js +6272 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/normalize.js +33 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/pending-intents.js +565 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/resource-id.js +42 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/route-state-variants.js +781 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/state.js +234 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/extract.js +40 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/index.js +6 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/manifest.js +513 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/resources.js +398 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/views.js +447 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/xml-parser.js +175 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/cli.js +398 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/area-graph.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/artifact.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/common.js +16 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/diagnostic.js +10 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/gap-audit.js +14 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/index.js +11 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/name-traceability.js +26 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/raw-fact.js +40 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/req-model.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/run.js +19 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/semantic-completeness.js +11 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/source-facts.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/core/output-guard.js +41 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/core/stable-id.js +92 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dashboard-cli.js +169 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/diagnostics.js +104 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/dump.js +1028 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump-cli.js +251 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/artifacts.js +49 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/index.js +18 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/quality-report.js +125 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/render.js +353 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/types.js +2 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/graph/paths.js +474 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/index.js +15 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/protocol.js +408 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/runner.js +452 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/index.js +8 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/output.js +165 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/run-stage1.js +891 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/discovery.js +299 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-model.js +358 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-runner.js +165 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/resource-profile.js +60 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff-cli.js +151 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff.js +1004 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-cli.js +109 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-input.js +419 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-name-trace-input.js +95 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/area-graph.js +444 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/index.js +7 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-model.js +580 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-text.js +195 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/semantic-area-graph.js +1431 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen-audit.mjs +298 -0
- package/skills/a2h-app-req-gen/scripts/reqgen-selfcheck.mjs +555 -0
- package/skills/a2h-code-review/SKILL.md +379 -0
- package/skills/{hmos-integration-test → a2h-integration-test}/README.md +6 -6
- package/skills/{hmos-integration-test → a2h-integration-test}/SKILL.md +6 -6
- package/skills/{hmos-integration-test → a2h-integration-test}/scripts/report-tool.mjs +1 -1
- package/skills/a2h-spec-design/SKILL.md +330 -0
- package/skills/a2h-spec-design/scripts/inspect_spec_design_inputs.mjs +207 -0
- package/skills/a2h-spec-design/scripts/move_spec_technical_reference.mjs +198 -0
- package/skills/a2h-spec-design/scripts/validate_spec_design_output.mjs +256 -0
- package/skills/a2h-spec-generate/SKILL.md +696 -0
- package/skills/a2h-spec-generate/references/trace-template.md +115 -0
- package/skills/a2h-spec-generate/scripts/parse_requirements.mjs +537 -0
- package/skills/a2h-spec-generate/template/REQ.txt +22 -0
- package/skills/a2h-spec-generate/template/REQ.xlsx +0 -0
- package/skills/a2h-spec-implement/SKILL.md +548 -0
- package/skills/a2h-spec-implement/references/harmony-pitfalls.md +186 -0
- package/skills/a2h-spec-implement/references/l1-unit-test.md +185 -0
- package/skills/a2h-spec-implement/references/l3-e2e-uitest.md +167 -0
- package/skills/a2h-spec-implement/references/test-core.md +132 -0
- package/skills/a2h-spec-implement/rules/arkts/arkts-standards.md +184 -0
- package/skills/a2h-spec-implement/rules/arkts/conventions/coding-style.md +96 -0
- package/skills/a2h-spec-implement/rules/arkts/conventions/security.md +126 -0
- package/skills/a2h-spec-implement/rules/arkts/language/arkts-rules.md +650 -0
- package/skills/a2h-spec-implement/rules/arkts/language/arkui-structure-rules.md +173 -0
- package/skills/a2h-spec-implement/rules/arkts/language/ts-to-arkts-rewrites.md +58 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/skills/a2h-spec-implement/rules/arkts/ui/component-cookbook.md +431 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/state-management.md +152 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/ui-quality.md +67 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/ui-runtime-diagnosis.md +32 -0
- package/skills/a2h-spec-implement/rules/pipeline/build/package-set-collection.md +43 -0
- package/skills/a2h-spec-implement/scripts/_lib/common.mjs +97 -0
- package/skills/a2h-spec-implement/scripts/_lib/config.mjs +70 -0
- package/skills/a2h-spec-implement/scripts/_lib/lessons_cli.mjs +48 -0
- package/skills/a2h-spec-implement/scripts/build_lessons.mjs +24 -0
- package/skills/a2h-spec-implement/scripts/code_pattern_lessons.mjs +26 -0
- package/skills/a2h-spec-implement/scripts/device_ui.mjs +433 -0
- package/skills/a2h-spec-implement/scripts/device_ui_core.mjs +895 -0
- package/skills/a2h-spec-implement/scripts/ledger/aggregate_run.mjs +207 -0
- package/skills/a2h-spec-implement/scripts/manifest/emit_summary_line.mjs +37 -0
- package/skills/a2h-spec-implement/scripts/manifest/finalize_run.mjs +115 -0
- package/skills/a2h-spec-implement/scripts/manifest/render_final_summary.mjs +78 -0
- package/skills/a2h-spec-implement/scripts/manifest/root_mirror.mjs +137 -0
- package/skills/a2h-spec-implement/scripts/package.json +12 -0
- package/skills/a2h-spec-implement/scripts/state_propagation_check.mjs +676 -0
- package/skills/a2h-spec-implement/scripts/test/l2_run.mjs +244 -0
- package/skills/a2h-spec-implement/scripts/test/tp_coverage.mjs +231 -0
- package/skills/a2h-spec-implement/scripts/ui_memory.mjs +161 -0
- package/skills/a2h-spec-implement/scripts/utils/ensure_app.mjs +134 -0
- package/skills/a2h-ui-transfer/SKILL.md +20 -9
- package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +80 -0
- package/skills/hmos-convert-pipeline/SKILL.md +7 -5
- package/skills/hmos-incremental-ui-align/README.md +7 -7
- package/skills/hmos-incremental-ui-align/SKILL.md +1 -1
- package/skills/hmos-incremental-ui-align/page_align.md +1 -1
- package/skills/hmos-spec-generate/SKILL.md +2 -2
- package/skills/hmos-spec-generate/scripts/parse_requirements.mjs +537 -0
- package/skills/hmos-fix-build-errors/SKILL.md +0 -266
- package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +0 -219
- package/skills/hmos-fix-build-errors/references/known-patterns.md +0 -157
- package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +0 -131
- package/skills/hmos-spec-generate/scripts/parse_requirements.ts +0 -515
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/resolve-metadata-tool.mjs +0 -0
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/self-test-runner.mjs +0 -0
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/testcases-tool.mjs +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aggregate_run.mjs
|
|
4
|
+
* Run-level defect aggregation from the ledger (convergence.jsonl).
|
|
5
|
+
*
|
|
6
|
+
* Replaces the former aggregate_wave.mjs + manifest/aggregate_pipeline.mjs pair:
|
|
7
|
+
* both carried a line-for-line duplicate of getTerminalVerdict() and of the
|
|
8
|
+
* defect arithmetic, differing only in whether they derived the case roster from
|
|
9
|
+
* the ledger or from the plan. One scenario roster, one implementation.
|
|
10
|
+
*
|
|
11
|
+
* Inputs (CLI args):
|
|
12
|
+
* --ledger path to convergence.jsonl
|
|
13
|
+
* --scenarios path to logic/scenarios.json (the delivery roster)
|
|
14
|
+
* --rounds rounds_executed (integer)
|
|
15
|
+
* --stop-reason all_passed | no_confirmed_defects | no_progress | max_rounds_reached
|
|
16
|
+
* --output optional path to also write the result JSON to
|
|
17
|
+
*
|
|
18
|
+
* The roster is authoritative for the pass-rate denominator. Scenarios listed
|
|
19
|
+
* under `skipped` are excluded from both denominators: they were never delivered,
|
|
20
|
+
* so counting them would understate the rate of the work actually attempted.
|
|
21
|
+
*
|
|
22
|
+
* Outputs (stdout JSON):
|
|
23
|
+
* {
|
|
24
|
+
* ok, defect_row, per_case[], invariant_holds, env_false_positive_count,
|
|
25
|
+
* total_cases, terminal_passed_count, defects_found, defects_fixed,
|
|
26
|
+
* defects_remaining, pass_rate_quality, pass_rate_operational
|
|
27
|
+
* }
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
31
|
+
import { dirname } from 'path';
|
|
32
|
+
|
|
33
|
+
// ── CLI ──────────────────────────────────────────────────────────────────────
|
|
34
|
+
const args = {};
|
|
35
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
36
|
+
const arg = process.argv[i];
|
|
37
|
+
if (arg.startsWith('--')) {
|
|
38
|
+
const key = arg.slice(2);
|
|
39
|
+
args[key] = process.argv[i + 1];
|
|
40
|
+
if (args[key]?.startsWith('--')) {
|
|
41
|
+
args[key] = true; // flag without value
|
|
42
|
+
i--;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const {
|
|
48
|
+
ledger: ledgerPath,
|
|
49
|
+
scenarios: scenariosPath,
|
|
50
|
+
rounds,
|
|
51
|
+
'stop-reason': stopReason,
|
|
52
|
+
output: outputPath,
|
|
53
|
+
} = args;
|
|
54
|
+
|
|
55
|
+
if (!ledgerPath || !scenariosPath || rounds === undefined || !stopReason) {
|
|
56
|
+
console.error(JSON.stringify({ ok: false, error: 'Usage: aggregate_run --ledger <path> --scenarios <path> --rounds <n> --stop-reason <reason> [--output <path>]' }));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const roundsNum = parseInt(rounds, 10);
|
|
61
|
+
|
|
62
|
+
// ── Parse scenario roster ────────────────────────────────────────────────────
|
|
63
|
+
let roster;
|
|
64
|
+
try {
|
|
65
|
+
roster = JSON.parse(readFileSync(scenariosPath, 'utf8'));
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error(JSON.stringify({ ok: false, error: `Cannot read scenarios roster ${scenariosPath}: ${e.message}` }));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const deliveredCases = Array.isArray(roster.scenarios) ? roster.scenarios : [];
|
|
72
|
+
if (deliveredCases.length === 0) {
|
|
73
|
+
console.error(JSON.stringify({ ok: false, error: `Scenario roster ${scenariosPath} lists no delivered scenarios` }));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// Skipped scenarios are recorded for traceability but never scored.
|
|
77
|
+
const skippedCases = new Set(
|
|
78
|
+
(Array.isArray(roster.skipped) ? roster.skipped : []).map(s => (typeof s === 'string' ? s : s.case_id))
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// ── Parse ledger ─────────────────────────────────────────────────────────────
|
|
82
|
+
const ledgerLines = readFileSync(ledgerPath, 'utf8').trim().split('\n').filter(Boolean);
|
|
83
|
+
const allLines = ledgerLines
|
|
84
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
85
|
+
.filter(l => l !== null);
|
|
86
|
+
|
|
87
|
+
// ── Group by case ─────────────────────────────────────────────────────────────
|
|
88
|
+
const caseMap = {}; // case_id -> array of ledger lines
|
|
89
|
+
for (const line of allLines) {
|
|
90
|
+
const id = line.case_id;
|
|
91
|
+
if (!caseMap[id]) caseMap[id] = [];
|
|
92
|
+
caseMap[id].push(line);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Source priority for terminal verdict ─────────────────────────────────────
|
|
96
|
+
// Priority: integration(3) > uitest(2) > unit(1) > review(0)
|
|
97
|
+
// "last non-fixer verdict in source-priority order" means:
|
|
98
|
+
// 1. pick the most recent entry by timestamp
|
|
99
|
+
// 2. if tie on timestamp, pick highest priority source
|
|
100
|
+
// This must stay identical to SKILL.md 阶段2 步骤⑤ — a divergence between the
|
|
101
|
+
// in-round decision and these stats makes the two disagree about convergence.
|
|
102
|
+
const SOURCE_PRIORITY = { review: 0, unit: 1, uitest: 2, integration: 3 };
|
|
103
|
+
|
|
104
|
+
function getTerminalVerdict(lines) {
|
|
105
|
+
const nonFixer = lines.filter(l => l.source !== 'fixer');
|
|
106
|
+
if (nonFixer.length === 0) return null;
|
|
107
|
+
const sorted = [...nonFixer].sort((a, b) => {
|
|
108
|
+
const tA = new Date(a.ts).getTime();
|
|
109
|
+
const tB = new Date(b.ts).getTime();
|
|
110
|
+
if (tB !== tA) return tB - tA; // most recent first
|
|
111
|
+
return (SOURCE_PRIORITY[b.source] ?? 0) - (SOURCE_PRIORITY[a.source] ?? 0); // highest priority wins
|
|
112
|
+
});
|
|
113
|
+
return sorted[0];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Compute stats over the delivered roster ──────────────────────────────────
|
|
117
|
+
const VERDICTS_FAIL = new Set(['FAIL', 'PARTIAL', 'BLOCKED']);
|
|
118
|
+
const VERDICTS_PASS = new Set(['PASS']);
|
|
119
|
+
|
|
120
|
+
let defectsFound = 0;
|
|
121
|
+
let defectsFixed = 0;
|
|
122
|
+
let envFalsePositiveCount = 0;
|
|
123
|
+
let terminalPassed = 0;
|
|
124
|
+
|
|
125
|
+
const perCase = [];
|
|
126
|
+
|
|
127
|
+
for (const caseId of deliveredCases) {
|
|
128
|
+
if (skippedCases.has(caseId)) continue; // never delivered — not scored
|
|
129
|
+
const lines = caseMap[caseId] || [];
|
|
130
|
+
const terminal = getTerminalVerdict(lines);
|
|
131
|
+
const verdict = terminal?.verdict ?? 'UNKNOWN';
|
|
132
|
+
const cls = terminal?.class ?? 'unknown';
|
|
133
|
+
const signature = terminal ? `${terminal.failing_point || ''}|${terminal.root_cause || ''}` : '';
|
|
134
|
+
|
|
135
|
+
// class=environment: count as env_false_positive, do NOT count toward defects
|
|
136
|
+
if (cls === 'environment') {
|
|
137
|
+
envFalsePositiveCount++;
|
|
138
|
+
perCase.push({ case_id: caseId, terminal_verdict: verdict, signature, last_source: terminal?.source ?? 'unknown', class: cls });
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Did this case ever have FAIL/PARTIAL/BLOCKED with class=code in ANY round?
|
|
143
|
+
const everFailed = lines.some(l => VERDICTS_FAIL.has(l.verdict) && l.class === 'code');
|
|
144
|
+
const finalIsPass = VERDICTS_PASS.has(verdict);
|
|
145
|
+
|
|
146
|
+
if (everFailed) {
|
|
147
|
+
defectsFound++;
|
|
148
|
+
if (finalIsPass) defectsFixed++;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (finalIsPass) terminalPassed++;
|
|
152
|
+
|
|
153
|
+
perCase.push({ case_id: caseId, terminal_verdict: verdict, signature, last_source: terminal?.source ?? 'unknown', class: cls });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Invariant check ──────────────────────────────────────────────────────────
|
|
157
|
+
const defectsRemaining = defectsFound - defectsFixed;
|
|
158
|
+
const invariantHolds = (defectsFound === defectsFixed + defectsRemaining);
|
|
159
|
+
|
|
160
|
+
// ── Pass rates ───────────────────────────────────────────────────────────────
|
|
161
|
+
// Scored population excludes skipped scenarios; quality additionally excludes
|
|
162
|
+
// environment false positives, so the two rates differ exactly when env noise exists.
|
|
163
|
+
const scoredTotal = deliveredCases.filter(id => !skippedCases.has(id)).length;
|
|
164
|
+
const denominatorQuality = scoredTotal - envFalsePositiveCount;
|
|
165
|
+
const denominatorOperational = scoredTotal;
|
|
166
|
+
|
|
167
|
+
const passRateQuality = denominatorQuality > 0
|
|
168
|
+
? `${Math.round((terminalPassed / denominatorQuality) * 100)}%`
|
|
169
|
+
: '100%';
|
|
170
|
+
const passRateOperational = denominatorOperational > 0
|
|
171
|
+
? `${Math.round((terminalPassed / denominatorOperational) * 100)}%`
|
|
172
|
+
: '100%';
|
|
173
|
+
|
|
174
|
+
// ── Build defect row ──────────────────────────────────────────────────────────
|
|
175
|
+
const defectRow = {
|
|
176
|
+
rounds: roundsNum,
|
|
177
|
+
terminal_passed: `${terminalPassed}/${scoredTotal}`,
|
|
178
|
+
defects_found: defectsFound,
|
|
179
|
+
defects_fixed: defectsFixed,
|
|
180
|
+
defects_remaining: defectsRemaining,
|
|
181
|
+
pass_rate_quality: passRateQuality,
|
|
182
|
+
pass_rate_operational: passRateOperational,
|
|
183
|
+
stop_reason: stopReason,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const result = {
|
|
187
|
+
ok: invariantHolds,
|
|
188
|
+
defect_row: defectRow,
|
|
189
|
+
per_case: perCase,
|
|
190
|
+
invariant_holds: invariantHolds,
|
|
191
|
+
env_false_positive_count: envFalsePositiveCount,
|
|
192
|
+
total_cases: scoredTotal,
|
|
193
|
+
skipped_count: skippedCases.size,
|
|
194
|
+
terminal_passed_count: terminalPassed,
|
|
195
|
+
defects_found: defectsFound,
|
|
196
|
+
defects_fixed: defectsFixed,
|
|
197
|
+
defects_remaining: defectsRemaining,
|
|
198
|
+
pass_rate_quality: passRateQuality,
|
|
199
|
+
pass_rate_operational: passRateOperational,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (outputPath) {
|
|
203
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
204
|
+
writeFileSync(outputPath, JSON.stringify(result, null, 2));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log(JSON.stringify(result, null, 2));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* emit_summary_line.mjs
|
|
4
|
+
* Generate the one-line run summary text (used by finalize_run.mjs §2).
|
|
5
|
+
*
|
|
6
|
+
* CLI:
|
|
7
|
+
* --terminal-passed integer (number of cases with terminal PASS)
|
|
8
|
+
* --total integer (total scored cases)
|
|
9
|
+
* --rounds-executed integer
|
|
10
|
+
* --max-rounds integer
|
|
11
|
+
* --stop-reason string (all_passed | no_confirmed_defects | no_progress | max_rounds_reached)
|
|
12
|
+
*
|
|
13
|
+
* Output (stdout):
|
|
14
|
+
* "{terminal_passed}/{total} PASS, {rounds_executed}/{max_rounds} rounds, stop={stop_reason}"
|
|
15
|
+
*
|
|
16
|
+
* Example: "5/6 PASS, 3/6 rounds, stop=no_progress"
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const args = {};
|
|
20
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
21
|
+
const arg = process.argv[i];
|
|
22
|
+
if (arg.startsWith('--')) {
|
|
23
|
+
const key = arg.slice(2);
|
|
24
|
+
args[key] = process.argv[i + 1];
|
|
25
|
+
if (args[key]?.startsWith('--')) { args[key] = true; i--; }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { 'terminal-passed': terminalPassed, total, 'rounds-executed': roundsExecuted, 'max-rounds': maxRounds, 'stop-reason': stopReason } = args;
|
|
30
|
+
|
|
31
|
+
if (terminalPassed === undefined || !total || roundsExecuted === undefined || !maxRounds || !stopReason) {
|
|
32
|
+
console.error(JSON.stringify({ ok: false, error: 'Usage: emit_summary_line --terminal-passed <n> --total <n> --rounds-executed <n> --max-rounds <n> --stop-reason <reason>' }));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const line = `${terminalPassed}/${total} PASS, ${roundsExecuted}/${maxRounds} rounds, stop=${stopReason}`;
|
|
37
|
+
console.log(JSON.stringify({ ok: true, summary_line: line }));
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* finalize_run.mjs
|
|
4
|
+
* Run finalization — called once after the ROUND loop exits.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the former wave/finalize_wave.mjs. Two of that script's four steps
|
|
7
|
+
* were mirror hops into WAVE_DIR that nothing downstream read: manifest/root_mirror.mjs
|
|
8
|
+
* re-derives the same artifacts from the round directories itself. With the layout
|
|
9
|
+
* flattened those hops became pure duplication, so finalization is now aggregate +
|
|
10
|
+
* summary line, and all mirroring belongs to 阶段3's root_mirror.mjs.
|
|
11
|
+
*
|
|
12
|
+
* CLI:
|
|
13
|
+
* --output OUTPUT/
|
|
14
|
+
* --ledger path to convergence.jsonl
|
|
15
|
+
* --scenarios path to logic/scenarios.json
|
|
16
|
+
* --rounds rounds_executed (integer)
|
|
17
|
+
* --stop-reason all_passed | no_confirmed_defects | no_progress | max_rounds_reached
|
|
18
|
+
* --max-rounds integer (MAX_ROUNDS from SKILL.md)
|
|
19
|
+
*
|
|
20
|
+
* On invariant_holds=false: writes OUTPUT/finalize-failure.md and exits non-zero.
|
|
21
|
+
* no_progress and max_rounds_reached are terminal states, NOT errors — they exit 0.
|
|
22
|
+
*
|
|
23
|
+
* Output (stdout JSON):
|
|
24
|
+
* { ok, invariant_holds, defect_row, per_case, summary_line }
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { execSync } from 'child_process';
|
|
28
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
29
|
+
import { dirname, join } from 'path';
|
|
30
|
+
import { fileURLToPath } from 'url';
|
|
31
|
+
|
|
32
|
+
const args = {};
|
|
33
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
34
|
+
const arg = process.argv[i];
|
|
35
|
+
if (arg.startsWith('--')) {
|
|
36
|
+
const key = arg.slice(2);
|
|
37
|
+
args[key] = process.argv[i + 1];
|
|
38
|
+
if (args[key]?.startsWith('--')) { args[key] = true; i--; }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
output: outputDir,
|
|
44
|
+
ledger: ledgerPath,
|
|
45
|
+
scenarios: scenariosPath,
|
|
46
|
+
rounds,
|
|
47
|
+
'stop-reason': stopReason,
|
|
48
|
+
'max-rounds': maxRounds,
|
|
49
|
+
} = args;
|
|
50
|
+
|
|
51
|
+
if (!outputDir || !ledgerPath || !scenariosPath || rounds === undefined || !stopReason || maxRounds === undefined) {
|
|
52
|
+
console.error(JSON.stringify({ ok: false, error: 'Usage: finalize_run --output <dir> --ledger <path> --scenarios <path> --rounds <n> --stop-reason <reason> --max-rounds <n>' }));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const scriptDir = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
57
|
+
|
|
58
|
+
// ── §1: Aggregate the run ────────────────────────────────────────────────────
|
|
59
|
+
const aggregatesDir = join(outputDir, 'aggregates');
|
|
60
|
+
mkdirSync(aggregatesDir, { recursive: true });
|
|
61
|
+
const aggregatePath = join(aggregatesDir, 'run-total.json');
|
|
62
|
+
|
|
63
|
+
let agg;
|
|
64
|
+
try {
|
|
65
|
+
const aggOut = execSync(
|
|
66
|
+
`node "${scriptDir}/ledger/aggregate_run.mjs" --ledger "${ledgerPath}" --scenarios "${scenariosPath}" --rounds "${rounds}" --stop-reason "${stopReason}" --output "${aggregatePath}"`,
|
|
67
|
+
{ encoding: 'utf8' }
|
|
68
|
+
);
|
|
69
|
+
agg = JSON.parse(aggOut);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
try {
|
|
72
|
+
agg = JSON.parse(e.stdout || e.stderr || '{}');
|
|
73
|
+
} catch {
|
|
74
|
+
agg = {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── §2: Summary line ─────────────────────────────────────────────────────────
|
|
79
|
+
const terminalPassed = agg.terminal_passed_count ?? 0;
|
|
80
|
+
const totalCases = agg.total_cases ?? 0;
|
|
81
|
+
let summaryLine;
|
|
82
|
+
try {
|
|
83
|
+
const sumOut = execSync(
|
|
84
|
+
`node "${scriptDir}/manifest/emit_summary_line.mjs" --terminal-passed "${terminalPassed}" --total "${totalCases}" --rounds-executed "${rounds}" --max-rounds "${maxRounds}" --stop-reason "${stopReason}"`,
|
|
85
|
+
{ encoding: 'utf8' }
|
|
86
|
+
);
|
|
87
|
+
summaryLine = JSON.parse(sumOut)?.summary_line;
|
|
88
|
+
} catch { /* fall through to the local format below */ }
|
|
89
|
+
if (!summaryLine) {
|
|
90
|
+
summaryLine = `${terminalPassed}/${totalCases} PASS, ${rounds}/${maxRounds} rounds, stop=${stopReason}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Invariant check ──────────────────────────────────────────────────────────
|
|
94
|
+
const invariantHolds = agg.invariant_holds === true;
|
|
95
|
+
const defectRow = agg.defect_row || {};
|
|
96
|
+
const perCase = agg.per_case || [];
|
|
97
|
+
|
|
98
|
+
if (!invariantHolds) {
|
|
99
|
+
const failureMsg = `Run finalization: invariant broke.\n` +
|
|
100
|
+
`defects_found=${agg.defects_found}, defects_fixed=${agg.defects_fixed}, defects_remaining=${agg.defects_remaining}\n` +
|
|
101
|
+
`Expected: defects_found == defects_fixed + defects_remaining`;
|
|
102
|
+
writeFileSync(join(outputDir, 'finalize-failure.md'), failureMsg);
|
|
103
|
+
writeFileSync(join(outputDir, 'finalize-result.json'), JSON.stringify({ aggregate: agg, summary_line: summaryLine }, null, 2));
|
|
104
|
+
console.error(JSON.stringify({ ok: false, invariant_holds: false, error: failureMsg, defect_row: defectRow }));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(JSON.stringify({
|
|
109
|
+
ok: true,
|
|
110
|
+
invariant_holds: invariantHolds,
|
|
111
|
+
defect_row: defectRow,
|
|
112
|
+
per_case: perCase,
|
|
113
|
+
summary_line: summaryLine,
|
|
114
|
+
aggregate_file: aggregatePath,
|
|
115
|
+
}, null, 2));
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* render_final_summary.mjs
|
|
4
|
+
* Phase 3 §4: Generate the final pipeline summary string.
|
|
5
|
+
*
|
|
6
|
+
* CLI:
|
|
7
|
+
* --stop-reason all_passed | no_confirmed_defects | no_progress | max_rounds_reached
|
|
8
|
+
* --rounds integer, rounds_executed (optional)
|
|
9
|
+
* --max-rounds integer (optional)
|
|
10
|
+
* --skip-test-auto "true" | "false" (optional)
|
|
11
|
+
*
|
|
12
|
+
* Output (stdout JSON):
|
|
13
|
+
* { ok, summary_text, summary_markdown }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const args = {};
|
|
17
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
18
|
+
const arg = process.argv[i];
|
|
19
|
+
if (arg.startsWith('--')) {
|
|
20
|
+
const key = arg.slice(2);
|
|
21
|
+
let val = process.argv[i + 1];
|
|
22
|
+
// Parse known booleans
|
|
23
|
+
if (val === 'true') val = true;
|
|
24
|
+
if (val === 'false') val = false;
|
|
25
|
+
args[key] = val;
|
|
26
|
+
if (String(process.argv[i + 1] || '').startsWith('--')) { args[key] = true; i--; }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
'max-rounds': maxRounds,
|
|
32
|
+
rounds: roundsExecuted,
|
|
33
|
+
} = args;
|
|
34
|
+
|
|
35
|
+
// stop-reason accepts: ARG_STOP_REASON env, `--stop-reason <value>`, or `--stop-reason=<value>`.
|
|
36
|
+
// (Env stays supported for callers that cannot quote safely.)
|
|
37
|
+
const argvStopReason = (() => {
|
|
38
|
+
const i = process.argv.findIndex(a => a === '--stop-reason');
|
|
39
|
+
const next = i >= 0 ? process.argv[i + 1] : undefined;
|
|
40
|
+
if (next && !next.startsWith('--')) return next;
|
|
41
|
+
const eq = process.argv.find(a => a.startsWith('--stop-reason='));
|
|
42
|
+
return eq ? eq.slice('--stop-reason='.length) : undefined;
|
|
43
|
+
})();
|
|
44
|
+
const envSkipTestAuto = process.env.ARG_SKIP_TEST_AUTO;
|
|
45
|
+
const stopReason = process.env.ARG_STOP_REASON ?? argvStopReason ?? args['stop-reason'];
|
|
46
|
+
const skipTestAuto = envSkipTestAuto !== undefined ? envSkipTestAuto === 'true' : (args['skip-test-auto'] ?? false);
|
|
47
|
+
|
|
48
|
+
if (!stopReason) {
|
|
49
|
+
console.error(JSON.stringify({ ok: false, error: 'Usage: render_final_summary --stop-reason <reason> [--rounds <n>] [--max-rounds <n>] [--skip-test-auto true|false]' }));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const overallStatus = stopReason === 'all_passed'
|
|
54
|
+
? 'all scenarios passed'
|
|
55
|
+
: `converged with stop reason: ${stopReason}`;
|
|
56
|
+
|
|
57
|
+
const skipNote = skipTestAuto
|
|
58
|
+
? '\n\n⚠ Integration test (ROUND step ④.7) was skipped because no device was detected; rerun with a device or emulator available, or set --skip-test=false.'
|
|
59
|
+
: '';
|
|
60
|
+
|
|
61
|
+
const roundsLabel = roundsExecuted !== undefined ? 'Rounds executed' : 'Max rounds';
|
|
62
|
+
const roundsValue = roundsExecuted !== undefined
|
|
63
|
+
? `${roundsExecuted}/${maxRounds || 'N/A'}`
|
|
64
|
+
: `${maxRounds || 'N/A'}`;
|
|
65
|
+
|
|
66
|
+
const summaryText = `Overall status: ${overallStatus}
|
|
67
|
+
${roundsLabel}: ${roundsValue}
|
|
68
|
+
Stop reason: ${stopReason}${skipNote}`;
|
|
69
|
+
|
|
70
|
+
const summaryMarkdown = `## Final Summary
|
|
71
|
+
|
|
72
|
+
**Overall status:** ${overallStatus}
|
|
73
|
+
|
|
74
|
+
**${roundsLabel}:** ${roundsValue}
|
|
75
|
+
|
|
76
|
+
**Stop reason:** ${stopReason}${skipNote}`;
|
|
77
|
+
|
|
78
|
+
console.log(JSON.stringify({ ok: true, summary_text: summaryText, summary_markdown: summaryMarkdown }));
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* root_mirror.mjs
|
|
4
|
+
* Phase 3 §2: Mirror canonical outputs to OUTPUT/ root for backward compatibility.
|
|
5
|
+
*
|
|
6
|
+
* CLI:
|
|
7
|
+
* --output OUTPUT/
|
|
8
|
+
*
|
|
9
|
+
* Copies:
|
|
10
|
+
* OUTPUT/code-review-report.md ← newest round that has one
|
|
11
|
+
* OUTPUT/self-test-report.md ← newest round that has one
|
|
12
|
+
* OUTPUT/self-test-fix-report.md ← if it exists
|
|
13
|
+
* OUTPUT/package-set/ ← newest round whose package-set/ is non-empty
|
|
14
|
+
* (clear-then-copy, skip if source==dest)
|
|
15
|
+
* If no round ever built: leave untouched
|
|
16
|
+
* OUTPUT/entry-default-{signed,unsigned}.hap
|
|
17
|
+
* ← best-effort copy of entry-*.hap from OUTPUT/package-set/,
|
|
18
|
+
* named after the artifact's real signing mode
|
|
19
|
+
*
|
|
20
|
+
* Output (stdout JSON):
|
|
21
|
+
* { ok, actions: [{file, action: "copied"|"skipped"|"skipped_not_found"|"error"}, ...] }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'fs';
|
|
25
|
+
import { dirname, join } from 'path';
|
|
26
|
+
|
|
27
|
+
const args = {};
|
|
28
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
29
|
+
const arg = process.argv[i];
|
|
30
|
+
if (arg.startsWith('--')) {
|
|
31
|
+
const key = arg.slice(2);
|
|
32
|
+
args[key] = process.argv[i + 1];
|
|
33
|
+
if (args[key]?.startsWith('--')) { args[key] = true; i--; }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { output: outputDir } = args;
|
|
38
|
+
|
|
39
|
+
if (!outputDir) {
|
|
40
|
+
console.error(JSON.stringify({ ok: false, error: 'Usage: root_mirror --output <path>' }));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const actions = [];
|
|
45
|
+
|
|
46
|
+
function copyIfExists(src, dst) {
|
|
47
|
+
if (!existsSync(src)) {
|
|
48
|
+
actions.push({ file: dst, action: 'skipped_not_found', src });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
52
|
+
copyFileSync(src, dst);
|
|
53
|
+
actions.push({ file: dst, action: 'copied', src });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Round discovery ───────────────────────────────────────────────────────────
|
|
57
|
+
// Rounds live directly under OUTPUT/ as round-{n}/. Scan rather than take a count
|
|
58
|
+
// argument: the directories on disk are the authority on what actually ran.
|
|
59
|
+
function roundDirsNewestFirst() {
|
|
60
|
+
if (!existsSync(outputDir)) return [];
|
|
61
|
+
return readdirSync(outputDir)
|
|
62
|
+
.map(name => ({ name, n: /^round-(\d+)$/.exec(name)?.[1] }))
|
|
63
|
+
.filter(e => e.n !== undefined)
|
|
64
|
+
.map(e => ({ dir: join(outputDir, e.name), n: parseInt(e.n, 10) }))
|
|
65
|
+
.sort((a, b) => b.n - a.n);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const rounds = roundDirsNewestFirst();
|
|
69
|
+
|
|
70
|
+
// ── Step A: mirror the newest round that has each artifact ────────────────────
|
|
71
|
+
const FILES_TO_MIRROR = ['code-review-report.md', 'self-test-report.md', 'self-test-fix-report.md'];
|
|
72
|
+
|
|
73
|
+
for (const filename of FILES_TO_MIRROR) {
|
|
74
|
+
const hit = rounds.find(r => existsSync(join(r.dir, filename)));
|
|
75
|
+
if (hit) {
|
|
76
|
+
copyIfExists(join(hit.dir, filename), join(outputDir, filename));
|
|
77
|
+
} else {
|
|
78
|
+
actions.push({ file: join(outputDir, filename), action: 'skipped_no_round_has_this_file' });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Step B: mirror the newest round that built (LAST_GOOD_PACKAGE_SET) ────────
|
|
83
|
+
let lastGoodPackageSet = null;
|
|
84
|
+
for (const r of rounds) {
|
|
85
|
+
const roundPackageSet = join(r.dir, 'package-set');
|
|
86
|
+
if (existsSync(roundPackageSet)) {
|
|
87
|
+
const files = readdirSync(roundPackageSet).filter(f => f.includes('signed') || f.endsWith('.hap') || f.endsWith('.hsp'));
|
|
88
|
+
if (files.length > 0) {
|
|
89
|
+
lastGoodPackageSet = roundPackageSet;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const outputPackageSet = join(outputDir, 'package-set');
|
|
96
|
+
|
|
97
|
+
if (lastGoodPackageSet) {
|
|
98
|
+
// Skip if source == dest (already up to date)
|
|
99
|
+
const srcFiles = readdirSync(lastGoodPackageSet).sort().join(',');
|
|
100
|
+
const dstFiles = existsSync(outputPackageSet) ? readdirSync(outputPackageSet).sort().join(',') : '';
|
|
101
|
+
if (srcFiles === dstFiles && srcFiles !== '') {
|
|
102
|
+
actions.push({ file: 'package-set', action: 'skipped_already_current' });
|
|
103
|
+
} else {
|
|
104
|
+
rmSync(outputPackageSet, { recursive: true, force: true });
|
|
105
|
+
mkdirSync(outputPackageSet, { recursive: true });
|
|
106
|
+
for (const f of readdirSync(lastGoodPackageSet)) {
|
|
107
|
+
copyFileSync(join(lastGoodPackageSet, f), join(outputPackageSet, f));
|
|
108
|
+
}
|
|
109
|
+
actions.push({ file: 'package-set/', action: 'copied', source: lastGoodPackageSet });
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
actions.push({ file: 'package-set/', action: 'skipped_no_round_built' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Step C: root entry HAP mirror (best-effort) ────────────────────────────────
|
|
116
|
+
// The mirrored name states the artifact's real signing mode. A project with
|
|
117
|
+
// `app.signingConfigs: []` produces entry-default-unsigned.hap; copying that to
|
|
118
|
+
// entry-default-signed.hap would hand a consumer a file whose name promises an
|
|
119
|
+
// installable package and whose bytes are not one.
|
|
120
|
+
if (existsSync(outputPackageSet)) {
|
|
121
|
+
const entryHap = readdirSync(outputPackageSet).find(f => f.startsWith('entry-') && f.endsWith('.hap'));
|
|
122
|
+
if (entryHap) {
|
|
123
|
+
const unsigned = entryHap.includes('-unsigned.');
|
|
124
|
+
const mirrorName = unsigned ? 'entry-default-unsigned.hap' : 'entry-default-signed.hap';
|
|
125
|
+
copyFileSync(join(outputPackageSet, entryHap), join(outputDir, mirrorName));
|
|
126
|
+
actions.push({ file: mirrorName, action: 'copied', source: entryHap, signing_mode: unsigned ? 'unsigned' : 'signed' });
|
|
127
|
+
if (unsigned) {
|
|
128
|
+
actions.push({ file: 'entry-default-signed.hap', action: 'skipped_unsigned_build', note: 'project has no signing config; mirrored as entry-default-unsigned.hap' });
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
actions.push({ file: 'entry-default-*.hap', action: 'skipped_no_entry_hap_in_package_set' });
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
actions.push({ file: 'entry-default-*.hap', action: 'skipped_package_set_not_found' });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(JSON.stringify({ ok: true, actions }, null, 2));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hometrans-scripts",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "HomeTrans 流水线 Node 脚本依赖(设备驱动 device_ui/l2_run 经 device_ui_core 懒加载 hypium-driver;其余零依赖)。插件形态下 scripts/ 位于 node_modules 内部,无法在原地 npm install —— device_ui_core.loadDriverPkg 会回退到全局 npm root,缺失时按下面声明的版本自动 npm install -g。该版本号是自动安装的唯一来源,只接受精确 semver(范围写法会被降级为 latest)。",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20.11.1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"hypium-driver": "^6.1.210"
|
|
11
|
+
}
|
|
12
|
+
}
|