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,811 @@
|
|
|
1
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { buildNameTraceEntries } from "../analysis/names/index.js";
|
|
4
|
+
import {
|
|
5
|
+
ensureOutputDirectoryEmpty,
|
|
6
|
+
OutputDirectoryNotEmptyError,
|
|
7
|
+
} from "../core/output-guard.js";
|
|
8
|
+
import { renderReqDraft } from "../generation/index.js";
|
|
9
|
+
import {
|
|
10
|
+
GenerationError,
|
|
11
|
+
filterRenderableAreas,
|
|
12
|
+
loadStage2AreaGraph,
|
|
13
|
+
loadStage2GapAudit,
|
|
14
|
+
loadStage2InputWithReport,
|
|
15
|
+
sortAreasReachableFirst,
|
|
16
|
+
} from "../stage2-input.js";
|
|
17
|
+
import { loadStage2NameTraceabilityInput } from "../stage2-name-trace-input.js";
|
|
18
|
+
import {
|
|
19
|
+
createClaudeBackend,
|
|
20
|
+
createCommandBackend,
|
|
21
|
+
createOpencodeBackend,
|
|
22
|
+
} from "./backend.js";
|
|
23
|
+
import { linkGapAuditToReqModel } from "./gap-audit-link.js";
|
|
24
|
+
import {
|
|
25
|
+
mergeAreaReqMarkdown,
|
|
26
|
+
mergeAreaReqTxt,
|
|
27
|
+
mergeReqMarkdown,
|
|
28
|
+
mergeReqTxt,
|
|
29
|
+
} from "./merge.js";
|
|
30
|
+
import { buildNameTraceLookup } from "./name-trace.js";
|
|
31
|
+
import {
|
|
32
|
+
defaultCheckScriptPath,
|
|
33
|
+
expandAreaRenderItems,
|
|
34
|
+
expandRenderItems,
|
|
35
|
+
prepareAreaInputs,
|
|
36
|
+
prepareFeatureInputs,
|
|
37
|
+
renderAllAreas,
|
|
38
|
+
renderAllAreasUnified,
|
|
39
|
+
renderAllFeatures,
|
|
40
|
+
renderAllFeaturesUnified,
|
|
41
|
+
writeRenderList,
|
|
42
|
+
} from "./orchestrate.js";
|
|
43
|
+
// overviewQuoteCheck is kept in overview.ts for programmatic use by other modules
|
|
44
|
+
import { runPostMergeAudit } from "./post-merge-fix.js";
|
|
45
|
+
import { exportSpecHandoff } from "../spec-handoff.js";
|
|
46
|
+
import {
|
|
47
|
+
snapshotAreaReq,
|
|
48
|
+
snapshotAreaReqTxt,
|
|
49
|
+
snapshotReq,
|
|
50
|
+
snapshotReqTxt,
|
|
51
|
+
writeReqTxtFiles,
|
|
52
|
+
} from "./snapshot.js";
|
|
53
|
+
class AgentUsageError extends Error {}
|
|
54
|
+
function parseMode(value) {
|
|
55
|
+
if (value === undefined) return "subagent";
|
|
56
|
+
if (value === "subagent" || value === "shell" || value === "shell-legacy")
|
|
57
|
+
return value;
|
|
58
|
+
throw new AgentUsageError(
|
|
59
|
+
`--mode must be "subagent", "shell", or "shell-legacy", got ${value}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const agentValueOptions = new Set([
|
|
63
|
+
"input",
|
|
64
|
+
"output",
|
|
65
|
+
"resume",
|
|
66
|
+
"mode",
|
|
67
|
+
"project-root",
|
|
68
|
+
"backend",
|
|
69
|
+
"agent-name",
|
|
70
|
+
"ai-command",
|
|
71
|
+
"spec-output",
|
|
72
|
+
"concurrency",
|
|
73
|
+
"max-retries",
|
|
74
|
+
"backoff-ms",
|
|
75
|
+
]);
|
|
76
|
+
function takeValue(args, index, name, inline) {
|
|
77
|
+
const value = inline ?? args[index + 1];
|
|
78
|
+
if (
|
|
79
|
+
value === undefined ||
|
|
80
|
+
value.trim().length === 0 ||
|
|
81
|
+
(inline === undefined && value.startsWith("--"))
|
|
82
|
+
) {
|
|
83
|
+
throw new AgentUsageError(`Option requires a value: --${name}`);
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
function positiveInteger(value, name) {
|
|
88
|
+
const result = Number(value);
|
|
89
|
+
if (!Number.isSafeInteger(result) || result <= 0) {
|
|
90
|
+
throw new AgentUsageError(
|
|
91
|
+
`--${name} must be a positive integer, got ${value}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
export function parseAgentArguments(args) {
|
|
97
|
+
const normalized = args[0] === "--" ? args.slice(1) : args;
|
|
98
|
+
const values = new Map();
|
|
99
|
+
let snapshot = false;
|
|
100
|
+
let perFeature = false;
|
|
101
|
+
let withUnreachableArea = false;
|
|
102
|
+
let noAuditFix = false;
|
|
103
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
104
|
+
const token = normalized[index];
|
|
105
|
+
if (token === "--snapshot") {
|
|
106
|
+
if (snapshot)
|
|
107
|
+
throw new AgentUsageError("--snapshot may be provided once");
|
|
108
|
+
snapshot = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (token === "--per-feature") {
|
|
112
|
+
if (perFeature)
|
|
113
|
+
throw new AgentUsageError("--per-feature may be provided once");
|
|
114
|
+
perFeature = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (token === "--with-unreachable-area") {
|
|
118
|
+
if (withUnreachableArea) {
|
|
119
|
+
throw new AgentUsageError(
|
|
120
|
+
"--with-unreachable-area may be provided once",
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
withUnreachableArea = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (token === "--no-audit-fix") {
|
|
127
|
+
if (noAuditFix)
|
|
128
|
+
throw new AgentUsageError("--no-audit-fix may be provided once");
|
|
129
|
+
noAuditFix = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!token.startsWith("--")) {
|
|
133
|
+
throw new AgentUsageError(`Unexpected argument: ${token}`);
|
|
134
|
+
}
|
|
135
|
+
const separator = token.indexOf("=");
|
|
136
|
+
const name = token.slice(2, separator === -1 ? undefined : separator);
|
|
137
|
+
if (!agentValueOptions.has(name)) {
|
|
138
|
+
throw new AgentUsageError(`Unknown option: --${name}`);
|
|
139
|
+
}
|
|
140
|
+
if (values.has(name)) {
|
|
141
|
+
throw new AgentUsageError(`Option may be provided once: --${name}`);
|
|
142
|
+
}
|
|
143
|
+
const inline = separator === -1 ? undefined : token.slice(separator + 1);
|
|
144
|
+
values.set(name, takeValue(normalized, index, name, inline));
|
|
145
|
+
if (inline === undefined) index += 1;
|
|
146
|
+
}
|
|
147
|
+
const input = values.get("input");
|
|
148
|
+
if (input === undefined) {
|
|
149
|
+
throw new AgentUsageError("Missing required option: --input");
|
|
150
|
+
}
|
|
151
|
+
const inputDirectory = resolve(input);
|
|
152
|
+
const mode = parseMode(values.get("mode"));
|
|
153
|
+
const resume = values.has("resume");
|
|
154
|
+
if (resume && values.has("output")) {
|
|
155
|
+
throw new AgentUsageError("--resume cannot be combined with --output");
|
|
156
|
+
}
|
|
157
|
+
if (resume && mode !== "shell") {
|
|
158
|
+
throw new AgentUsageError("--resume requires --mode shell");
|
|
159
|
+
}
|
|
160
|
+
if (resume && snapshot) {
|
|
161
|
+
throw new AgentUsageError("--resume cannot be combined with --snapshot");
|
|
162
|
+
}
|
|
163
|
+
const outputExplicit = values.has("output");
|
|
164
|
+
return {
|
|
165
|
+
inputDirectory,
|
|
166
|
+
outputDirectory: resume
|
|
167
|
+
? resolve(values.get("resume"))
|
|
168
|
+
: outputExplicit
|
|
169
|
+
? resolve(values.get("output"))
|
|
170
|
+
: join(inputDirectory, "generation"),
|
|
171
|
+
projectRoot: values.has("project-root")
|
|
172
|
+
? resolve(values.get("project-root"))
|
|
173
|
+
: undefined,
|
|
174
|
+
mode,
|
|
175
|
+
backendName: values.get("backend") ?? "claude",
|
|
176
|
+
agentName: values.get("agent-name") ?? "reqgen-renderer",
|
|
177
|
+
aiCommand: values.get("ai-command"),
|
|
178
|
+
specOutputDirectory: values.has("spec-output")
|
|
179
|
+
? resolve(values.get("spec-output"))
|
|
180
|
+
: undefined,
|
|
181
|
+
concurrency: values.has("concurrency")
|
|
182
|
+
? positiveInteger(values.get("concurrency"), "concurrency")
|
|
183
|
+
: 8,
|
|
184
|
+
maxRetries: values.has("max-retries")
|
|
185
|
+
? positiveInteger(values.get("max-retries"), "max-retries")
|
|
186
|
+
: 5,
|
|
187
|
+
backoffMs: values.has("backoff-ms")
|
|
188
|
+
? positiveInteger(values.get("backoff-ms"), "backoff-ms")
|
|
189
|
+
: 5000,
|
|
190
|
+
snapshot,
|
|
191
|
+
perFeature,
|
|
192
|
+
withUnreachableArea,
|
|
193
|
+
noAuditFix,
|
|
194
|
+
outputExplicit,
|
|
195
|
+
resume,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function buildBackend(options) {
|
|
199
|
+
if (options.aiCommand !== undefined) {
|
|
200
|
+
return createCommandBackend(options.aiCommand);
|
|
201
|
+
}
|
|
202
|
+
if (options.backendName === "claude") {
|
|
203
|
+
return createClaudeBackend({ legacy: options.mode === "shell-legacy" });
|
|
204
|
+
}
|
|
205
|
+
if (options.backendName === "opencode") {
|
|
206
|
+
return createOpencodeBackend();
|
|
207
|
+
}
|
|
208
|
+
throw new AgentUsageError(
|
|
209
|
+
`Unknown backend: ${options.backendName} (use --ai-command for custom backends)`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
function summary(status, options, totalAreas, totalFeatures, aiRendered) {
|
|
213
|
+
return JSON.stringify(
|
|
214
|
+
{
|
|
215
|
+
status,
|
|
216
|
+
output: options.outputDirectory,
|
|
217
|
+
renderDir: join(options.outputDirectory, "rendering"),
|
|
218
|
+
totalAreas,
|
|
219
|
+
totalFeatures,
|
|
220
|
+
aiRendered,
|
|
221
|
+
fallback: totalFeatures - aiRendered,
|
|
222
|
+
},
|
|
223
|
+
null,
|
|
224
|
+
2,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
function aiRenderedCountArea(results) {
|
|
228
|
+
let aiRendered = 0;
|
|
229
|
+
for (const areaResult of results.values()) {
|
|
230
|
+
if (areaResult.status !== "ok") continue;
|
|
231
|
+
for (const featureResult of areaResult.features.values()) {
|
|
232
|
+
if (featureResult.status === "ok" && featureResult.body !== undefined) {
|
|
233
|
+
aiRendered += 1;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return aiRendered;
|
|
238
|
+
}
|
|
239
|
+
function aiRenderedCountFeature(results) {
|
|
240
|
+
let aiRendered = 0;
|
|
241
|
+
for (const result of results.values()) {
|
|
242
|
+
if (result.status === "ok" && result.body !== undefined) aiRendered += 1;
|
|
243
|
+
}
|
|
244
|
+
return aiRendered;
|
|
245
|
+
}
|
|
246
|
+
/** Recomputed via renderReqDraft (Stage 1's deterministic layer, unchanged)
|
|
247
|
+
* rather than reading generation/req.md off disk — the merge template
|
|
248
|
+
* is then always hash-verified against this run's req-model/area-graph
|
|
249
|
+
* instead of trusting a plain text file that could be stale or hand-edited.
|
|
250
|
+
* The Agent-rendered output still overwrites req.md in the end. */
|
|
251
|
+
async function loadTemplate(inputDirectory, report, fullReqModel) {
|
|
252
|
+
const graph = await loadStage2AreaGraph(inputDirectory, report, fullReqModel);
|
|
253
|
+
return renderReqDraft(fullReqModel, graph);
|
|
254
|
+
}
|
|
255
|
+
// runPostMergeAudit is in post-merge-fix.ts
|
|
256
|
+
async function runSnapshot(options, io) {
|
|
257
|
+
const { reqModel: loadedReqModel, report } = await loadStage2InputWithReport(
|
|
258
|
+
options.inputDirectory,
|
|
259
|
+
);
|
|
260
|
+
const sortedReqModel = sortAreasReachableFirst(loadedReqModel);
|
|
261
|
+
const reqModel = filterRenderableAreas(sortedReqModel);
|
|
262
|
+
const renderDir = join(options.outputDirectory, "rendering");
|
|
263
|
+
// Always produce both formats: req.md (markdown) + req-{idx}.txt (plain text).
|
|
264
|
+
const templateText = await loadTemplate(
|
|
265
|
+
options.inputDirectory,
|
|
266
|
+
report,
|
|
267
|
+
reqModel,
|
|
268
|
+
);
|
|
269
|
+
const graph = await loadStage2AreaGraph(
|
|
270
|
+
options.inputDirectory,
|
|
271
|
+
report,
|
|
272
|
+
reqModel,
|
|
273
|
+
);
|
|
274
|
+
if (options.perFeature) {
|
|
275
|
+
const mergeOptions = {
|
|
276
|
+
includeUnreachableAreas: options.withUnreachableArea,
|
|
277
|
+
};
|
|
278
|
+
const mdResult = await snapshotReq(
|
|
279
|
+
reqModel,
|
|
280
|
+
reqModel,
|
|
281
|
+
renderDir,
|
|
282
|
+
options.outputDirectory,
|
|
283
|
+
templateText,
|
|
284
|
+
mergeOptions,
|
|
285
|
+
);
|
|
286
|
+
const txtResult = await snapshotReqTxt(
|
|
287
|
+
reqModel,
|
|
288
|
+
reqModel,
|
|
289
|
+
graph,
|
|
290
|
+
renderDir,
|
|
291
|
+
options.outputDirectory,
|
|
292
|
+
mergeOptions,
|
|
293
|
+
);
|
|
294
|
+
io.stdout(
|
|
295
|
+
JSON.stringify(
|
|
296
|
+
{
|
|
297
|
+
status: "ok",
|
|
298
|
+
md: mdResult,
|
|
299
|
+
txt: txtResult,
|
|
300
|
+
},
|
|
301
|
+
null,
|
|
302
|
+
2,
|
|
303
|
+
) + "\n",
|
|
304
|
+
);
|
|
305
|
+
} else {
|
|
306
|
+
const mergeOptions = {
|
|
307
|
+
includeUnreachableAreas: options.withUnreachableArea,
|
|
308
|
+
};
|
|
309
|
+
const mdResult = await snapshotAreaReq(
|
|
310
|
+
reqModel,
|
|
311
|
+
reqModel,
|
|
312
|
+
renderDir,
|
|
313
|
+
options.outputDirectory,
|
|
314
|
+
templateText,
|
|
315
|
+
mergeOptions,
|
|
316
|
+
);
|
|
317
|
+
const txtResult = await snapshotAreaReqTxt(
|
|
318
|
+
reqModel,
|
|
319
|
+
reqModel,
|
|
320
|
+
graph,
|
|
321
|
+
renderDir,
|
|
322
|
+
options.outputDirectory,
|
|
323
|
+
mergeOptions,
|
|
324
|
+
);
|
|
325
|
+
io.stdout(
|
|
326
|
+
JSON.stringify(
|
|
327
|
+
{
|
|
328
|
+
status: "ok",
|
|
329
|
+
md: mdResult,
|
|
330
|
+
txt: txtResult,
|
|
331
|
+
},
|
|
332
|
+
null,
|
|
333
|
+
2,
|
|
334
|
+
) + "\n",
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
await runRequestedSpecHandoff(options, io);
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
/** Export only when the caller explicitly asks for it. Keeping this opt-in
|
|
341
|
+
* preserves the existing Stage 2 output layout while allowing a single
|
|
342
|
+
* Stage 2 invocation to hand its result directly to `$a2h-spec-generate`. */
|
|
343
|
+
async function runRequestedSpecHandoff(options, io) {
|
|
344
|
+
if (options.specOutputDirectory === undefined) return;
|
|
345
|
+
const result = await exportSpecHandoff({
|
|
346
|
+
inputDirectory: options.inputDirectory,
|
|
347
|
+
stage2Directory: options.outputDirectory,
|
|
348
|
+
outputDirectory: options.specOutputDirectory,
|
|
349
|
+
mode: options.perFeature ? "feature" : "area",
|
|
350
|
+
includeUnreachableAreas: options.withUnreachableArea,
|
|
351
|
+
// The Stage 2 output directory is an explicitly selected generated path;
|
|
352
|
+
// refreshing its handoff is safe and avoids stale page batches on resume.
|
|
353
|
+
replace: true,
|
|
354
|
+
});
|
|
355
|
+
io.stdout(
|
|
356
|
+
`${JSON.stringify(
|
|
357
|
+
{
|
|
358
|
+
status: "spec-handoff",
|
|
359
|
+
outputDirectory: result.outputDirectory,
|
|
360
|
+
manifestPath: result.manifestPath,
|
|
361
|
+
batches: result.manifest.batches.length,
|
|
362
|
+
requirements: result.manifest.batches.reduce(
|
|
363
|
+
(sum, batch) => sum + batch.requirementCount,
|
|
364
|
+
0,
|
|
365
|
+
),
|
|
366
|
+
warnings: result.manifest.warnings,
|
|
367
|
+
},
|
|
368
|
+
null,
|
|
369
|
+
2,
|
|
370
|
+
)}\n`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
async function loadGapAuditLinks(inputDirectory, report, fullReqModel) {
|
|
374
|
+
const gapAudit = await loadStage2GapAudit(
|
|
375
|
+
inputDirectory,
|
|
376
|
+
report,
|
|
377
|
+
fullReqModel,
|
|
378
|
+
);
|
|
379
|
+
return linkGapAuditToReqModel(fullReqModel, gapAudit);
|
|
380
|
+
}
|
|
381
|
+
/** Absent when the Stage 1 run predates source-facts.json/area-graph.json;
|
|
382
|
+
* the Agent then degrades to blind exploration for everyone. */
|
|
383
|
+
async function loadNameTrace(inputDirectory, reqModel) {
|
|
384
|
+
const input = await loadStage2NameTraceabilityInput(inputDirectory, reqModel);
|
|
385
|
+
if (input === undefined) return undefined;
|
|
386
|
+
return buildNameTraceLookup(buildNameTraceEntries({ ...input, reqModel }));
|
|
387
|
+
}
|
|
388
|
+
/** `reqModel` (post-filterRenderableAreas) decides what gets sent to the
|
|
389
|
+
* Agent; `fullReqModel` must be the same, unfiltered req-model templateText
|
|
390
|
+
* was rendered from, so the merge walk's Area/Feature positions line up with
|
|
391
|
+
* the template text — filtering some Areas out of rendering must not desync
|
|
392
|
+
* it from the template it is walking. */
|
|
393
|
+
async function runRenderPerFeature(
|
|
394
|
+
reqModel,
|
|
395
|
+
fullReqModel,
|
|
396
|
+
options,
|
|
397
|
+
io,
|
|
398
|
+
templateText,
|
|
399
|
+
graph,
|
|
400
|
+
nameTrace,
|
|
401
|
+
gapAuditLinks,
|
|
402
|
+
backend,
|
|
403
|
+
renderDir,
|
|
404
|
+
analysisMode,
|
|
405
|
+
unified,
|
|
406
|
+
) {
|
|
407
|
+
const items = expandRenderItems(reqModel);
|
|
408
|
+
const totalAreas = reqModel.areas.length;
|
|
409
|
+
const totalFeatures = items.length;
|
|
410
|
+
const results = new Map();
|
|
411
|
+
let interrupted = false;
|
|
412
|
+
process.once("SIGINT", () => {
|
|
413
|
+
interrupted = true;
|
|
414
|
+
io.stdout(
|
|
415
|
+
summary(
|
|
416
|
+
"interrupted",
|
|
417
|
+
options,
|
|
418
|
+
totalAreas,
|
|
419
|
+
totalFeatures,
|
|
420
|
+
aiRenderedCountFeature(results),
|
|
421
|
+
) + "\n",
|
|
422
|
+
);
|
|
423
|
+
process.exit(0);
|
|
424
|
+
});
|
|
425
|
+
const agentOpts = {
|
|
426
|
+
backend,
|
|
427
|
+
projectRoot: options.projectRoot,
|
|
428
|
+
inputDirectory: options.inputDirectory,
|
|
429
|
+
outputDirectory: options.outputDirectory,
|
|
430
|
+
analysisMode,
|
|
431
|
+
launchMode: options.mode,
|
|
432
|
+
renderDir,
|
|
433
|
+
agentName: options.agentName,
|
|
434
|
+
concurrency: options.concurrency,
|
|
435
|
+
maxRetries: options.maxRetries,
|
|
436
|
+
backoffMs: options.backoffMs,
|
|
437
|
+
nameTrace,
|
|
438
|
+
gapAuditLinks,
|
|
439
|
+
noAuditFix: options.noAuditFix,
|
|
440
|
+
resume: options.resume,
|
|
441
|
+
};
|
|
442
|
+
const rendered = unified
|
|
443
|
+
? await renderAllFeaturesUnified(items, reqModel, agentOpts)
|
|
444
|
+
: await renderAllFeatures(items, reqModel, agentOpts);
|
|
445
|
+
for (const [id, result] of rendered) results.set(id, result);
|
|
446
|
+
if (!interrupted) {
|
|
447
|
+
// Always write both formats: req.md (markdown) + req-{idx}.txt (plain text).
|
|
448
|
+
const mergeOptions = {
|
|
449
|
+
includeUnreachableAreas: options.withUnreachableArea,
|
|
450
|
+
};
|
|
451
|
+
const mdText = mergeReqMarkdown(
|
|
452
|
+
fullReqModel,
|
|
453
|
+
reqModel,
|
|
454
|
+
results,
|
|
455
|
+
templateText,
|
|
456
|
+
mergeOptions,
|
|
457
|
+
);
|
|
458
|
+
const txtAreas = mergeReqTxt(
|
|
459
|
+
fullReqModel,
|
|
460
|
+
reqModel,
|
|
461
|
+
results,
|
|
462
|
+
graph,
|
|
463
|
+
mergeOptions,
|
|
464
|
+
);
|
|
465
|
+
await writeFile(join(options.outputDirectory, "req.md"), mdText, "utf8");
|
|
466
|
+
await writeReqTxtFiles(options.outputDirectory, txtAreas);
|
|
467
|
+
// Post-merge audit + optional fix agent (shell / shell-legacy modes)
|
|
468
|
+
if (!options.noAuditFix) {
|
|
469
|
+
await runPostMergeAudit(
|
|
470
|
+
join(options.outputDirectory, "req.md"),
|
|
471
|
+
options.inputDirectory,
|
|
472
|
+
options.outputDirectory,
|
|
473
|
+
options.mode,
|
|
474
|
+
io,
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
await runRequestedSpecHandoff(options, io);
|
|
478
|
+
io.stdout(
|
|
479
|
+
summary(
|
|
480
|
+
"ok",
|
|
481
|
+
options,
|
|
482
|
+
totalAreas,
|
|
483
|
+
totalFeatures,
|
|
484
|
+
aiRenderedCountFeature(results),
|
|
485
|
+
) + "\n",
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/** See runRenderPerFeature for why both a renderable and a full req-model
|
|
490
|
+
* are needed. */
|
|
491
|
+
async function runRenderPerArea(
|
|
492
|
+
reqModel,
|
|
493
|
+
fullReqModel,
|
|
494
|
+
options,
|
|
495
|
+
io,
|
|
496
|
+
templateText,
|
|
497
|
+
graph,
|
|
498
|
+
nameTrace,
|
|
499
|
+
gapAuditLinks,
|
|
500
|
+
backend,
|
|
501
|
+
renderDir,
|
|
502
|
+
analysisMode,
|
|
503
|
+
unified,
|
|
504
|
+
) {
|
|
505
|
+
const items = expandAreaRenderItems(reqModel);
|
|
506
|
+
const totalAreas = reqModel.areas.length;
|
|
507
|
+
const totalFeatures = items.reduce(
|
|
508
|
+
(sum, item) => sum + item.area.features.length,
|
|
509
|
+
0,
|
|
510
|
+
);
|
|
511
|
+
const results = new Map();
|
|
512
|
+
let interrupted = false;
|
|
513
|
+
process.once("SIGINT", () => {
|
|
514
|
+
interrupted = true;
|
|
515
|
+
io.stdout(
|
|
516
|
+
summary(
|
|
517
|
+
"interrupted",
|
|
518
|
+
options,
|
|
519
|
+
totalAreas,
|
|
520
|
+
totalFeatures,
|
|
521
|
+
aiRenderedCountArea(results),
|
|
522
|
+
) + "\n",
|
|
523
|
+
);
|
|
524
|
+
process.exit(0);
|
|
525
|
+
});
|
|
526
|
+
const agentOpts = {
|
|
527
|
+
backend,
|
|
528
|
+
projectRoot: options.projectRoot,
|
|
529
|
+
inputDirectory: options.inputDirectory,
|
|
530
|
+
outputDirectory: options.outputDirectory,
|
|
531
|
+
analysisMode,
|
|
532
|
+
launchMode: options.mode,
|
|
533
|
+
renderDir,
|
|
534
|
+
agentName: options.agentName,
|
|
535
|
+
concurrency: options.concurrency,
|
|
536
|
+
maxRetries: options.maxRetries,
|
|
537
|
+
backoffMs: options.backoffMs,
|
|
538
|
+
nameTrace,
|
|
539
|
+
gapAuditLinks,
|
|
540
|
+
noAuditFix: options.noAuditFix,
|
|
541
|
+
resume: options.resume,
|
|
542
|
+
};
|
|
543
|
+
const rendered = unified
|
|
544
|
+
? await renderAllAreasUnified(items, reqModel, agentOpts)
|
|
545
|
+
: await renderAllAreas(items, reqModel, agentOpts);
|
|
546
|
+
for (const [id, result] of rendered) results.set(id, result);
|
|
547
|
+
if (!interrupted) {
|
|
548
|
+
// Always write both formats: req.md (markdown) + req-{idx}.txt (plain text).
|
|
549
|
+
const mergeOptions = {
|
|
550
|
+
includeUnreachableAreas: options.withUnreachableArea,
|
|
551
|
+
};
|
|
552
|
+
await writeFile(
|
|
553
|
+
join(options.outputDirectory, "req.md"),
|
|
554
|
+
mergeAreaReqMarkdown(
|
|
555
|
+
fullReqModel,
|
|
556
|
+
reqModel,
|
|
557
|
+
results,
|
|
558
|
+
templateText,
|
|
559
|
+
mergeOptions,
|
|
560
|
+
),
|
|
561
|
+
"utf8",
|
|
562
|
+
);
|
|
563
|
+
const txtAreas = mergeAreaReqTxt(
|
|
564
|
+
fullReqModel,
|
|
565
|
+
reqModel,
|
|
566
|
+
results,
|
|
567
|
+
graph,
|
|
568
|
+
mergeOptions,
|
|
569
|
+
);
|
|
570
|
+
await writeReqTxtFiles(options.outputDirectory, txtAreas);
|
|
571
|
+
// Post-merge audit + optional fix agent (shell / shell-legacy modes)
|
|
572
|
+
if (!options.noAuditFix) {
|
|
573
|
+
await runPostMergeAudit(
|
|
574
|
+
join(options.outputDirectory, "req.md"),
|
|
575
|
+
options.inputDirectory,
|
|
576
|
+
options.outputDirectory,
|
|
577
|
+
options.mode,
|
|
578
|
+
io,
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
await runRequestedSpecHandoff(options, io);
|
|
582
|
+
io.stdout(
|
|
583
|
+
summary(
|
|
584
|
+
"ok",
|
|
585
|
+
options,
|
|
586
|
+
totalAreas,
|
|
587
|
+
totalFeatures,
|
|
588
|
+
aiRenderedCountArea(results),
|
|
589
|
+
) + "\n",
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function prepareSummary(
|
|
594
|
+
options,
|
|
595
|
+
checkScriptPath,
|
|
596
|
+
totalAreas,
|
|
597
|
+
entries,
|
|
598
|
+
renderList,
|
|
599
|
+
) {
|
|
600
|
+
return JSON.stringify(
|
|
601
|
+
{
|
|
602
|
+
status: "ok",
|
|
603
|
+
mode: "subagent",
|
|
604
|
+
output: options.outputDirectory,
|
|
605
|
+
renderDir: join(options.outputDirectory, "rendering"),
|
|
606
|
+
checkScript: checkScriptPath,
|
|
607
|
+
renderList: renderList.listPath,
|
|
608
|
+
renderManifest: renderList.manifestPath,
|
|
609
|
+
totalAreas,
|
|
610
|
+
total: entries.length,
|
|
611
|
+
done: renderList.done,
|
|
612
|
+
pending: renderList.pending,
|
|
613
|
+
entries,
|
|
614
|
+
},
|
|
615
|
+
null,
|
|
616
|
+
2,
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
/** ADR-0005: writes task.md + expected.json for every renderable unit, plus
|
|
620
|
+
* render-list.md/render-manifest.json (see writeRenderList) — never spawns a
|
|
621
|
+
* backend. Turning each manifest entry into an output.json is the job of a
|
|
622
|
+
* subagent, dispatched by the main Agent running a2h-app-req-gen (see
|
|
623
|
+
* references/subagent-render.md), not this CLI process. Idempotent and safe
|
|
624
|
+
* to rerun anytime, including to resume an interrupted batch: it never
|
|
625
|
+
* touches draft.json/output.json, and always recomputes render-list.md from
|
|
626
|
+
* whichever output.json files already exist on disk. */
|
|
627
|
+
async function runPrepare(options, io) {
|
|
628
|
+
if (options.specOutputDirectory !== undefined) {
|
|
629
|
+
throw new AgentUsageError(
|
|
630
|
+
"--spec-output requires finalized Stage 2 output; omit it during subagent preparation " +
|
|
631
|
+
"and pass it to the later --snapshot invocation",
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
if (options.projectRoot === undefined) {
|
|
635
|
+
throw new AgentUsageError(
|
|
636
|
+
"--mode subagent requires --project-root <android-project> " +
|
|
637
|
+
"(the subagent's read-only exploration boundary)",
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
const { reqModel: loadedReqModel, report } = await loadStage2InputWithReport(
|
|
641
|
+
options.inputDirectory,
|
|
642
|
+
);
|
|
643
|
+
const sortedReqModel = sortAreasReachableFirst(loadedReqModel);
|
|
644
|
+
const reqModel = filterRenderableAreas(sortedReqModel);
|
|
645
|
+
const nameTrace = await loadNameTrace(options.inputDirectory, reqModel);
|
|
646
|
+
const gapAuditLinks = await loadGapAuditLinks(
|
|
647
|
+
options.inputDirectory,
|
|
648
|
+
report,
|
|
649
|
+
sortedReqModel,
|
|
650
|
+
);
|
|
651
|
+
const renderDir = join(options.outputDirectory, "rendering");
|
|
652
|
+
const analysisMode = report.semanticCompleteness ?? "unknown";
|
|
653
|
+
const prepareOpts = {
|
|
654
|
+
projectRoot: options.projectRoot,
|
|
655
|
+
analysisMode,
|
|
656
|
+
renderDir,
|
|
657
|
+
gapAuditLinks,
|
|
658
|
+
nameTrace,
|
|
659
|
+
};
|
|
660
|
+
const entries = options.perFeature
|
|
661
|
+
? await prepareFeatureInputs(
|
|
662
|
+
expandRenderItems(reqModel),
|
|
663
|
+
reqModel,
|
|
664
|
+
prepareOpts,
|
|
665
|
+
)
|
|
666
|
+
: await prepareAreaInputs(
|
|
667
|
+
expandAreaRenderItems(reqModel),
|
|
668
|
+
reqModel,
|
|
669
|
+
prepareOpts,
|
|
670
|
+
);
|
|
671
|
+
const checkScriptPath = defaultCheckScriptPath();
|
|
672
|
+
const renderList = await writeRenderList(
|
|
673
|
+
options.outputDirectory,
|
|
674
|
+
checkScriptPath,
|
|
675
|
+
entries,
|
|
676
|
+
);
|
|
677
|
+
io.stdout(
|
|
678
|
+
prepareSummary(
|
|
679
|
+
options,
|
|
680
|
+
checkScriptPath,
|
|
681
|
+
reqModel.areas.length,
|
|
682
|
+
entries,
|
|
683
|
+
renderList,
|
|
684
|
+
) + "\n",
|
|
685
|
+
);
|
|
686
|
+
return 0;
|
|
687
|
+
}
|
|
688
|
+
async function runRender(options, io) {
|
|
689
|
+
if (options.projectRoot === undefined) {
|
|
690
|
+
throw new AgentUsageError(
|
|
691
|
+
"--mode shell requires --project-root <android-project> " +
|
|
692
|
+
"(the Agent read-only exploration boundary)",
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
const { reqModel: loadedReqModel, report } = await loadStage2InputWithReport(
|
|
696
|
+
options.inputDirectory,
|
|
697
|
+
);
|
|
698
|
+
const sortedReqModel = sortAreasReachableFirst(loadedReqModel);
|
|
699
|
+
const reqModel = filterRenderableAreas(sortedReqModel);
|
|
700
|
+
const nameTrace = await loadNameTrace(options.inputDirectory, reqModel);
|
|
701
|
+
const gapAuditLinks = await loadGapAuditLinks(
|
|
702
|
+
options.inputDirectory,
|
|
703
|
+
report,
|
|
704
|
+
sortedReqModel,
|
|
705
|
+
);
|
|
706
|
+
const templateText = await loadTemplate(
|
|
707
|
+
options.inputDirectory,
|
|
708
|
+
report,
|
|
709
|
+
sortedReqModel,
|
|
710
|
+
);
|
|
711
|
+
// Load graph for both md-out (via loadTemplate above) and txt modes.
|
|
712
|
+
const graph = await loadStage2AreaGraph(
|
|
713
|
+
options.inputDirectory,
|
|
714
|
+
report,
|
|
715
|
+
sortedReqModel,
|
|
716
|
+
);
|
|
717
|
+
const backend = buildBackend(options);
|
|
718
|
+
const renderDir = join(options.outputDirectory, "rendering");
|
|
719
|
+
await mkdir(renderDir, { recursive: true });
|
|
720
|
+
const analysisMode = report.semanticCompleteness ?? "unknown";
|
|
721
|
+
// The unified task-file protocol is implemented by the Claude backend in
|
|
722
|
+
// ordinary shell mode. Custom commands and opencode still receive the
|
|
723
|
+
// inline prompt; passing them a task path silently produced invalid JSON in
|
|
724
|
+
// the old routing rule.
|
|
725
|
+
const unified =
|
|
726
|
+
options.mode === "shell" &&
|
|
727
|
+
options.aiCommand === undefined &&
|
|
728
|
+
options.backendName === "claude";
|
|
729
|
+
if (options.perFeature) {
|
|
730
|
+
await runRenderPerFeature(
|
|
731
|
+
reqModel,
|
|
732
|
+
sortedReqModel,
|
|
733
|
+
options,
|
|
734
|
+
io,
|
|
735
|
+
templateText,
|
|
736
|
+
graph,
|
|
737
|
+
nameTrace,
|
|
738
|
+
gapAuditLinks,
|
|
739
|
+
backend,
|
|
740
|
+
renderDir,
|
|
741
|
+
analysisMode,
|
|
742
|
+
unified,
|
|
743
|
+
);
|
|
744
|
+
} else {
|
|
745
|
+
await runRenderPerArea(
|
|
746
|
+
reqModel,
|
|
747
|
+
sortedReqModel,
|
|
748
|
+
options,
|
|
749
|
+
io,
|
|
750
|
+
templateText,
|
|
751
|
+
graph,
|
|
752
|
+
nameTrace,
|
|
753
|
+
gapAuditLinks,
|
|
754
|
+
backend,
|
|
755
|
+
renderDir,
|
|
756
|
+
analysisMode,
|
|
757
|
+
unified,
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return 0;
|
|
761
|
+
}
|
|
762
|
+
export async function runAgentStage2(argv, io) {
|
|
763
|
+
let options;
|
|
764
|
+
try {
|
|
765
|
+
options = parseAgentArguments(argv);
|
|
766
|
+
} catch (error) {
|
|
767
|
+
io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
768
|
+
return 2;
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
if (options.resume) {
|
|
772
|
+
let isDirectory = false;
|
|
773
|
+
try {
|
|
774
|
+
isDirectory = (await stat(options.outputDirectory)).isDirectory();
|
|
775
|
+
} catch {
|
|
776
|
+
// Report a usage error below rather than silently starting a new run.
|
|
777
|
+
}
|
|
778
|
+
if (!isDirectory) {
|
|
779
|
+
io.stderr(
|
|
780
|
+
`--resume directory does not exist: ${options.outputDirectory}\n`,
|
|
781
|
+
);
|
|
782
|
+
return 2;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// Guard: refuse to overwrite an existing output directory when the user
|
|
786
|
+
// explicitly set --output. Auto-generated default paths and --snapshot
|
|
787
|
+
// mode (which must read existing output.json) are exempt.
|
|
788
|
+
if (options.outputExplicit && !options.snapshot) {
|
|
789
|
+
try {
|
|
790
|
+
await ensureOutputDirectoryEmpty(options.outputDirectory);
|
|
791
|
+
} catch (error) {
|
|
792
|
+
if (error instanceof OutputDirectoryNotEmptyError) {
|
|
793
|
+
io.stderr(error.message + "\n");
|
|
794
|
+
return 2;
|
|
795
|
+
}
|
|
796
|
+
throw error;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (options.snapshot) return await runSnapshot(options, io);
|
|
800
|
+
return options.mode === "shell" || options.mode === "shell-legacy"
|
|
801
|
+
? await runRender(options, io)
|
|
802
|
+
: await runPrepare(options, io);
|
|
803
|
+
} catch (error) {
|
|
804
|
+
const details =
|
|
805
|
+
error instanceof GenerationError ? (error.details ?? []) : [];
|
|
806
|
+
io.stderr(
|
|
807
|
+
`Stage 2 (agent) failed: ${error instanceof Error ? error.message : String(error)}\n${details.map((detail) => `${detail}\n`).join("")}`,
|
|
808
|
+
);
|
|
809
|
+
return 1;
|
|
810
|
+
}
|
|
811
|
+
}
|