taphound 0.2.0-dev.1 → 0.2.0-dev.12
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/README.md +49 -149
- package/README.zh-CN.md +47 -147
- package/assets/skills/taphound-case-suite/SKILL.md +363 -0
- package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
- package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
- package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
- package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
- package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
- package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
- package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
- package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
- package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
- package/assets/skills/taphound-flash/SKILL.md +115 -0
- package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
- package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
- package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
- package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
- package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
- package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
- package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
- package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
- package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
- package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
- package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
- package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
- package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
- package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
- package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
- package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
- package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
- package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
- package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
- package/assets/skills/taphound-verify-change/SKILL.md +91 -0
- package/assets/skills/taphound-verify-change/references/accept.md +38 -0
- package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
- package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
- package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
- package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
- package/dist/adapters/adb/adb-adapter.d.ts +16 -2
- package/dist/adapters/adb/adb-adapter.js +199 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
- package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
- package/dist/adapters/adb/ui-automator-parser.js +160 -0
- package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
- package/dist/adapters/adb/window-topology-parser.js +101 -0
- package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
- package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
- package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
- package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
- package/dist/adapters/android-cli/layout-parser.js +12 -8
- package/dist/adapters/appium/appium-doctor.d.ts +7 -0
- package/dist/adapters/appium/appium-doctor.js +128 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
- package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
- package/dist/adapters/camera/camera-probe-adapter.js +286 -0
- package/dist/adapters/filesystem/artifact-store.js +40 -1
- package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
- package/dist/adapters/filesystem/context-document-writer.js +112 -0
- package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
- package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
- package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
- package/dist/adapters/filesystem/external-flow-registry.js +325 -0
- package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
- package/dist/adapters/filesystem/generation-session-store.js +281 -855
- package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
- package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
- package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
- package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
- package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
- package/dist/adapters/filesystem/journey-composition-store.js +151 -0
- package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
- package/dist/adapters/filesystem/knowledge-registry.js +223 -0
- package/dist/adapters/filesystem/project-bound-file.js +3 -5
- package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
- package/dist/adapters/filesystem/project-file-inspector.js +57 -28
- package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
- package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
- package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
- package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
- package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
- package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
- package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
- package/dist/adapters/filesystem/skill-installer.js +40 -11
- package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
- package/dist/adapters/filesystem/workspace-layout.js +57 -0
- package/dist/adapters/git/node-git-diff.d.ts +17 -0
- package/dist/adapters/git/node-git-diff.js +114 -0
- package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
- package/dist/adapters/process/node-detached-process-launcher.js +96 -0
- package/dist/adapters/process/node-process-runner.js +11 -2
- package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
- package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
- package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
- package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
- package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
- package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
- package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
- package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
- package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
- package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
- package/dist/adapters/runtime/session-adb-view.js +193 -0
- package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
- package/dist/adapters/runtime/session-backed-ports.js +43 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
- package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
- package/dist/adapters/ui/device-layout-temp.js +18 -0
- package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
- package/dist/adapters/ui/device-ui-environment.js +81 -0
- package/dist/adapters/ui/layout-normalization.d.ts +9 -0
- package/dist/adapters/ui/layout-normalization.js +44 -0
- package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
- package/dist/adapters/ui/ui-snapshot-error.js +12 -0
- package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
- package/dist/adapters/ui/ui-snapshot-support.js +12 -0
- package/dist/application/align/align-service.d.ts +42 -0
- package/dist/application/align/align-service.js +106 -0
- package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
- package/dist/application/assertion/expectation-evaluator.js +156 -18
- package/dist/application/assertion/guarded-expectation.d.ts +45 -0
- package/dist/application/assertion/guarded-expectation.js +74 -0
- package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
- package/dist/application/checkpoint/baseline-capturer.js +163 -0
- package/dist/application/checkpoint/baseline-error.d.ts +5 -0
- package/dist/application/checkpoint/baseline-error.js +8 -0
- package/dist/application/checkpoint/baseline-service.d.ts +31 -0
- package/dist/application/checkpoint/baseline-service.js +147 -0
- package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
- package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
- package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
- package/dist/application/checkpoint/regression-comparator.js +192 -0
- package/dist/application/collector/logcat-collector.d.ts +22 -1
- package/dist/application/collector/logcat-collector.js +103 -15
- package/dist/application/collector/logcat-event.d.ts +8 -0
- package/dist/application/collector/logcat-event.js +79 -0
- package/dist/application/collector/logcat-stop.d.ts +2 -0
- package/dist/application/collector/logcat-stop.js +11 -0
- package/dist/application/context/context-generator.d.ts +45 -0
- package/dist/application/context/context-generator.js +228 -0
- package/dist/application/context/context-loader.d.ts +40 -0
- package/dist/application/context/context-loader.js +223 -0
- package/dist/application/context/context-refresher.d.ts +59 -0
- package/dist/application/context/context-refresher.js +349 -0
- package/dist/application/context/context-rehasher.d.ts +36 -0
- package/dist/application/context/context-rehasher.js +110 -0
- package/dist/application/context/context-validator.d.ts +23 -1
- package/dist/application/context/context-validator.js +116 -9
- package/dist/application/context/evidence-hash.d.ts +1 -0
- package/dist/application/context/evidence-hash.js +125 -0
- package/dist/application/context/evidence-match.d.ts +9 -0
- package/dist/application/context/evidence-match.js +8 -0
- package/dist/application/context/shard-identity.d.ts +2 -0
- package/dist/application/context/shard-identity.js +7 -0
- package/dist/application/contract/contract-loader.d.ts +25 -0
- package/dist/application/contract/contract-loader.js +68 -0
- package/dist/application/contract/contract-review.d.ts +25 -0
- package/dist/application/contract/contract-review.js +51 -0
- package/dist/application/contract/contract-verifier.d.ts +53 -0
- package/dist/application/contract/contract-verifier.js +645 -0
- package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
- package/dist/application/devices/resolve-device-assignments.js +60 -0
- package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
- package/dist/application/diagnosis/failure-classifier.js +179 -0
- package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
- package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
- package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
- package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
- package/dist/application/doctor/doctor-service.d.ts +36 -3
- package/dist/application/doctor/doctor-service.js +255 -25
- package/dist/application/generation/generation-app-preparer.d.ts +20 -0
- package/dist/application/generation/generation-app-preparer.js +28 -0
- package/dist/application/generation/generation-config-service.d.ts +21 -0
- package/dist/application/generation/generation-config-service.js +53 -0
- package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
- package/dist/application/generation/generation-confirmation-service.js +109 -24
- package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
- package/dist/application/generation/generation-context-snapshot.js +29 -0
- package/dist/application/generation/generation-finalizer.d.ts +15 -9
- package/dist/application/generation/generation-finalizer.js +167 -70
- package/dist/application/generation/generation-publisher.js +12 -8
- package/dist/application/generation/generation-recovery-service.d.ts +35 -0
- package/dist/application/generation/generation-recovery-service.js +144 -0
- package/dist/application/generation/generation-reopen-service.d.ts +15 -0
- package/dist/application/generation/generation-reopen-service.js +37 -0
- package/dist/application/generation/generation-replace-service.d.ts +32 -0
- package/dist/application/generation/generation-replace-service.js +116 -0
- package/dist/application/generation/generation-starter.d.ts +44 -4
- package/dist/application/generation/generation-starter.js +175 -8
- package/dist/application/generation/generation-step-executor.d.ts +67 -5
- package/dist/application/generation/generation-step-executor.js +471 -108
- package/dist/application/generation/proposed-step-validator.js +96 -30
- package/dist/application/generation/replay-policy-loader.d.ts +16 -0
- package/dist/application/generation/replay-policy-loader.js +29 -0
- package/dist/application/generation/risk-evaluator.d.ts +4 -1
- package/dist/application/generation/risk-evaluator.js +118 -3
- package/dist/application/generation/runtime-observer.d.ts +39 -7
- package/dist/application/generation/runtime-observer.js +193 -24
- package/dist/application/impact/impact-resolver.d.ts +32 -0
- package/dist/application/impact/impact-resolver.js +160 -0
- package/dist/application/init/init-service.js +26 -13
- package/dist/application/interaction/action-executor.d.ts +3 -2
- package/dist/application/interaction/action-executor.js +11 -1
- package/dist/application/interaction/action-target.d.ts +17 -0
- package/dist/application/interaction/action-target.js +40 -0
- package/dist/application/interaction/external-step-runner.d.ts +153 -0
- package/dist/application/interaction/external-step-runner.js +332 -0
- package/dist/application/interaction/fallback-resolver.d.ts +6 -3
- package/dist/application/interaction/fallback-resolver.js +7 -5
- package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
- package/dist/application/interaction/scroll-to-executor.js +118 -36
- package/dist/application/journey/external-flow-resolver.d.ts +25 -0
- package/dist/application/journey/external-flow-resolver.js +22 -0
- package/dist/application/journey/journey-check-service.d.ts +54 -0
- package/dist/application/journey/journey-check-service.js +207 -0
- package/dist/application/journey/journey-promoter.d.ts +26 -0
- package/dist/application/journey/journey-promoter.js +123 -0
- package/dist/application/journey/journey-resolver.d.ts +42 -0
- package/dist/application/journey/journey-resolver.js +239 -0
- package/dist/application/journey/journey-retirer.d.ts +25 -0
- package/dist/application/journey/journey-retirer.js +87 -0
- package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
- package/dist/application/knowledge/anchor-resolver.js +110 -0
- package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
- package/dist/application/knowledge/knowledge-loader.js +36 -0
- package/dist/application/locator/layout-failure-summary.d.ts +2 -0
- package/dist/application/locator/layout-failure-summary.js +67 -0
- package/dist/application/locator/layout-traversal.d.ts +11 -0
- package/dist/application/locator/layout-traversal.js +18 -0
- package/dist/application/locator/locator-resolver.d.ts +13 -1
- package/dist/application/locator/locator-resolver.js +182 -18
- package/dist/application/observe/observe-service.d.ts +20 -0
- package/dist/application/observe/observe-service.js +91 -0
- package/dist/application/project/project-describer.d.ts +14 -6
- package/dist/application/project/project-describer.js +38 -5
- package/dist/application/recognition/screen-detector.d.ts +28 -0
- package/dist/application/recognition/screen-detector.js +111 -0
- package/dist/application/recorder/locator-selector.js +151 -17
- package/dist/application/recorder/recorder-service.d.ts +16 -4
- package/dist/application/recorder/recorder-service.js +420 -128
- package/dist/application/report/report-writer.d.ts +5 -0
- package/dist/application/report/report-writer.js +17 -1
- package/dist/application/runtime/cold-launch.d.ts +28 -0
- package/dist/application/runtime/cold-launch.js +59 -0
- package/dist/application/runtime/step-runner.d.ts +49 -4
- package/dist/application/runtime/step-runner.js +721 -130
- package/dist/application/runtime/verify-runtime.d.ts +57 -7
- package/dist/application/runtime/verify-runtime.js +577 -175
- package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
- package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
- package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
- package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
- package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
- package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
- package/dist/application/ui/ui-stability-probe.d.ts +9 -0
- package/dist/application/ui/ui-stability-probe.js +24 -0
- package/dist/application/wait/idle-advice.d.ts +6 -0
- package/dist/application/wait/idle-advice.js +27 -0
- package/dist/application/wait/idle-profiles.d.ts +15 -0
- package/dist/application/wait/idle-profiles.js +75 -0
- package/dist/application/wait/idle-waiter.d.ts +28 -4
- package/dist/application/wait/idle-waiter.js +201 -15
- package/dist/cli/commands/align.d.ts +3 -0
- package/dist/cli/commands/align.js +124 -0
- package/dist/cli/commands/baseline.d.ts +3 -0
- package/dist/cli/commands/baseline.js +130 -0
- package/dist/cli/commands/context.js +300 -27
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.js +120 -0
- package/dist/cli/commands/diagnose.d.ts +3 -0
- package/dist/cli/commands/diagnose.js +84 -0
- package/dist/cli/commands/doctor.js +16 -9
- package/dist/cli/commands/failure.d.ts +3 -0
- package/dist/cli/commands/failure.js +63 -0
- package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
- package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
- package/dist/cli/commands/generation/session-commands.d.ts +11 -0
- package/dist/cli/commands/generation/session-commands.js +472 -0
- package/dist/cli/commands/generation/shared.d.ts +1414 -0
- package/dist/cli/commands/generation/shared.js +291 -0
- package/dist/cli/commands/generation/step-commands.d.ts +8 -0
- package/dist/cli/commands/generation/step-commands.js +315 -0
- package/dist/cli/commands/generation.js +10 -507
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.js +87 -0
- package/dist/cli/commands/init.js +13 -6
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.js +382 -0
- package/dist/cli/commands/knowledge.d.ts +3 -0
- package/dist/cli/commands/knowledge.js +93 -0
- package/dist/cli/commands/observe.d.ts +3 -0
- package/dist/cli/commands/observe.js +127 -0
- package/dist/cli/commands/project.js +2 -1
- package/dist/cli/commands/record.js +18 -5
- package/dist/cli/commands/verify.js +326 -64
- package/dist/cli/dependencies.d.ts +136 -1
- package/dist/cli/dependencies.js +504 -52
- package/dist/cli/diagnostics-recorder.d.ts +31 -0
- package/dist/cli/diagnostics-recorder.js +82 -0
- package/dist/cli/diff-verification.d.ts +41 -0
- package/dist/cli/diff-verification.js +192 -0
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.js +107 -2
- package/dist/cli/output.d.ts +5 -3
- package/dist/cli/output.js +12 -0
- package/dist/cli/program.js +21 -1
- package/dist/cli/project-root.d.ts +1 -0
- package/dist/cli/project-root.js +6 -0
- package/dist/cli/runtime-selection.d.ts +22 -0
- package/dist/cli/runtime-selection.js +92 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/workspace-guard.d.ts +2 -0
- package/dist/cli/workspace-guard.js +3 -0
- package/dist/domain/binding-reference.d.ts +4 -0
- package/dist/domain/binding-reference.js +23 -0
- package/dist/domain/checkpoint.d.ts +404 -0
- package/dist/domain/checkpoint.js +297 -0
- package/dist/domain/config.d.ts +107 -1
- package/dist/domain/config.js +52 -6
- package/dist/domain/contract.d.ts +413 -0
- package/dist/domain/contract.js +196 -0
- package/dist/domain/diagnostics.d.ts +1396 -0
- package/dist/domain/diagnostics.js +193 -0
- package/dist/domain/external-flow.d.ts +493 -0
- package/dist/domain/external-flow.js +15 -0
- package/dist/domain/failure-classification.d.ts +176 -0
- package/dist/domain/failure-classification.js +169 -0
- package/dist/domain/failure.d.ts +2 -1
- package/dist/domain/failure.js +123 -0
- package/dist/domain/generation.d.ts +1671 -132
- package/dist/domain/generation.js +202 -13
- package/dist/domain/geometry.d.ts +13 -0
- package/dist/domain/geometry.js +16 -0
- package/dist/domain/impact.d.ts +71 -0
- package/dist/domain/impact.js +76 -0
- package/dist/domain/init.d.ts +12 -4
- package/dist/domain/init.js +24 -18
- package/dist/domain/journey-composition.d.ts +2285 -0
- package/dist/domain/journey-composition.js +85 -0
- package/dist/domain/journey-lifecycle.d.ts +10 -0
- package/dist/domain/journey-lifecycle.js +9 -0
- package/dist/domain/journey.d.ts +2419 -212
- package/dist/domain/journey.js +384 -14
- package/dist/domain/knowledge.d.ts +165 -0
- package/dist/domain/knowledge.js +188 -0
- package/dist/domain/layout.d.ts +34 -5
- package/dist/domain/layout.js +64 -6
- package/dist/domain/locator-evidence.d.ts +3 -0
- package/dist/domain/locator-evidence.js +36 -0
- package/dist/domain/logcat-event.d.ts +54 -0
- package/dist/domain/logcat-event.js +40 -0
- package/dist/domain/observation.d.ts +33 -0
- package/dist/domain/observation.js +18 -0
- package/dist/domain/project-context.d.ts +219 -1
- package/dist/domain/project-context.js +209 -6
- package/dist/domain/proposed-step.d.ts +340 -60
- package/dist/domain/proposed-step.js +23 -3
- package/dist/domain/report.d.ts +1455 -28
- package/dist/domain/report.js +211 -14
- package/dist/domain/runtime-snapshot.d.ts +58 -1
- package/dist/domain/runtime-snapshot.js +14 -3
- package/dist/domain/runtime.d.ts +50 -0
- package/dist/domain/runtime.js +36 -0
- package/dist/domain/system-app-profiles.d.ts +6 -0
- package/dist/domain/system-app-profiles.js +43 -0
- package/dist/domain/ui-backend.d.ts +28 -0
- package/dist/domain/ui-backend.js +33 -0
- package/dist/domain/ui-cache.d.ts +11 -0
- package/dist/domain/ui-cache.js +10 -0
- package/dist/domain/verify-receipt.d.ts +17 -0
- package/dist/domain/verify-receipt.js +17 -0
- package/dist/domain/window-hierarchy.d.ts +84 -0
- package/dist/domain/window-hierarchy.js +172 -0
- package/dist/domain/workflow-manifest.d.ts +77 -0
- package/dist/domain/workflow-manifest.js +136 -0
- package/dist/domain/workspace.d.ts +30 -0
- package/dist/domain/workspace.js +74 -0
- package/dist/ports/adb.d.ts +34 -1
- package/dist/ports/align-prompt.d.ts +11 -0
- package/dist/ports/align-prompt.js +6 -0
- package/dist/ports/anchor-resolver.d.ts +30 -0
- package/dist/ports/anchor-resolver.js +1 -0
- package/dist/ports/annotated-screen-resolver.d.ts +4 -0
- package/dist/ports/annotated-screen-resolver.js +1 -0
- package/dist/ports/camera-probe.d.ts +23 -0
- package/dist/ports/camera-probe.js +8 -0
- package/dist/ports/context-document-writer.d.ts +24 -0
- package/dist/ports/context-document-writer.js +1 -0
- package/dist/ports/detached-process-launcher.d.ts +12 -0
- package/dist/ports/detached-process-launcher.js +1 -0
- package/dist/ports/diagnostics.d.ts +15 -0
- package/dist/ports/diagnostics.js +1 -0
- package/dist/ports/external-flow-registry.d.ts +37 -0
- package/dist/ports/external-flow-registry.js +1 -0
- package/dist/ports/generation-prompt.d.ts +2 -1
- package/dist/ports/generation-session-store.d.ts +12 -2
- package/dist/ports/git-diff.d.ts +9 -0
- package/dist/ports/git-diff.js +1 -0
- package/dist/ports/journey-composition-store.d.ts +17 -0
- package/dist/ports/journey-composition-store.js +1 -0
- package/dist/ports/knowledge-registry.d.ts +20 -0
- package/dist/ports/knowledge-registry.js +1 -0
- package/dist/ports/process-runner.d.ts +7 -0
- package/dist/ports/project-file-inspector.d.ts +1 -0
- package/dist/ports/project-identity-inspector.d.ts +24 -0
- package/dist/ports/project-identity-inspector.js +1 -0
- package/dist/ports/project-inventory-inspector.d.ts +21 -0
- package/dist/ports/project-inventory-inspector.js +1 -0
- package/dist/ports/project-module-discoverer.d.ts +26 -0
- package/dist/ports/project-module-discoverer.js +1 -0
- package/dist/ports/recorder-prompt.d.ts +10 -1
- package/dist/ports/runtime-backend.d.ts +89 -0
- package/dist/ports/runtime-backend.js +13 -0
- package/dist/ports/runtime-capability.d.ts +12 -0
- package/dist/ports/runtime-capability.js +20 -0
- package/dist/ports/runtime-session-ports.d.ts +25 -0
- package/dist/ports/runtime-session-ports.js +1 -0
- package/dist/ports/screenshot.d.ts +11 -0
- package/dist/ports/screenshot.js +1 -0
- package/dist/ports/skill-installer.d.ts +2 -2
- package/dist/ports/ui-snapshot.d.ts +35 -0
- package/dist/ports/ui-snapshot.js +1 -0
- package/dist/ports/ui-stability.d.ts +19 -0
- package/dist/ports/ui-stability.js +1 -0
- package/dist/ports/workspace-layout.d.ts +4 -0
- package/dist/ports/workspace-layout.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/paths.d.ts +2 -0
- package/dist/shared/paths.js +24 -0
- package/dist/shared/strings.d.ts +2 -0
- package/dist/shared/strings.js +16 -0
- package/package.json +7 -2
- package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
- package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
- package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
- package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
- package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
- package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
- package/dist/ports/android-cli.d.ts +0 -21
- /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
const THREADTIME =
|
|
1
|
+
const THREADTIME = /^(\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+(\d+)\s+(\d+)\s+([VDIWEFA])\s+(.+?):\s(.*)$/;
|
|
2
2
|
function parseLine(raw, receivedAt) {
|
|
3
3
|
const match = THREADTIME.exec(raw);
|
|
4
4
|
if (match === null) {
|
|
5
5
|
return { receivedAt, raw };
|
|
6
6
|
}
|
|
7
|
-
const [, pid, tid, level, tag, message] = match;
|
|
8
|
-
if (
|
|
7
|
+
const [, deviceTimestamp, pid, tid, level, tag, message] = match;
|
|
8
|
+
if (deviceTimestamp === undefined
|
|
9
|
+
||
|
|
10
|
+
pid === undefined
|
|
9
11
|
|| tid === undefined
|
|
10
12
|
|| level === undefined
|
|
11
13
|
|| tag === undefined
|
|
@@ -14,6 +16,7 @@ function parseLine(raw, receivedAt) {
|
|
|
14
16
|
}
|
|
15
17
|
return {
|
|
16
18
|
receivedAt,
|
|
19
|
+
deviceTimestamp,
|
|
17
20
|
raw,
|
|
18
21
|
pid: Number(pid),
|
|
19
22
|
tid: Number(tid),
|
|
@@ -31,30 +34,106 @@ function startupFailure(result) {
|
|
|
31
34
|
? "Logcat startup timed out"
|
|
32
35
|
: `Logcat exited during startup with code ${String(result.exitCode)}`);
|
|
33
36
|
}
|
|
37
|
+
const COMPACT_THRESHOLD = 1024;
|
|
34
38
|
export class LogcatCollector {
|
|
35
39
|
adb;
|
|
36
40
|
clock;
|
|
41
|
+
limits;
|
|
42
|
+
/** Live lines are `collected[head..]`; dropped lines are compacted lazily. */
|
|
37
43
|
collected = [];
|
|
44
|
+
head = 0;
|
|
45
|
+
bufferedBytes = 0;
|
|
46
|
+
droppedByPid = new Map();
|
|
38
47
|
stderr = [];
|
|
39
48
|
scopedPids = new Set();
|
|
40
49
|
running;
|
|
41
50
|
streamMetadata;
|
|
42
51
|
stopPromise;
|
|
43
|
-
constructor(adb, clock
|
|
52
|
+
constructor(adb, clock, limits = {
|
|
53
|
+
maxLines: 10000,
|
|
54
|
+
maxBytes: 2 * 1024 * 1024
|
|
55
|
+
}) {
|
|
44
56
|
this.adb = adb;
|
|
45
57
|
this.clock = clock;
|
|
58
|
+
this.limits = limits;
|
|
59
|
+
if (!Number.isInteger(limits.maxLines) || limits.maxLines < 1
|
|
60
|
+
|| !Number.isInteger(limits.maxBytes) || limits.maxBytes < 1) {
|
|
61
|
+
throw new Error("Logcat buffer limits must be positive integers");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
append(line) {
|
|
65
|
+
const parsed = parseLine(line, this.clock.now());
|
|
66
|
+
if (this.scopedPids.size > 0 && parsed.pid !== undefined
|
|
67
|
+
&& !this.scopedPids.has(parsed.pid)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const bytes = Buffer.byteLength(line, "utf8") + 1;
|
|
71
|
+
this.collected.push(parsed);
|
|
72
|
+
this.bufferedBytes += bytes;
|
|
73
|
+
while (this.collected.length - this.head > this.limits.maxLines
|
|
74
|
+
|| this.bufferedBytes > this.limits.maxBytes) {
|
|
75
|
+
const dropped = this.collected[this.head];
|
|
76
|
+
if (dropped === undefined) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
this.head += 1;
|
|
80
|
+
const size = Buffer.byteLength(dropped.raw, "utf8") + 1;
|
|
81
|
+
this.bufferedBytes -= size;
|
|
82
|
+
const previous = this.droppedByPid.get(dropped.pid);
|
|
83
|
+
this.droppedByPid.set(dropped.pid, {
|
|
84
|
+
lines: (previous?.lines ?? 0) + 1,
|
|
85
|
+
bytes: (previous?.bytes ?? 0) + size,
|
|
86
|
+
lastDroppedAt: dropped.receivedAt
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Array#shift is O(n); a saturated buffer would otherwise move every
|
|
90
|
+
// retained line on each append. Compaction is amortized O(1) per line.
|
|
91
|
+
if (this.head >= COMPACT_THRESHOLD
|
|
92
|
+
&& this.head * 2 >= this.collected.length) {
|
|
93
|
+
this.collected.splice(0, this.head);
|
|
94
|
+
this.head = 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
retained() {
|
|
98
|
+
return this.head === 0 ? this.collected : this.collected.slice(this.head);
|
|
99
|
+
}
|
|
100
|
+
addScopedPids(pids) {
|
|
101
|
+
for (const pid of pids) {
|
|
102
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
103
|
+
throw new Error("Logcat PID must be a positive integer");
|
|
104
|
+
}
|
|
105
|
+
this.scopedPids.add(pid);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
droppedEvidence() {
|
|
109
|
+
const relevant = [
|
|
110
|
+
this.droppedByPid.get(undefined),
|
|
111
|
+
...[...this.scopedPids].map((pid) => this.droppedByPid.get(pid))
|
|
112
|
+
].filter((entry) => entry !== undefined);
|
|
113
|
+
if (relevant.length === 0) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
lines: relevant.reduce((total, entry) => total + entry.lines, 0),
|
|
118
|
+
bytes: relevant.reduce((total, entry) => total + entry.bytes, 0),
|
|
119
|
+
lastDroppedAt: Math.max(...relevant.map((entry) => entry.lastDroppedAt))
|
|
120
|
+
};
|
|
46
121
|
}
|
|
47
122
|
async start(options) {
|
|
48
123
|
if (this.running !== undefined) {
|
|
49
124
|
throw new Error("Logcat collector already started");
|
|
50
125
|
}
|
|
126
|
+
this.addScopedPids(options.pids ?? []);
|
|
51
127
|
const logcatOptions = {
|
|
52
128
|
deviceSerial: options.deviceSerial,
|
|
53
129
|
onStdoutLine: (line) => {
|
|
54
|
-
this.
|
|
130
|
+
this.append(line);
|
|
55
131
|
},
|
|
56
132
|
onStderrLine: (line) => {
|
|
57
133
|
this.stderr.push(line);
|
|
134
|
+
if (this.stderr.length > 100) {
|
|
135
|
+
this.stderr.shift();
|
|
136
|
+
}
|
|
58
137
|
},
|
|
59
138
|
...(options.signal === undefined ? {} : { signal: options.signal })
|
|
60
139
|
};
|
|
@@ -62,34 +141,43 @@ export class LogcatCollector {
|
|
|
62
141
|
this.streamMetadata = { deviceSerial: options.deviceSerial };
|
|
63
142
|
const startupResult = await this.running.started;
|
|
64
143
|
if (startupResult !== undefined) {
|
|
65
|
-
throw new Error(startupFailure(startupResult));
|
|
144
|
+
throw new Error(this.stderr.join("\n").trim() || startupFailure(startupResult));
|
|
66
145
|
}
|
|
67
146
|
}
|
|
68
147
|
scopeToPids(pids) {
|
|
69
148
|
if (this.streamMetadata === undefined) {
|
|
70
149
|
throw new Error("Logcat collector has not started");
|
|
71
150
|
}
|
|
72
|
-
|
|
73
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
74
|
-
throw new Error("Logcat PID must be a positive integer");
|
|
75
|
-
}
|
|
76
|
-
this.scopedPids.add(pid);
|
|
77
|
-
}
|
|
151
|
+
this.addScopedPids(pids);
|
|
78
152
|
}
|
|
79
153
|
metadata() {
|
|
80
154
|
if (this.streamMetadata === undefined) {
|
|
81
155
|
throw new Error("Logcat collector has not started");
|
|
82
156
|
}
|
|
157
|
+
const dropped = this.droppedEvidence();
|
|
83
158
|
return {
|
|
84
159
|
deviceSerial: this.streamMetadata.deviceSerial,
|
|
85
|
-
pids: [...this.scopedPids].sort((left, right) => left - right)
|
|
160
|
+
pids: [...this.scopedPids].sort((left, right) => left - right),
|
|
161
|
+
...(dropped === undefined ? {} : {
|
|
162
|
+
droppedLines: dropped.lines,
|
|
163
|
+
droppedBytes: dropped.bytes,
|
|
164
|
+
lastDroppedAtMs: dropped.lastDroppedAt
|
|
165
|
+
})
|
|
86
166
|
};
|
|
87
167
|
}
|
|
88
168
|
lines() {
|
|
89
169
|
if (this.scopedPids.size === 0) {
|
|
90
|
-
return [
|
|
170
|
+
return [];
|
|
91
171
|
}
|
|
92
|
-
return this.
|
|
172
|
+
return this.retained().filter((line) => line.pid !== undefined && this.scopedPids.has(line.pid));
|
|
173
|
+
}
|
|
174
|
+
/** Includes unparsed lines as raw artifacts, within the declared buffer limit. */
|
|
175
|
+
rawLines() {
|
|
176
|
+
return this.retained().filter((line) => line.pid === undefined || this.scopedPids.has(line.pid));
|
|
177
|
+
}
|
|
178
|
+
completeSince(startedAt) {
|
|
179
|
+
const dropped = this.droppedEvidence();
|
|
180
|
+
return dropped === undefined || dropped.lastDroppedAt < startedAt;
|
|
93
181
|
}
|
|
94
182
|
diagnosticLines() {
|
|
95
183
|
return [...this.stderr];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EventCapture, LogcatEventExpect } from "../../domain/logcat-event.js";
|
|
2
|
+
import type { LogcatLine } from "./logcat-collector.js";
|
|
3
|
+
/** Events are JSON objects in the message, with an `event` and `fields` object. */
|
|
4
|
+
export declare function matchesLogcatEvent(line: LogcatLine, expect: LogcatEventExpect): boolean;
|
|
5
|
+
/** Read only a declared field from the uniquely matched structured event. */
|
|
6
|
+
export declare function captureLogcatEvent(line: LogcatLine, expect: LogcatEventExpect, capture: EventCapture): string | undefined;
|
|
7
|
+
/** Only a stable, structured app-emitted class is accepted. */
|
|
8
|
+
export declare function requestErrorClassFor(line: LogcatLine): "client" | "auth" | "network" | "server" | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Events are JSON objects in the message, with an `event` and `fields` object. */
|
|
2
|
+
export function matchesLogcatEvent(line, expect) {
|
|
3
|
+
if (line.pid === undefined || line.tag !== expect.tag
|
|
4
|
+
|| line.message === undefined) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
let value;
|
|
8
|
+
try {
|
|
9
|
+
value = JSON.parse(line.message);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const record = value;
|
|
18
|
+
if (record.event !== expect.event) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const fields = record.fields;
|
|
22
|
+
if (fields === null || typeof fields !== "object" || Array.isArray(fields)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const facts = fields;
|
|
26
|
+
return Object.entries(expect.fields).every(([key, item]) => facts[key] === item)
|
|
27
|
+
&& (expect.correlation === undefined
|
|
28
|
+
|| facts[expect.correlation.key] === expect.correlation.value);
|
|
29
|
+
}
|
|
30
|
+
/** Read only a declared field from the uniquely matched structured event. */
|
|
31
|
+
export function captureLogcatEvent(line, expect, capture) {
|
|
32
|
+
if (!matchesLogcatEvent(line, expect) || line.message === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const event = JSON.parse(line.message);
|
|
35
|
+
const key = capture.field ?? expect.correlation?.key;
|
|
36
|
+
if (key === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
const value = event.fields[key];
|
|
39
|
+
if (capture.valueType === "integer") {
|
|
40
|
+
return typeof value === "number" && Number.isSafeInteger(value)
|
|
41
|
+
? String(value) : undefined;
|
|
42
|
+
}
|
|
43
|
+
if (typeof value !== "string")
|
|
44
|
+
return undefined;
|
|
45
|
+
if (capture.valueType === "identifier") {
|
|
46
|
+
return /^[a-zA-Z0-9_-]{1,64}$/.test(value) ? value : undefined;
|
|
47
|
+
}
|
|
48
|
+
if (value.length === 0 || value.length > 128)
|
|
49
|
+
return undefined;
|
|
50
|
+
for (const character of value) {
|
|
51
|
+
const point = character.codePointAt(0) ?? 0;
|
|
52
|
+
if (point < 32 || point === 127)
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
/** Only a stable, structured app-emitted class is accepted. */
|
|
58
|
+
export function requestErrorClassFor(line) {
|
|
59
|
+
if (line.message === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
let event;
|
|
62
|
+
try {
|
|
63
|
+
event = JSON.parse(line.message);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
if (event === null || typeof event !== "object" || Array.isArray(event)) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const fields = event.fields;
|
|
72
|
+
if (fields === null || typeof fields !== "object" || Array.isArray(fields)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const value = fields.errorClass;
|
|
76
|
+
return value === "client" || value === "auth"
|
|
77
|
+
|| value === "network" || value === "server"
|
|
78
|
+
? value : undefined;
|
|
79
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function logcatStopFailed(result) {
|
|
2
|
+
if (result.timedOut
|
|
3
|
+
|| result.cancelled
|
|
4
|
+
|| result.spawnError !== undefined) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
return result.exitCode !== 0
|
|
8
|
+
&& !(result.terminationRequested === true
|
|
9
|
+
&& (result.signal === "SIGTERM"
|
|
10
|
+
|| result.signal === "SIGKILL"));
|
|
11
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ProjectIdentityInspector } from "../../ports/project-identity-inspector.js";
|
|
2
|
+
import type { ProjectModuleDiscoverer } from "../../ports/project-module-discoverer.js";
|
|
3
|
+
import type { ContextDocumentWriter } from "../../ports/context-document-writer.js";
|
|
4
|
+
import type { ProjectFileInspector } from "../../ports/project-file-inspector.js";
|
|
5
|
+
import type { ProjectInventoryInspector } from "../../ports/project-inventory-inspector.js";
|
|
6
|
+
export type ContextGenerateErrorCode = "CONTEXT_INVALID" | "MODULE_DISCOVERY_FAILED" | "IDENTITY_INSPECTION_FAILED" | "NO_EVIDENCE" | "CONTEXT_WRITE_FAILED" | "CONTEXT_ALREADY_EXISTS";
|
|
7
|
+
export declare class ContextGenerateError extends Error {
|
|
8
|
+
readonly code: ContextGenerateErrorCode;
|
|
9
|
+
readonly name = "ContextGenerateError";
|
|
10
|
+
constructor(code: ContextGenerateErrorCode, message: string);
|
|
11
|
+
}
|
|
12
|
+
export interface ContextGenerateInput {
|
|
13
|
+
readonly projectRoot: string;
|
|
14
|
+
readonly contextPath: string;
|
|
15
|
+
readonly force?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ContextGenerateModuleResult {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly projectDir: string;
|
|
20
|
+
readonly kind: string;
|
|
21
|
+
readonly status: "notAnalyzed";
|
|
22
|
+
readonly evidenceCount: number;
|
|
23
|
+
readonly contextPath: string;
|
|
24
|
+
readonly sha256: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ContextGenerateResult {
|
|
27
|
+
readonly status: "generated";
|
|
28
|
+
readonly packageName: string;
|
|
29
|
+
readonly launchActivity: string;
|
|
30
|
+
readonly modules: readonly ContextGenerateModuleResult[];
|
|
31
|
+
readonly indexHash: string;
|
|
32
|
+
readonly contextPath: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ContextGeneratorDependencies {
|
|
35
|
+
readonly discoverer: ProjectModuleDiscoverer;
|
|
36
|
+
readonly identity: ProjectIdentityInspector;
|
|
37
|
+
readonly files: ProjectFileInspector;
|
|
38
|
+
readonly inventory: ProjectInventoryInspector;
|
|
39
|
+
readonly writer: ContextDocumentWriter;
|
|
40
|
+
}
|
|
41
|
+
export declare class ContextGenerator {
|
|
42
|
+
private readonly dependencies;
|
|
43
|
+
constructor(dependencies: ContextGeneratorDependencies);
|
|
44
|
+
readonly generate: (input: ContextGenerateInput) => Promise<ContextGenerateResult>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { isAbsolute, resolve } from "node:path";
|
|
2
|
+
import { ProjectContextModuleSchema, ProjectContextSchema } from "../../domain/project-context.js";
|
|
3
|
+
import { assertProjectPathUnder, CONTEXT_DIR } from "../../domain/workspace.js";
|
|
4
|
+
import { projectRelativePath } from "../../shared/paths.js";
|
|
5
|
+
import { compareStrings } from "../../shared/strings.js";
|
|
6
|
+
import { MAX_CONTEXT_EVIDENCE_BYTES } from "./context-validator.js";
|
|
7
|
+
import { semanticSha256 } from "./evidence-hash.js";
|
|
8
|
+
export class ContextGenerateError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
name = "ContextGenerateError";
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const INVENTORY_CATEGORIES = ["manifests", "sources", "layouts", "navigation"];
|
|
17
|
+
const DEFAULT_INTERACTION_POLICY = {
|
|
18
|
+
allowedActions: ["click", "inputText", "back", "wait"],
|
|
19
|
+
confirmationRequiredActions: [],
|
|
20
|
+
forbiddenActions: []
|
|
21
|
+
};
|
|
22
|
+
const ROOT_EVIDENCE_CANDIDATES = [
|
|
23
|
+
"settings.gradle.kts",
|
|
24
|
+
"settings.gradle",
|
|
25
|
+
"build.gradle.kts",
|
|
26
|
+
"build.gradle",
|
|
27
|
+
"gradle.properties",
|
|
28
|
+
"gradle/libs.versions.toml"
|
|
29
|
+
];
|
|
30
|
+
function moduleIdToContextPath(moduleId) {
|
|
31
|
+
const sanitized = moduleId.replace(/^:/, "").replace(/:/g, "-");
|
|
32
|
+
return `${CONTEXT_DIR}/modules/${sanitized}.json`;
|
|
33
|
+
}
|
|
34
|
+
async function collectEvidence(files, projectRoot, paths) {
|
|
35
|
+
const evidence = [];
|
|
36
|
+
for (const path of paths) {
|
|
37
|
+
const inspection = await files.inspectProjectFile({
|
|
38
|
+
projectRoot,
|
|
39
|
+
relativePath: path,
|
|
40
|
+
maximumBytes: MAX_CONTEXT_EVIDENCE_BYTES
|
|
41
|
+
});
|
|
42
|
+
if (inspection.status === "inspected" && inspection.bytes !== undefined) {
|
|
43
|
+
evidence.push({
|
|
44
|
+
path: inspection.resolvedRelativePath,
|
|
45
|
+
sha256: inspection.sha256,
|
|
46
|
+
semanticSha256: semanticSha256(inspection.bytes)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return evidence;
|
|
51
|
+
}
|
|
52
|
+
async function collectRootEvidence(files, projectRoot) {
|
|
53
|
+
return collectEvidence(files, projectRoot, ROOT_EVIDENCE_CANDIDATES);
|
|
54
|
+
}
|
|
55
|
+
function buildShardDocument(module, evidence, pathSetSha256) {
|
|
56
|
+
const manifest = {
|
|
57
|
+
version: 1,
|
|
58
|
+
files: evidence.map((record) => ({
|
|
59
|
+
path: record.path,
|
|
60
|
+
sha256: record.sha256,
|
|
61
|
+
semanticSha256: record.semanticSha256,
|
|
62
|
+
confidence: "sourceConfirmed"
|
|
63
|
+
}))
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
version: 2,
|
|
67
|
+
moduleId: module.id,
|
|
68
|
+
projectDir: module.projectDir,
|
|
69
|
+
status: "notAnalyzed",
|
|
70
|
+
inventory: {
|
|
71
|
+
version: 2,
|
|
72
|
+
pathSetSha256,
|
|
73
|
+
categories: [...INVENTORY_CATEGORIES]
|
|
74
|
+
},
|
|
75
|
+
manifest,
|
|
76
|
+
summary: {
|
|
77
|
+
features: [],
|
|
78
|
+
activities: [],
|
|
79
|
+
elements: [],
|
|
80
|
+
transitions: [],
|
|
81
|
+
logcat: []
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function buildModuleReference(module, contextPath, sha256, dependsOn) {
|
|
86
|
+
return {
|
|
87
|
+
id: module.id,
|
|
88
|
+
projectDir: module.projectDir,
|
|
89
|
+
kind: module.kind,
|
|
90
|
+
contextPath,
|
|
91
|
+
sha256,
|
|
92
|
+
features: [],
|
|
93
|
+
activities: [],
|
|
94
|
+
dependsOn: [...dependsOn],
|
|
95
|
+
status: "notAnalyzed"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function writeDocument(writer, projectRoot, relativePath, document) {
|
|
99
|
+
const result = await writer.writeContextDocument({
|
|
100
|
+
projectRoot,
|
|
101
|
+
relativePath,
|
|
102
|
+
document
|
|
103
|
+
});
|
|
104
|
+
if (result.status !== "written") {
|
|
105
|
+
throw new ContextGenerateError("CONTEXT_WRITE_FAILED", result.status === "escape"
|
|
106
|
+
? `Context document resolves outside the project: ${relativePath}`
|
|
107
|
+
: `Context document cannot be written: ${relativePath}${"message" in result ? `: ${result.message}` : ""}`);
|
|
108
|
+
}
|
|
109
|
+
return result.sha256;
|
|
110
|
+
}
|
|
111
|
+
export class ContextGenerator {
|
|
112
|
+
dependencies;
|
|
113
|
+
constructor(dependencies) {
|
|
114
|
+
this.dependencies = dependencies;
|
|
115
|
+
}
|
|
116
|
+
generate = async (input) => {
|
|
117
|
+
const contextRoot = input.projectRoot;
|
|
118
|
+
const contextPath = isAbsolute(input.contextPath)
|
|
119
|
+
? input.contextPath
|
|
120
|
+
: resolve(contextRoot, input.contextPath);
|
|
121
|
+
const contextRelativePath = projectRelativePath(contextRoot, contextPath, (message) => new ContextGenerateError("CONTEXT_INVALID", message));
|
|
122
|
+
const documentRelativePath = contextRelativePath;
|
|
123
|
+
try {
|
|
124
|
+
assertProjectPathUnder(contextRoot, documentRelativePath, CONTEXT_DIR, "Project Context output");
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new ContextGenerateError("CONTEXT_INVALID", error instanceof Error ? error.message : String(error));
|
|
128
|
+
}
|
|
129
|
+
if (input.force !== true) {
|
|
130
|
+
const existing = await this.dependencies.files.inspectProjectFile({
|
|
131
|
+
projectRoot: contextRoot,
|
|
132
|
+
relativePath: documentRelativePath,
|
|
133
|
+
maximumBytes: 1
|
|
134
|
+
});
|
|
135
|
+
if (existing.status === "inspected") {
|
|
136
|
+
throw new ContextGenerateError("CONTEXT_ALREADY_EXISTS", `Project Context already exists: ${documentRelativePath} (use --force to overwrite)`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const discovery = await this.dependencies.discoverer.discoverModules({
|
|
140
|
+
projectRoot: input.projectRoot
|
|
141
|
+
});
|
|
142
|
+
if (discovery.status !== "discovered") {
|
|
143
|
+
throw new ContextGenerateError("MODULE_DISCOVERY_FAILED", discovery.status === "noApplicationModule"
|
|
144
|
+
? "No application module found in settings.gradle"
|
|
145
|
+
: discovery.status === "noSettingsFile"
|
|
146
|
+
? "No settings.gradle or settings.gradle.kts found"
|
|
147
|
+
: `Module discovery failed: ${discovery.status}`);
|
|
148
|
+
}
|
|
149
|
+
const appModule = discovery.modules.find((module) => module.kind === "application");
|
|
150
|
+
if (appModule === undefined) {
|
|
151
|
+
throw new ContextGenerateError("MODULE_DISCOVERY_FAILED", "No application module found");
|
|
152
|
+
}
|
|
153
|
+
const identity = await this.dependencies.identity.inspectIdentity({
|
|
154
|
+
projectRoot: input.projectRoot,
|
|
155
|
+
moduleDir: appModule.projectDir
|
|
156
|
+
});
|
|
157
|
+
if (identity.status !== "inspected") {
|
|
158
|
+
throw new ContextGenerateError("IDENTITY_INSPECTION_FAILED", `Identity inspection failed: ${identity.status}`);
|
|
159
|
+
}
|
|
160
|
+
const moduleResults = [];
|
|
161
|
+
const moduleReferences = [];
|
|
162
|
+
for (const module of discovery.modules) {
|
|
163
|
+
const inventoryResult = await this.dependencies.inventory.inspectProjectInventory({
|
|
164
|
+
projectRoot: input.projectRoot,
|
|
165
|
+
projectDir: module.projectDir,
|
|
166
|
+
categories: [...INVENTORY_CATEGORIES]
|
|
167
|
+
});
|
|
168
|
+
if (inventoryResult.status !== "inspected") {
|
|
169
|
+
throw new ContextGenerateError("MODULE_DISCOVERY_FAILED", `Unable to inspect module inventory: ${module.id}`);
|
|
170
|
+
}
|
|
171
|
+
const evidence = await collectEvidence(this.dependencies.files, input.projectRoot, inventoryResult.paths);
|
|
172
|
+
if (evidence.length === 0) {
|
|
173
|
+
throw new ContextGenerateError("NO_EVIDENCE", `No evidence files collected for module: ${module.id}`);
|
|
174
|
+
}
|
|
175
|
+
const shardPath = moduleIdToContextPath(module.id);
|
|
176
|
+
const shardDocument = buildShardDocument(module, evidence, inventoryResult.pathSetSha256);
|
|
177
|
+
const parsed = ProjectContextModuleSchema.safeParse(shardDocument);
|
|
178
|
+
if (!parsed.success) {
|
|
179
|
+
throw new ContextGenerateError("CONTEXT_INVALID", `Generated shard does not match module schema: ${module.id}`);
|
|
180
|
+
}
|
|
181
|
+
const shardSha256 = await writeDocument(this.dependencies.writer, contextRoot, shardPath, parsed.data);
|
|
182
|
+
moduleResults.push({
|
|
183
|
+
id: module.id,
|
|
184
|
+
projectDir: module.projectDir,
|
|
185
|
+
kind: module.kind,
|
|
186
|
+
status: "notAnalyzed",
|
|
187
|
+
evidenceCount: evidence.length,
|
|
188
|
+
contextPath: shardPath,
|
|
189
|
+
sha256: shardSha256
|
|
190
|
+
});
|
|
191
|
+
moduleReferences.push(buildModuleReference(module, shardPath, shardSha256, module.dependsOn));
|
|
192
|
+
}
|
|
193
|
+
moduleReferences.sort((a, b) => compareStrings(a.id, b.id));
|
|
194
|
+
const rootEvidence = await collectRootEvidence(this.dependencies.files, input.projectRoot);
|
|
195
|
+
if (rootEvidence.length === 0) {
|
|
196
|
+
throw new ContextGenerateError("NO_EVIDENCE", "No root evidence files collected");
|
|
197
|
+
}
|
|
198
|
+
const indexDocument = {
|
|
199
|
+
version: 2,
|
|
200
|
+
packageName: identity.packageName,
|
|
201
|
+
launchActivity: identity.launchActivity,
|
|
202
|
+
manifest: {
|
|
203
|
+
version: 1,
|
|
204
|
+
files: rootEvidence.map((record) => ({
|
|
205
|
+
path: record.path,
|
|
206
|
+
sha256: record.sha256,
|
|
207
|
+
semanticSha256: record.semanticSha256,
|
|
208
|
+
confidence: "sourceConfirmed"
|
|
209
|
+
}))
|
|
210
|
+
},
|
|
211
|
+
interactionPolicy: { ...DEFAULT_INTERACTION_POLICY },
|
|
212
|
+
modules: moduleReferences
|
|
213
|
+
};
|
|
214
|
+
const parsedIndex = ProjectContextSchema.safeParse(indexDocument);
|
|
215
|
+
if (!parsedIndex.success) {
|
|
216
|
+
throw new ContextGenerateError("CONTEXT_INVALID", "Generated index does not match the version 2 schema");
|
|
217
|
+
}
|
|
218
|
+
const indexHash = await writeDocument(this.dependencies.writer, contextRoot, documentRelativePath, parsedIndex.data);
|
|
219
|
+
return {
|
|
220
|
+
status: "generated",
|
|
221
|
+
packageName: identity.packageName,
|
|
222
|
+
launchActivity: identity.launchActivity,
|
|
223
|
+
modules: moduleResults,
|
|
224
|
+
indexHash,
|
|
225
|
+
contextPath: contextRelativePath
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ProjectContext, type ProjectContextModule, type ResolvedProjectContext } from "../../domain/project-context.js";
|
|
2
|
+
import type { ProjectFileInspector } from "../../ports/project-file-inspector.js";
|
|
3
|
+
import type { ProjectInventoryInspector } from "../../ports/project-inventory-inspector.js";
|
|
4
|
+
export declare const MAX_CONTEXT_SHARD_BYTES: number;
|
|
5
|
+
export type ContextLoadErrorCode = "CONTEXT_INVALID" | "CONTEXT_STALE" | "CONTEXT_MODULE_NOT_FOUND" | "CONTEXT_MODULE_INCOMPLETE";
|
|
6
|
+
export declare class ContextLoadError extends Error {
|
|
7
|
+
readonly code: ContextLoadErrorCode;
|
|
8
|
+
readonly name = "ContextLoadError";
|
|
9
|
+
constructor(code: ContextLoadErrorCode, message: string);
|
|
10
|
+
}
|
|
11
|
+
export interface LoadedProjectContext {
|
|
12
|
+
context: ResolvedProjectContext;
|
|
13
|
+
bundle: ProjectContext;
|
|
14
|
+
modules: ProjectContextModule[];
|
|
15
|
+
}
|
|
16
|
+
export interface LoadedContextIndex {
|
|
17
|
+
bundle: ProjectContext;
|
|
18
|
+
indexHash: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ContextLoadInput {
|
|
21
|
+
projectRoot: string;
|
|
22
|
+
contextPath: string;
|
|
23
|
+
moduleIds?: string[] | undefined;
|
|
24
|
+
allowIncomplete?: boolean | undefined;
|
|
25
|
+
}
|
|
26
|
+
export interface ContextLoaderDependencies {
|
|
27
|
+
files: ProjectFileInspector;
|
|
28
|
+
inventory: ProjectInventoryInspector;
|
|
29
|
+
readJson: (path: string) => Promise<unknown>;
|
|
30
|
+
}
|
|
31
|
+
export declare class ContextLoader {
|
|
32
|
+
private readonly dependencies;
|
|
33
|
+
constructor(dependencies: ContextLoaderDependencies);
|
|
34
|
+
private readonly readStableDocument;
|
|
35
|
+
readonly readIndex: (input: {
|
|
36
|
+
projectRoot: string;
|
|
37
|
+
contextPath: string;
|
|
38
|
+
}) => Promise<LoadedContextIndex>;
|
|
39
|
+
readonly load: (input: ContextLoadInput) => Promise<LoadedProjectContext>;
|
|
40
|
+
}
|