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,605 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildAreaContentTxt,
|
|
3
|
+
renderedPath,
|
|
4
|
+
serializeReqMarkdown,
|
|
5
|
+
serializeReqTxtAreaContent,
|
|
6
|
+
} from "../generation/index.js";
|
|
7
|
+
import { expandRenderItems } from "./orchestrate.js";
|
|
8
|
+
// Verbatim port: models sometimes repeat the title on the first body line
|
|
9
|
+
// even though the prompt forbids it.
|
|
10
|
+
export function stripLeadingTitle(body, title) {
|
|
11
|
+
const trimmed = body.replace(/^\s+/, "");
|
|
12
|
+
if (!trimmed.startsWith(title)) return body;
|
|
13
|
+
return trimmed.slice(title.length).replace(/^\s*\n+/, "");
|
|
14
|
+
}
|
|
15
|
+
/** Feature ids `filterRenderableAreas` kept for this run (i.e. actually
|
|
16
|
+
* eligible to be sent to the Agent). A Feature outside this set was never
|
|
17
|
+
* attempted — it is omitted from the merged output rather than falling back
|
|
18
|
+
* to the Stage 1 draft, which is reserved for Features the Agent attempted
|
|
19
|
+
* and failed. */
|
|
20
|
+
function collectFeatureIds(reqModel) {
|
|
21
|
+
return new Set(
|
|
22
|
+
reqModel.areas.flatMap((area) =>
|
|
23
|
+
area.features.map((feature) => feature.id),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function selectEntry(
|
|
28
|
+
item,
|
|
29
|
+
results,
|
|
30
|
+
templateTitle,
|
|
31
|
+
templateBody,
|
|
32
|
+
renderableFeatureIds,
|
|
33
|
+
) {
|
|
34
|
+
if (!renderableFeatureIds.has(item.feature.id)) return undefined;
|
|
35
|
+
const result = results.get(item.feature.id);
|
|
36
|
+
if (result?.status !== "ok" || result.body === undefined) {
|
|
37
|
+
return { title: templateTitle, body: templateBody };
|
|
38
|
+
}
|
|
39
|
+
const title =
|
|
40
|
+
result.titleValid === true && result.title !== undefined
|
|
41
|
+
? result.title
|
|
42
|
+
: templateTitle;
|
|
43
|
+
return { title, body: result.body };
|
|
44
|
+
}
|
|
45
|
+
function selectEntryFromArea(
|
|
46
|
+
featureId,
|
|
47
|
+
areaResults,
|
|
48
|
+
templateTitle,
|
|
49
|
+
templateBody,
|
|
50
|
+
renderableFeatureIds,
|
|
51
|
+
) {
|
|
52
|
+
if (!renderableFeatureIds.has(featureId)) return undefined;
|
|
53
|
+
// Find the feature result across all area results
|
|
54
|
+
for (const areaResult of areaResults.values()) {
|
|
55
|
+
if (areaResult.status !== "ok") continue;
|
|
56
|
+
const featureResult = areaResult.features.get(featureId);
|
|
57
|
+
if (featureResult === undefined) continue;
|
|
58
|
+
if (featureResult.status !== "ok" || featureResult.body === undefined) {
|
|
59
|
+
return { title: templateTitle, body: templateBody };
|
|
60
|
+
}
|
|
61
|
+
const title =
|
|
62
|
+
featureResult.titleValid === true && featureResult.title !== undefined
|
|
63
|
+
? featureResult.title
|
|
64
|
+
: templateTitle;
|
|
65
|
+
return { title, body: featureResult.body };
|
|
66
|
+
}
|
|
67
|
+
return { title: templateTitle, body: templateBody };
|
|
68
|
+
}
|
|
69
|
+
function findFeatureAttemptResult(featureId, areaResults) {
|
|
70
|
+
for (const areaResult of areaResults.values()) {
|
|
71
|
+
if (areaResult.status !== "ok") continue;
|
|
72
|
+
const featureResult = areaResult.features.get(featureId);
|
|
73
|
+
if (featureResult !== undefined) return featureResult;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
function addedEntriesForArea(areaId, areaResults) {
|
|
78
|
+
const result = areaResults.get(areaId);
|
|
79
|
+
if (result?.status !== "ok") return [];
|
|
80
|
+
return (result.addedFeatures ?? []).flatMap((feature) =>
|
|
81
|
+
feature.status === "ok" &&
|
|
82
|
+
feature.title !== undefined &&
|
|
83
|
+
feature.body !== undefined
|
|
84
|
+
? [{ title: feature.title, body: feature.body }]
|
|
85
|
+
: [],
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
/** Feature ids whose own template slot must be skipped (never emitted) —
|
|
89
|
+
* every non-earliest member of a merge group the render Agent reported (see
|
|
90
|
+
* FeatureAttemptResult.mergedFeatureIds). A merge group's content is
|
|
91
|
+
* rendered exactly once, at the position of whichever member appears first
|
|
92
|
+
* in Area.features order; later members' 3-line slots are still consumed
|
|
93
|
+
* from the template (cursor still advances) but push nothing. Standalone
|
|
94
|
+
* (non-merged) Features are never in this set — selectEntryFromArea still
|
|
95
|
+
* decides per its own rules (draft fallback / renderableFeatureIds) whether
|
|
96
|
+
* they actually emit. */
|
|
97
|
+
function planSkippedMergeSlots(area, areaResults) {
|
|
98
|
+
const skip = new Set();
|
|
99
|
+
const seenGroups = new Set();
|
|
100
|
+
for (const feature of area.features) {
|
|
101
|
+
const result = findFeatureAttemptResult(feature.id, areaResults);
|
|
102
|
+
const mergedIds =
|
|
103
|
+
result?.status === "ok" ? result.mergedFeatureIds : undefined;
|
|
104
|
+
if (mergedIds === undefined || mergedIds.length <= 1) continue;
|
|
105
|
+
const groupKey = [...mergedIds].sort().join(" ");
|
|
106
|
+
if (seenGroups.has(groupKey)) {
|
|
107
|
+
skip.add(feature.id);
|
|
108
|
+
} else {
|
|
109
|
+
seenGroups.add(groupKey);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return skip;
|
|
113
|
+
}
|
|
114
|
+
function assertTemplateShape(lines, index, areaId) {
|
|
115
|
+
if (lines[index] === undefined) {
|
|
116
|
+
throw new Error(`template REQ text ended unexpectedly at Area ${areaId}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Area heads are taken verbatim from the Stage 1 deterministic draft
|
|
120
|
+
* (renderReqDraft output, recomputed by the caller rather than read from
|
|
121
|
+
* req.md); Feature entries use the Agent title+body when the render
|
|
122
|
+
* succeeded, the draft when the Agent attempted and failed, and are omitted
|
|
123
|
+
* entirely when `renderableReqModel` never sent the Feature to the Agent in
|
|
124
|
+
* the first place (e.g. a navigation-only Feature dropped by
|
|
125
|
+
* filterRenderableAreas) — a Feature the Agent never saw does not get its
|
|
126
|
+
* draft text pasted in as if it had been considered. */
|
|
127
|
+
export function mergeReqText(
|
|
128
|
+
fullReqModel,
|
|
129
|
+
renderableReqModel,
|
|
130
|
+
results,
|
|
131
|
+
templateText,
|
|
132
|
+
) {
|
|
133
|
+
const lines = templateText.split("\n");
|
|
134
|
+
const items = expandRenderItems(fullReqModel);
|
|
135
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
136
|
+
const out = [];
|
|
137
|
+
let cursor = 0;
|
|
138
|
+
let itemIndex = 0;
|
|
139
|
+
fullReqModel.areas.forEach((area, areaIndex) => {
|
|
140
|
+
while (!lines[cursor].startsWith("区域说明:")) {
|
|
141
|
+
assertTemplateShape(lines, cursor, area.id);
|
|
142
|
+
out.push(lines[cursor]);
|
|
143
|
+
cursor += 1;
|
|
144
|
+
}
|
|
145
|
+
out.push(lines[cursor]);
|
|
146
|
+
cursor += 1;
|
|
147
|
+
for (const feature of area.features) {
|
|
148
|
+
const item = items[itemIndex];
|
|
149
|
+
itemIndex += 1;
|
|
150
|
+
const templateTitle = lines[cursor + 1];
|
|
151
|
+
const templateBody = lines[cursor + 2];
|
|
152
|
+
assertTemplateShape(lines, cursor + 2, feature.id);
|
|
153
|
+
const entry = selectEntry(
|
|
154
|
+
item,
|
|
155
|
+
results,
|
|
156
|
+
templateTitle,
|
|
157
|
+
templateBody,
|
|
158
|
+
renderableFeatureIds,
|
|
159
|
+
);
|
|
160
|
+
if (entry !== undefined) out.push("", entry.title, entry.body);
|
|
161
|
+
cursor += 3;
|
|
162
|
+
}
|
|
163
|
+
if (areaIndex < fullReqModel.areas.length - 1) {
|
|
164
|
+
out.push("");
|
|
165
|
+
cursor += 1;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return `${out.join("\n")}\n`;
|
|
169
|
+
}
|
|
170
|
+
/** Same walk as mergeReqText, but returns the Area-blocked Markdown form
|
|
171
|
+
* (req.md) directly from the structured {title, body} entries, instead of
|
|
172
|
+
* flattening into a single string and re-parsing it — an Agent-rendered
|
|
173
|
+
* body may legitimately contain multiple lines or embedded blank lines
|
|
174
|
+
* (e.g. Law 4's 枚举行 per `options` entry in prompt.ts), which a re-parse
|
|
175
|
+
* of flat text cannot reliably un-flatten.
|
|
176
|
+
*
|
|
177
|
+
* Per-feature mode has no per-Area Agent reachability judgment, so
|
|
178
|
+
* isAgentJudgedUnreachable falls back to static analysis
|
|
179
|
+
* (canonicalPath.status === "unreachable") — the same default-drop
|
|
180
|
+
* behaviour as mergeAreaReqMarkdown, controlled by the same
|
|
181
|
+
* MergeAreaOptions.includeUnreachableAreas flag. */
|
|
182
|
+
export function mergeReqMarkdown(
|
|
183
|
+
fullReqModel,
|
|
184
|
+
renderableReqModel,
|
|
185
|
+
results,
|
|
186
|
+
templateText,
|
|
187
|
+
options = {},
|
|
188
|
+
) {
|
|
189
|
+
const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
|
|
190
|
+
const lines = templateText.split("\n");
|
|
191
|
+
const items = expandRenderItems(fullReqModel);
|
|
192
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
193
|
+
const blocks = [];
|
|
194
|
+
let cursor = 0;
|
|
195
|
+
let itemIndex = 0;
|
|
196
|
+
fullReqModel.areas.forEach((area, areaIndex) => {
|
|
197
|
+
const headerLines = [];
|
|
198
|
+
while (!lines[cursor].startsWith("区域说明:")) {
|
|
199
|
+
assertTemplateShape(lines, cursor, area.id);
|
|
200
|
+
headerLines.push(lines[cursor]);
|
|
201
|
+
cursor += 1;
|
|
202
|
+
}
|
|
203
|
+
headerLines.push(lines[cursor]);
|
|
204
|
+
cursor += 1;
|
|
205
|
+
const features = [];
|
|
206
|
+
for (const feature of area.features) {
|
|
207
|
+
const item = items[itemIndex];
|
|
208
|
+
itemIndex += 1;
|
|
209
|
+
const templateTitle = lines[cursor + 1];
|
|
210
|
+
const templateBody = lines[cursor + 2];
|
|
211
|
+
assertTemplateShape(lines, cursor + 2, feature.id);
|
|
212
|
+
const entry = selectEntry(
|
|
213
|
+
item,
|
|
214
|
+
results,
|
|
215
|
+
templateTitle,
|
|
216
|
+
templateBody,
|
|
217
|
+
renderableFeatureIds,
|
|
218
|
+
);
|
|
219
|
+
if (entry !== undefined) features.push(entry);
|
|
220
|
+
cursor += 3;
|
|
221
|
+
}
|
|
222
|
+
blocks.push({ headerLines, features });
|
|
223
|
+
if (areaIndex < fullReqModel.areas.length - 1) cursor += 1;
|
|
224
|
+
});
|
|
225
|
+
// Per-feature mode has no AreaAttemptResult — pass an empty Map so
|
|
226
|
+
// isAgentJudgedUnreachable falls back to static analysis.
|
|
227
|
+
const ordered = partitionAreaBlocks(
|
|
228
|
+
fullReqModel.areas,
|
|
229
|
+
blocks,
|
|
230
|
+
new Map(),
|
|
231
|
+
includeUnreachableAreas,
|
|
232
|
+
);
|
|
233
|
+
return serializeReqMarkdown(ordered);
|
|
234
|
+
}
|
|
235
|
+
function selectAreaDescriptionLine(areaId, areaResults, templateLine) {
|
|
236
|
+
const result = areaResults.get(areaId);
|
|
237
|
+
const description =
|
|
238
|
+
result?.status === "ok" ? result.areaDescription?.trim() : undefined;
|
|
239
|
+
return description === undefined || description.length === 0
|
|
240
|
+
? templateLine
|
|
241
|
+
: `区域说明:${description}`;
|
|
242
|
+
}
|
|
243
|
+
/** The Agent owns the final source-grounded 访问路径 for every Area. Retain
|
|
244
|
+
* the deterministic draft only when that Agent response has no accessPath. */
|
|
245
|
+
function selectAccessPathLine(area, areaResults, templateLine) {
|
|
246
|
+
const result = areaResults.get(area.id);
|
|
247
|
+
const accessPath =
|
|
248
|
+
result?.status === "ok" ? result.accessPath?.trim() : undefined;
|
|
249
|
+
return accessPath === undefined || accessPath.length === 0
|
|
250
|
+
? templateLine
|
|
251
|
+
: `访问路径:${accessPath}`;
|
|
252
|
+
}
|
|
253
|
+
/** True for an Area that should sink to the end of req.md: it was
|
|
254
|
+
* statically unreachable AND the Agent either never confirmed a real access
|
|
255
|
+
* path (no result, a failed attempt, or an explicit "confirmed unreachable"
|
|
256
|
+
* judgment) — conservative default when there is no better information.
|
|
257
|
+
* Areas the Agent judged reachable (reachable: true, alongside a found
|
|
258
|
+
* accessPath) join the reachable group instead, ahead of any Area still
|
|
259
|
+
* judged unreachable. Never affects Areas that were reachable by static
|
|
260
|
+
* analysis in the first place — those are always in the reachable group. */
|
|
261
|
+
function isAgentJudgedUnreachable(area, areaResults) {
|
|
262
|
+
if (area.canonicalPath.status !== "unreachable") return false;
|
|
263
|
+
const result = areaResults.get(area.id);
|
|
264
|
+
return !(result?.status === "ok" && result.reachable === true);
|
|
265
|
+
}
|
|
266
|
+
/** Stable partition of already-assembled per-Area blocks: Areas judged
|
|
267
|
+
* reachable first (original relative order), then — only when
|
|
268
|
+
* includeUnreachableAreas is true — Areas judged unreachable (original
|
|
269
|
+
* relative order) appended after them; otherwise those blocks are dropped
|
|
270
|
+
* — see isAgentJudgedUnreachable. Runs only after both the deterministic
|
|
271
|
+
* template and the per-Area merge walk have already matched every block to
|
|
272
|
+
* its source Area positionally, so it cannot desync that walk (it only
|
|
273
|
+
* reorders/drops whole, already-built blocks). */
|
|
274
|
+
function partitionAreaBlocks(
|
|
275
|
+
originalAreas,
|
|
276
|
+
blocks,
|
|
277
|
+
areaResults,
|
|
278
|
+
includeUnreachableAreas,
|
|
279
|
+
) {
|
|
280
|
+
const reachable = [];
|
|
281
|
+
const unreachable = [];
|
|
282
|
+
originalAreas.forEach((area, index) => {
|
|
283
|
+
(isAgentJudgedUnreachable(area, areaResults)
|
|
284
|
+
? unreachable
|
|
285
|
+
: reachable
|
|
286
|
+
).push(blocks[index]);
|
|
287
|
+
});
|
|
288
|
+
return includeUnreachableAreas ? [...reachable, ...unreachable] : reachable;
|
|
289
|
+
}
|
|
290
|
+
/** Merge per-Area rendering results into the Stage 1 deterministic draft
|
|
291
|
+
* (renderReqDraft output, recomputed by the caller rather than read from
|
|
292
|
+
* req.md): Area heads verbatim except 区域说明 (Agent-produced when the
|
|
293
|
+
* session succeeded, else draft fallback) and 访问路径 for unreachable Areas
|
|
294
|
+
* (Agent-produced access finding/confirmation when available, else draft
|
|
295
|
+
* fallback); Feature entries from Agent results when available, the draft
|
|
296
|
+
* when the Agent attempted and failed, and omitted entirely when
|
|
297
|
+
* `renderableReqModel` never sent the Feature to the Agent at all (see
|
|
298
|
+
* mergeReqText). Areas the Agent judges unreachable (see
|
|
299
|
+
* isAgentJudgedUnreachable) are dropped by default, or moved after every
|
|
300
|
+
* Area it judges reachable when `options.includeUnreachableAreas` is set
|
|
301
|
+
* (see MergeAreaOptions). */
|
|
302
|
+
export function mergeAreaReqText(
|
|
303
|
+
fullReqModel,
|
|
304
|
+
renderableReqModel,
|
|
305
|
+
areaResults,
|
|
306
|
+
templateText,
|
|
307
|
+
options = {},
|
|
308
|
+
) {
|
|
309
|
+
const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
|
|
310
|
+
const lines = templateText.split("\n");
|
|
311
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
312
|
+
const blocks = [];
|
|
313
|
+
let cursor = 0;
|
|
314
|
+
fullReqModel.areas.forEach((area) => {
|
|
315
|
+
const block = [];
|
|
316
|
+
while (!lines[cursor].startsWith("区域说明:")) {
|
|
317
|
+
assertTemplateShape(lines, cursor, area.id);
|
|
318
|
+
block.push(
|
|
319
|
+
lines[cursor].startsWith("访问路径:")
|
|
320
|
+
? selectAccessPathLine(area, areaResults, lines[cursor])
|
|
321
|
+
: lines[cursor],
|
|
322
|
+
);
|
|
323
|
+
cursor += 1;
|
|
324
|
+
}
|
|
325
|
+
block.push(selectAreaDescriptionLine(area.id, areaResults, lines[cursor]));
|
|
326
|
+
cursor += 1;
|
|
327
|
+
const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
|
|
328
|
+
for (const feature of area.features) {
|
|
329
|
+
const templateTitle = lines[cursor + 1];
|
|
330
|
+
const templateBody = lines[cursor + 2];
|
|
331
|
+
assertTemplateShape(lines, cursor + 2, feature.id);
|
|
332
|
+
const entry = skippedMergeSlots.has(feature.id)
|
|
333
|
+
? undefined
|
|
334
|
+
: selectEntryFromArea(
|
|
335
|
+
feature.id,
|
|
336
|
+
areaResults,
|
|
337
|
+
templateTitle,
|
|
338
|
+
templateBody,
|
|
339
|
+
renderableFeatureIds,
|
|
340
|
+
);
|
|
341
|
+
if (entry !== undefined) block.push("", entry.title, entry.body);
|
|
342
|
+
cursor += 3;
|
|
343
|
+
}
|
|
344
|
+
for (const entry of addedEntriesForArea(area.id, areaResults)) {
|
|
345
|
+
block.push("", entry.title, entry.body);
|
|
346
|
+
}
|
|
347
|
+
blocks.push(block);
|
|
348
|
+
});
|
|
349
|
+
const ordered = partitionAreaBlocks(
|
|
350
|
+
fullReqModel.areas,
|
|
351
|
+
blocks,
|
|
352
|
+
areaResults,
|
|
353
|
+
includeUnreachableAreas,
|
|
354
|
+
);
|
|
355
|
+
return `${ordered.map((block) => block.join("\n")).join("\n\n")}\n`;
|
|
356
|
+
}
|
|
357
|
+
/** Same walk as mergeAreaReqText, but returns the Area-blocked Markdown form
|
|
358
|
+
* directly from structured entries — see mergeReqMarkdown for why this
|
|
359
|
+
* avoids re-parsing a flattened, potentially multi-line Agent body. Areas
|
|
360
|
+
* the Agent judges unreachable are dropped by default, or moved after every
|
|
361
|
+
* Area it judges reachable when `options.includeUnreachableAreas` is set
|
|
362
|
+
* (see MergeAreaOptions, same as mergeAreaReqText). */
|
|
363
|
+
export function mergeAreaReqMarkdown(
|
|
364
|
+
fullReqModel,
|
|
365
|
+
renderableReqModel,
|
|
366
|
+
areaResults,
|
|
367
|
+
templateText,
|
|
368
|
+
options = {},
|
|
369
|
+
) {
|
|
370
|
+
const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
|
|
371
|
+
const lines = templateText.split("\n");
|
|
372
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
373
|
+
const blocks = [];
|
|
374
|
+
let cursor = 0;
|
|
375
|
+
fullReqModel.areas.forEach((area, areaIndex) => {
|
|
376
|
+
const headerLines = [];
|
|
377
|
+
while (!lines[cursor].startsWith("区域说明:")) {
|
|
378
|
+
assertTemplateShape(lines, cursor, area.id);
|
|
379
|
+
headerLines.push(
|
|
380
|
+
lines[cursor].startsWith("访问路径:")
|
|
381
|
+
? selectAccessPathLine(area, areaResults, lines[cursor])
|
|
382
|
+
: lines[cursor],
|
|
383
|
+
);
|
|
384
|
+
cursor += 1;
|
|
385
|
+
}
|
|
386
|
+
headerLines.push(
|
|
387
|
+
selectAreaDescriptionLine(area.id, areaResults, lines[cursor]),
|
|
388
|
+
);
|
|
389
|
+
cursor += 1;
|
|
390
|
+
const features = [];
|
|
391
|
+
const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
|
|
392
|
+
for (const feature of area.features) {
|
|
393
|
+
const templateTitle = lines[cursor + 1];
|
|
394
|
+
const templateBody = lines[cursor + 2];
|
|
395
|
+
assertTemplateShape(lines, cursor + 2, feature.id);
|
|
396
|
+
const entry = skippedMergeSlots.has(feature.id)
|
|
397
|
+
? undefined
|
|
398
|
+
: selectEntryFromArea(
|
|
399
|
+
feature.id,
|
|
400
|
+
areaResults,
|
|
401
|
+
templateTitle,
|
|
402
|
+
templateBody,
|
|
403
|
+
renderableFeatureIds,
|
|
404
|
+
);
|
|
405
|
+
if (entry !== undefined) features.push(entry);
|
|
406
|
+
cursor += 3;
|
|
407
|
+
}
|
|
408
|
+
features.push(...addedEntriesForArea(area.id, areaResults));
|
|
409
|
+
blocks.push({ headerLines, features });
|
|
410
|
+
if (areaIndex < fullReqModel.areas.length - 1) cursor += 1;
|
|
411
|
+
});
|
|
412
|
+
const ordered = partitionAreaBlocks(
|
|
413
|
+
fullReqModel.areas,
|
|
414
|
+
blocks,
|
|
415
|
+
areaResults,
|
|
416
|
+
includeUnreachableAreas,
|
|
417
|
+
);
|
|
418
|
+
return serializeReqMarkdown(ordered);
|
|
419
|
+
}
|
|
420
|
+
/** Build the suffix part of a txt-format title (without the access path
|
|
421
|
+
* prefix). The template title is `{path} - {areaName} - {controlName}`;
|
|
422
|
+
* this returns `{areaName} - {controlName}`. */
|
|
423
|
+
function titleSuffix(templateTitle, path) {
|
|
424
|
+
const prefix = `${path} - `;
|
|
425
|
+
return templateTitle.startsWith(prefix)
|
|
426
|
+
? templateTitle.slice(prefix.length)
|
|
427
|
+
: templateTitle;
|
|
428
|
+
}
|
|
429
|
+
/** Txt-format equivalent of selectEntryFromArea: returns the merged
|
|
430
|
+
* {title, body} for one Feature, preferring Agent output over the template.
|
|
431
|
+
* Agent titles are already complete navigation titles, so they are not
|
|
432
|
+
* prefixed with an independently rendered access path. */
|
|
433
|
+
function selectEntryFromAreaTxt(
|
|
434
|
+
featureId,
|
|
435
|
+
areaResults,
|
|
436
|
+
templateTitle,
|
|
437
|
+
templateBody,
|
|
438
|
+
accessPath,
|
|
439
|
+
renderableFeatureIds,
|
|
440
|
+
) {
|
|
441
|
+
if (!renderableFeatureIds.has(featureId)) return undefined;
|
|
442
|
+
for (const areaResult of areaResults.values()) {
|
|
443
|
+
if (areaResult.status !== "ok") continue;
|
|
444
|
+
const featureResult = areaResult.features.get(featureId);
|
|
445
|
+
if (featureResult === undefined) continue;
|
|
446
|
+
if (featureResult.status !== "ok" || featureResult.body === undefined) {
|
|
447
|
+
return { title: templateTitle, body: templateBody };
|
|
448
|
+
}
|
|
449
|
+
const agentTitle =
|
|
450
|
+
featureResult.titleValid === true && featureResult.title !== undefined
|
|
451
|
+
? featureResult.title
|
|
452
|
+
: titleSuffix(templateTitle, accessPath);
|
|
453
|
+
return {
|
|
454
|
+
title:
|
|
455
|
+
featureResult.titleValid === true && featureResult.title !== undefined
|
|
456
|
+
? agentTitle
|
|
457
|
+
: `${accessPath} - ${agentTitle}`,
|
|
458
|
+
body: featureResult.body,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
return { title: templateTitle, body: templateBody };
|
|
462
|
+
}
|
|
463
|
+
/** Merge per-Area rendering results into per-area plain-text strings (one
|
|
464
|
+
* per Area). Operates on structured data (buildAreaContentTxt) rather
|
|
465
|
+
* than walking template text. Areas the Agent judges unreachable are
|
|
466
|
+
* dropped by default; pass includeUnreachableAreas to keep them. */
|
|
467
|
+
export function mergeAreaReqTxt(
|
|
468
|
+
fullReqModel,
|
|
469
|
+
renderableReqModel,
|
|
470
|
+
areaResults,
|
|
471
|
+
graph,
|
|
472
|
+
options = {},
|
|
473
|
+
) {
|
|
474
|
+
return mergeAreaReqContents(
|
|
475
|
+
fullReqModel,
|
|
476
|
+
renderableReqModel,
|
|
477
|
+
areaResults,
|
|
478
|
+
graph,
|
|
479
|
+
options,
|
|
480
|
+
).map(({ content }) => serializeReqTxtAreaContent(content));
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Merge per-Area rendering results without flattening them to text. This is
|
|
484
|
+
* the canonical source for downstream adapters; `mergeAreaReqTxt` delegates
|
|
485
|
+
* to it so both paths have exactly the same fallback, reachability and
|
|
486
|
+
* Agent-added Feature semantics.
|
|
487
|
+
*/
|
|
488
|
+
export function mergeAreaReqContents(
|
|
489
|
+
fullReqModel,
|
|
490
|
+
renderableReqModel,
|
|
491
|
+
areaResults,
|
|
492
|
+
graph,
|
|
493
|
+
options = {},
|
|
494
|
+
) {
|
|
495
|
+
const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
|
|
496
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
497
|
+
const contents = fullReqModel.areas.map((area) => {
|
|
498
|
+
const result = areaResults.get(area.id);
|
|
499
|
+
const template = buildAreaContentTxt(area, graph);
|
|
500
|
+
const detPath = renderedPath(area, graph);
|
|
501
|
+
// Use the Agent's complete source-grounded access path when available,
|
|
502
|
+
// otherwise keep the deterministic fallback path.
|
|
503
|
+
const accessPath =
|
|
504
|
+
(result?.status === "ok" && result.accessPath?.trim()) || detPath;
|
|
505
|
+
// Rebuild headerLines (preconditions only — area description is dropped).
|
|
506
|
+
const headerLines = template.headerLines;
|
|
507
|
+
// Features: prefer Agent output, fall back to template.
|
|
508
|
+
const skippedMergeSlots = planSkippedMergeSlots(area, areaResults);
|
|
509
|
+
const features = [];
|
|
510
|
+
for (let i = 0; i < area.features.length; i++) {
|
|
511
|
+
const feature = area.features[i];
|
|
512
|
+
if (skippedMergeSlots.has(feature.id)) continue;
|
|
513
|
+
const templateFeature = template.features[i];
|
|
514
|
+
// Rebuild template title with the (possibly overridden) access path.
|
|
515
|
+
const tplTitle = `${accessPath} - ${titleSuffix(templateFeature.title, detPath)}`;
|
|
516
|
+
const entry = selectEntryFromAreaTxt(
|
|
517
|
+
feature.id,
|
|
518
|
+
areaResults,
|
|
519
|
+
tplTitle,
|
|
520
|
+
templateFeature.body,
|
|
521
|
+
accessPath,
|
|
522
|
+
renderableFeatureIds,
|
|
523
|
+
);
|
|
524
|
+
if (entry !== undefined) features.push(entry);
|
|
525
|
+
}
|
|
526
|
+
features.push(...addedEntriesForArea(area.id, areaResults));
|
|
527
|
+
return { areaId: area.id, content: { headerLines, features } };
|
|
528
|
+
});
|
|
529
|
+
const ordered = partitionAreaBlocks(
|
|
530
|
+
fullReqModel.areas,
|
|
531
|
+
contents,
|
|
532
|
+
areaResults,
|
|
533
|
+
includeUnreachableAreas,
|
|
534
|
+
);
|
|
535
|
+
return ordered.filter((entry) => entry.content.features.length > 0);
|
|
536
|
+
}
|
|
537
|
+
/** Merge per-Feature rendering results into per-area plain-text strings.
|
|
538
|
+
* Like mergeAreaReqTxt but results are keyed by Feature id (per-feature
|
|
539
|
+
* rendering mode). Unreachable Areas are dropped by default (same as
|
|
540
|
+
* mergeAreaReqTxt), controlled by MergeAreaOptions.includeUnreachableAreas. */
|
|
541
|
+
export function mergeReqTxt(
|
|
542
|
+
fullReqModel,
|
|
543
|
+
renderableReqModel,
|
|
544
|
+
results,
|
|
545
|
+
graph,
|
|
546
|
+
options = {},
|
|
547
|
+
) {
|
|
548
|
+
return mergeReqContents(
|
|
549
|
+
fullReqModel,
|
|
550
|
+
renderableReqModel,
|
|
551
|
+
results,
|
|
552
|
+
graph,
|
|
553
|
+
options,
|
|
554
|
+
).map(({ content }) => serializeReqTxtAreaContent(content));
|
|
555
|
+
}
|
|
556
|
+
/** Structured counterpart of `mergeReqTxt` for per-Feature rendering. */
|
|
557
|
+
export function mergeReqContents(
|
|
558
|
+
fullReqModel,
|
|
559
|
+
renderableReqModel,
|
|
560
|
+
results,
|
|
561
|
+
graph,
|
|
562
|
+
options = {},
|
|
563
|
+
) {
|
|
564
|
+
const includeUnreachableAreas = options.includeUnreachableAreas ?? false;
|
|
565
|
+
const renderableFeatureIds = collectFeatureIds(renderableReqModel);
|
|
566
|
+
const contents = fullReqModel.areas.map((area) => {
|
|
567
|
+
const template = buildAreaContentTxt(area, graph);
|
|
568
|
+
const detPath = renderedPath(area, graph);
|
|
569
|
+
const accessPath = detPath;
|
|
570
|
+
const headerLines = template.headerLines;
|
|
571
|
+
const features = [];
|
|
572
|
+
for (let i = 0; i < area.features.length; i++) {
|
|
573
|
+
const feature = area.features[i];
|
|
574
|
+
if (!renderableFeatureIds.has(feature.id)) continue;
|
|
575
|
+
const templateFeature = template.features[i];
|
|
576
|
+
const result = results.get(feature.id);
|
|
577
|
+
const tplTitle = `${accessPath} - ${titleSuffix(templateFeature.title, detPath)}`;
|
|
578
|
+
if (result?.status === "ok" && result.body !== undefined) {
|
|
579
|
+
const agentTitle =
|
|
580
|
+
result.titleValid === true && result.title !== undefined
|
|
581
|
+
? result.title
|
|
582
|
+
: titleSuffix(templateFeature.title, accessPath);
|
|
583
|
+
features.push({
|
|
584
|
+
title:
|
|
585
|
+
result.titleValid === true && result.title !== undefined
|
|
586
|
+
? agentTitle
|
|
587
|
+
: `${accessPath} - ${agentTitle}`,
|
|
588
|
+
body: result.body,
|
|
589
|
+
});
|
|
590
|
+
} else {
|
|
591
|
+
features.push({ title: tplTitle, body: templateFeature.body });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return { areaId: area.id, content: { headerLines, features } };
|
|
595
|
+
});
|
|
596
|
+
// Per-feature mode has no AreaAttemptResult — pass an empty Map so
|
|
597
|
+
// isAgentJudgedUnreachable falls back to static analysis.
|
|
598
|
+
const ordered = partitionAreaBlocks(
|
|
599
|
+
fullReqModel.areas,
|
|
600
|
+
contents,
|
|
601
|
+
new Map(),
|
|
602
|
+
includeUnreachableAreas,
|
|
603
|
+
);
|
|
604
|
+
return ordered.filter((entry) => entry.content.features.length > 0);
|
|
605
|
+
}
|