taphound 0.2.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +200 -0
  3. package/README.zh-CN.md +200 -0
  4. package/assets/brand/taphound-mark.svg +9 -0
  5. package/assets/skills/taphound-ai-journey/GUIDE.md +862 -0
  6. package/assets/skills/taphound-ai-journey/SKILL.md +377 -0
  7. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +526 -0
  8. package/assets/skills/taphound-ai-journey/prompts/check-completion.md +52 -0
  9. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +109 -0
  10. package/assets/skills/taphound-ai-journey/schemas/observe-output.json +42 -0
  11. package/assets/skills/taphound-ai-journey/schemas/project-context.json +81 -0
  12. package/assets/skills/taphound-ai-journey/schemas/proposed-step-envelope.json +267 -0
  13. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +49 -0
  14. package/dist/adapters/adb/adb-adapter.d.ts +23 -0
  15. package/dist/adapters/adb/adb-adapter.js +218 -0
  16. package/dist/adapters/android-cli/android-cli-adapter.d.ts +11 -0
  17. package/dist/adapters/android-cli/android-cli-adapter.js +64 -0
  18. package/dist/adapters/android-cli/layout-parser.d.ts +3 -0
  19. package/dist/adapters/android-cli/layout-parser.js +174 -0
  20. package/dist/adapters/clock/system-clock.d.ts +5 -0
  21. package/dist/adapters/clock/system-clock.js +13 -0
  22. package/dist/adapters/filesystem/artifact-store.d.ts +4 -0
  23. package/dist/adapters/filesystem/artifact-store.js +73 -0
  24. package/dist/adapters/filesystem/generation-meta-writer.d.ts +16 -0
  25. package/dist/adapters/filesystem/generation-meta-writer.js +34 -0
  26. package/dist/adapters/filesystem/generation-session-store.d.ts +68 -0
  27. package/dist/adapters/filesystem/generation-session-store.js +1603 -0
  28. package/dist/adapters/filesystem/journey-writer.d.ts +16 -0
  29. package/dist/adapters/filesystem/journey-writer.js +34 -0
  30. package/dist/adapters/filesystem/project-bound-file.d.ts +6 -0
  31. package/dist/adapters/filesystem/project-bound-file.js +240 -0
  32. package/dist/adapters/filesystem/project-file-inspector.d.ts +8 -0
  33. package/dist/adapters/filesystem/project-file-inspector.js +198 -0
  34. package/dist/adapters/filesystem/skill-installer.d.ts +10 -0
  35. package/dist/adapters/filesystem/skill-installer.js +39 -0
  36. package/dist/adapters/process/node-process-runner.d.ts +8 -0
  37. package/dist/adapters/process/node-process-runner.js +155 -0
  38. package/dist/adapters/prompt/inquirer-generation-prompt.d.ts +55 -0
  39. package/dist/adapters/prompt/inquirer-generation-prompt.js +213 -0
  40. package/dist/adapters/prompt/inquirer-init-prompt.d.ts +32 -0
  41. package/dist/adapters/prompt/inquirer-init-prompt.js +55 -0
  42. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +47 -0
  43. package/dist/adapters/prompt/inquirer-recorder-prompt.js +157 -0
  44. package/dist/application/assertion/expectation-evaluator.d.ts +61 -0
  45. package/dist/application/assertion/expectation-evaluator.js +207 -0
  46. package/dist/application/collector/logcat-collector.d.ts +39 -0
  47. package/dist/application/collector/logcat-collector.js +107 -0
  48. package/dist/application/context/context-validator.d.ts +27 -0
  49. package/dist/application/context/context-validator.js +120 -0
  50. package/dist/application/doctor/doctor-service.d.ts +35 -0
  51. package/dist/application/doctor/doctor-service.js +178 -0
  52. package/dist/application/generation/focused-input.d.ts +2 -0
  53. package/dist/application/generation/focused-input.js +9 -0
  54. package/dist/application/generation/generation-confirmation-service.d.ts +76 -0
  55. package/dist/application/generation/generation-confirmation-service.js +383 -0
  56. package/dist/application/generation/generation-finalizer.d.ts +67 -0
  57. package/dist/application/generation/generation-finalizer.js +646 -0
  58. package/dist/application/generation/generation-publisher.d.ts +46 -0
  59. package/dist/application/generation/generation-publisher.js +178 -0
  60. package/dist/application/generation/generation-starter.d.ts +31 -0
  61. package/dist/application/generation/generation-starter.js +102 -0
  62. package/dist/application/generation/generation-step-executor.d.ts +52 -0
  63. package/dist/application/generation/generation-step-executor.js +668 -0
  64. package/dist/application/generation/proposed-step-validator.d.ts +11 -0
  65. package/dist/application/generation/proposed-step-validator.js +88 -0
  66. package/dist/application/generation/risk-evaluator.d.ts +9 -0
  67. package/dist/application/generation/risk-evaluator.js +14 -0
  68. package/dist/application/generation/runtime-observer.d.ts +39 -0
  69. package/dist/application/generation/runtime-observer.js +157 -0
  70. package/dist/application/init/init-service.d.ts +17 -0
  71. package/dist/application/init/init-service.js +44 -0
  72. package/dist/application/interaction/action-executor.d.ts +25 -0
  73. package/dist/application/interaction/action-executor.js +103 -0
  74. package/dist/application/interaction/fallback-resolver.d.ts +24 -0
  75. package/dist/application/interaction/fallback-resolver.js +48 -0
  76. package/dist/application/interaction/scroll-to-executor.d.ts +40 -0
  77. package/dist/application/interaction/scroll-to-executor.js +191 -0
  78. package/dist/application/locator/locator-resolver.d.ts +20 -0
  79. package/dist/application/locator/locator-resolver.js +89 -0
  80. package/dist/application/project/project-describer.d.ts +14 -0
  81. package/dist/application/project/project-describer.js +8 -0
  82. package/dist/application/recorder/locator-selector.d.ts +10 -0
  83. package/dist/application/recorder/locator-selector.js +80 -0
  84. package/dist/application/recorder/recorder-service.d.ts +41 -0
  85. package/dist/application/recorder/recorder-service.js +331 -0
  86. package/dist/application/report/report-writer.d.ts +10 -0
  87. package/dist/application/report/report-writer.js +39 -0
  88. package/dist/application/runtime/activity-waiter.d.ts +33 -0
  89. package/dist/application/runtime/activity-waiter.js +73 -0
  90. package/dist/application/runtime/launch-failure.d.ts +2 -0
  91. package/dist/application/runtime/launch-failure.js +20 -0
  92. package/dist/application/runtime/process-waiter.d.ts +27 -0
  93. package/dist/application/runtime/process-waiter.js +73 -0
  94. package/dist/application/runtime/step-runner.d.ts +50 -0
  95. package/dist/application/runtime/step-runner.js +429 -0
  96. package/dist/application/runtime/verify-runtime.d.ts +44 -0
  97. package/dist/application/runtime/verify-runtime.js +350 -0
  98. package/dist/application/wait/idle-waiter.d.ts +30 -0
  99. package/dist/application/wait/idle-waiter.js +90 -0
  100. package/dist/cli/commands/context.d.ts +3 -0
  101. package/dist/cli/commands/context.js +94 -0
  102. package/dist/cli/commands/doctor.d.ts +3 -0
  103. package/dist/cli/commands/doctor.js +51 -0
  104. package/dist/cli/commands/generation.d.ts +3 -0
  105. package/dist/cli/commands/generation.js +518 -0
  106. package/dist/cli/commands/init.d.ts +3 -0
  107. package/dist/cli/commands/init.js +104 -0
  108. package/dist/cli/commands/project.d.ts +3 -0
  109. package/dist/cli/commands/project.js +58 -0
  110. package/dist/cli/commands/record.d.ts +3 -0
  111. package/dist/cli/commands/record.js +88 -0
  112. package/dist/cli/commands/verify.d.ts +3 -0
  113. package/dist/cli/commands/verify.js +106 -0
  114. package/dist/cli/dependencies.d.ts +66 -0
  115. package/dist/cli/dependencies.js +204 -0
  116. package/dist/cli/main.d.ts +8 -0
  117. package/dist/cli/main.js +73 -0
  118. package/dist/cli/output.d.ts +16 -0
  119. package/dist/cli/output.js +22 -0
  120. package/dist/cli/program.d.ts +3 -0
  121. package/dist/cli/program.js +43 -0
  122. package/dist/domain/activity.d.ts +7 -0
  123. package/dist/domain/activity.js +65 -0
  124. package/dist/domain/app-process.d.ts +7 -0
  125. package/dist/domain/app-process.js +10 -0
  126. package/dist/domain/config.d.ts +15 -0
  127. package/dist/domain/config.js +17 -0
  128. package/dist/domain/failure.d.ts +4 -0
  129. package/dist/domain/failure.js +43 -0
  130. package/dist/domain/generation.d.ts +912 -0
  131. package/dist/domain/generation.js +342 -0
  132. package/dist/domain/init.d.ts +42 -0
  133. package/dist/domain/init.js +114 -0
  134. package/dist/domain/journey.d.ts +673 -0
  135. package/dist/domain/journey.js +109 -0
  136. package/dist/domain/layout.d.ts +35 -0
  137. package/dist/domain/layout.js +33 -0
  138. package/dist/domain/locator.d.ts +8 -0
  139. package/dist/domain/locator.js +5 -0
  140. package/dist/domain/project-context.d.ts +101 -0
  141. package/dist/domain/project-context.js +64 -0
  142. package/dist/domain/proposed-step.d.ts +341 -0
  143. package/dist/domain/proposed-step.js +87 -0
  144. package/dist/domain/report.d.ts +349 -0
  145. package/dist/domain/report.js +135 -0
  146. package/dist/domain/runtime-snapshot.d.ts +16 -0
  147. package/dist/domain/runtime-snapshot.js +46 -0
  148. package/dist/ports/adb.d.ts +42 -0
  149. package/dist/ports/adb.js +1 -0
  150. package/dist/ports/android-cli.d.ts +21 -0
  151. package/dist/ports/android-cli.js +1 -0
  152. package/dist/ports/artifact-store.d.ts +12 -0
  153. package/dist/ports/artifact-store.js +1 -0
  154. package/dist/ports/clock.d.ts +4 -0
  155. package/dist/ports/clock.js +1 -0
  156. package/dist/ports/generation-meta-writer.d.ts +10 -0
  157. package/dist/ports/generation-meta-writer.js +1 -0
  158. package/dist/ports/generation-prompt.d.ts +19 -0
  159. package/dist/ports/generation-prompt.js +6 -0
  160. package/dist/ports/generation-session-store.d.ts +32 -0
  161. package/dist/ports/generation-session-store.js +26 -0
  162. package/dist/ports/init-prompt.d.ts +8 -0
  163. package/dist/ports/init-prompt.js +6 -0
  164. package/dist/ports/journey-writer.d.ts +10 -0
  165. package/dist/ports/journey-writer.js +1 -0
  166. package/dist/ports/process-runner.d.ts +32 -0
  167. package/dist/ports/process-runner.js +1 -0
  168. package/dist/ports/project-bound-file.d.ts +8 -0
  169. package/dist/ports/project-bound-file.js +1 -0
  170. package/dist/ports/project-file-inspector.d.ts +33 -0
  171. package/dist/ports/project-file-inspector.js +1 -0
  172. package/dist/ports/recorder-prompt.d.ts +29 -0
  173. package/dist/ports/recorder-prompt.js +1 -0
  174. package/dist/ports/skill-installer.d.ts +8 -0
  175. package/dist/ports/skill-installer.js +1 -0
  176. package/package.json +64 -0
@@ -0,0 +1,6 @@
1
+ export class GenerationPromptCancelledError extends Error {
2
+ name = "GenerationPromptCancelledError";
3
+ constructor() {
4
+ super("Generation prompt was cancelled");
5
+ }
6
+ }
@@ -0,0 +1,32 @@
1
+ import type { GenerationInFlight, GenerationSession, PendingConfirmation } from "../domain/generation.js";
2
+ export declare const GENERATION_SESSION_STORE_ERROR_CODES: readonly ["INVALID_ID", "INVALID_SESSION", "INVALID_REVISION", "SESSION_ALREADY_EXISTS", "SESSION_NOT_FOUND", "SESSION_PUBLISHED", "REVISION_CONFLICT", "INVALID_TRANSITION", "LOCK_TIMEOUT", "INVALID_EVIDENCE_PATH", "INVALID_EVIDENCE", "EVIDENCE_NOT_FOUND", "EVIDENCE_ALREADY_EXISTS", "SESSION_NOT_PUBLISHABLE", "PUBLISH_DESTINATION_EXISTS", "IO_ERROR"];
3
+ export type GenerationSessionStoreErrorCode = typeof GENERATION_SESSION_STORE_ERROR_CODES[number];
4
+ export declare class GenerationSessionStoreError extends Error {
5
+ readonly code: GenerationSessionStoreErrorCode;
6
+ constructor(code: GenerationSessionStoreErrorCode, message: string, options?: ErrorOptions);
7
+ }
8
+ export interface GenerationEvidenceFile {
9
+ path: string;
10
+ contentBase64: string;
11
+ byteLength: number;
12
+ }
13
+ export interface GenerationSessionStore {
14
+ create: (session: GenerationSession) => Promise<void>;
15
+ read: (id: string) => Promise<GenerationSession>;
16
+ update: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
17
+ commitSnapshot: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
18
+ updateConfirmation: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
19
+ beginStep: (id: string, expectedRevision: number, inFlight: GenerationInFlight, approvedConfirmation?: PendingConfirmation) => Promise<GenerationSession>;
20
+ completeStep: (id: string, expectedRevision: number, expectedInFlight: GenerationInFlight, next: GenerationSession) => Promise<void>;
21
+ recover: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
22
+ beginVerification: (id: string, expectedRevision: number, attemptId: string) => Promise<GenerationSession>;
23
+ completeVerification: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
24
+ failVerification: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
25
+ markBundlePublishable: (id: string, expectedRevision: number, next: GenerationSession) => Promise<void>;
26
+ writeEvidence: (id: string, relativePath: string, value: unknown) => Promise<void>;
27
+ writeTextEvidence: (id: string, relativePath: string, value: string) => Promise<void>;
28
+ produceEvidence: (id: string, relativePath: string, produce: (temporaryPath: string) => Promise<void>) => Promise<void>;
29
+ readEvidence: (id: string, relativePath: string) => Promise<Buffer>;
30
+ listEvidence: (id: string) => Promise<readonly GenerationEvidenceFile[]>;
31
+ publish: (id: string) => Promise<string>;
32
+ }
@@ -0,0 +1,26 @@
1
+ export const GENERATION_SESSION_STORE_ERROR_CODES = [
2
+ "INVALID_ID",
3
+ "INVALID_SESSION",
4
+ "INVALID_REVISION",
5
+ "SESSION_ALREADY_EXISTS",
6
+ "SESSION_NOT_FOUND",
7
+ "SESSION_PUBLISHED",
8
+ "REVISION_CONFLICT",
9
+ "INVALID_TRANSITION",
10
+ "LOCK_TIMEOUT",
11
+ "INVALID_EVIDENCE_PATH",
12
+ "INVALID_EVIDENCE",
13
+ "EVIDENCE_NOT_FOUND",
14
+ "EVIDENCE_ALREADY_EXISTS",
15
+ "SESSION_NOT_PUBLISHABLE",
16
+ "PUBLISH_DESTINATION_EXISTS",
17
+ "IO_ERROR"
18
+ ];
19
+ export class GenerationSessionStoreError extends Error {
20
+ code;
21
+ constructor(code, message, options) {
22
+ super(message, options);
23
+ this.code = code;
24
+ this.name = "GenerationSessionStoreError";
25
+ }
26
+ }
@@ -0,0 +1,8 @@
1
+ import type { AgentId } from "../domain/init.js";
2
+ export interface InitPromptPort {
3
+ selectAgents: () => Promise<AgentId[]>;
4
+ }
5
+ export declare class InitPromptCancelledError extends Error {
6
+ readonly name = "InitPromptCancelledError";
7
+ constructor();
8
+ }
@@ -0,0 +1,6 @@
1
+ export class InitPromptCancelledError extends Error {
2
+ name = "InitPromptCancelledError";
3
+ constructor() {
4
+ super("Init prompt was cancelled");
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ import type { Journey } from "../domain/journey.js";
2
+ import type { ProjectBoundFileReader, ProjectBoundPath } from "./project-bound-file.js";
3
+ export interface JourneyWriterPort {
4
+ write: (outputPath: string, journey: Journey) => Promise<void>;
5
+ }
6
+ export interface ProjectBoundJourneyWriterPort extends JourneyWriterPort, ProjectBoundFileReader {
7
+ writeProjectBound: (path: ProjectBoundPath & {
8
+ journey: Journey;
9
+ }) => Promise<void>;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ export interface CommandSpec {
2
+ executable: string;
3
+ args: readonly string[];
4
+ cwd?: string | undefined;
5
+ env?: Readonly<Record<string, string | undefined>> | undefined;
6
+ timeoutMs?: number | undefined;
7
+ signal?: AbortSignal | undefined;
8
+ }
9
+ export interface CommandResult {
10
+ exitCode: number | null;
11
+ signal: NodeJS.Signals | null;
12
+ stdout: string;
13
+ stderr: string;
14
+ durationMs: number;
15
+ timedOut: boolean;
16
+ cancelled: boolean;
17
+ spawnError?: string | undefined;
18
+ }
19
+ export interface StreamHandlers {
20
+ onStdoutLine?(line: string): void;
21
+ onStderrLine?(line: string): void;
22
+ }
23
+ export interface RunningCommand {
24
+ /** Resolves undefined once the stream remains alive through startup. */
25
+ readonly started: Promise<CommandResult | undefined>;
26
+ readonly completion: Promise<CommandResult>;
27
+ stop: () => Promise<CommandResult>;
28
+ }
29
+ export interface ProcessRunner {
30
+ run: (spec: CommandSpec) => Promise<CommandResult>;
31
+ start: (spec: CommandSpec, handlers?: StreamHandlers) => RunningCommand;
32
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export interface ProjectBoundPath {
2
+ projectRoot: string;
3
+ authorityRoot: string;
4
+ outputPath: string;
5
+ }
6
+ export interface ProjectBoundFileReader {
7
+ readProjectBound: (path: ProjectBoundPath) => Promise<Buffer>;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ export type ProjectFileInspection = {
2
+ status: "rootNotFound";
3
+ } | {
4
+ status: "rootUnreadable";
5
+ } | {
6
+ status: "rootNotDirectory";
7
+ } | {
8
+ status: "notFound";
9
+ } | {
10
+ status: "unreadable";
11
+ } | {
12
+ status: "escape";
13
+ } | {
14
+ status: "changedIdentity";
15
+ resolvedRelativePath?: string;
16
+ } | {
17
+ status: "notFile";
18
+ resolvedRelativePath: string;
19
+ } | {
20
+ status: "tooLarge";
21
+ resolvedRelativePath: string;
22
+ } | {
23
+ status: "inspected";
24
+ resolvedRelativePath: string;
25
+ sha256: string;
26
+ };
27
+ export interface ProjectFileInspector {
28
+ inspectProjectFile: (input: {
29
+ projectRoot: string;
30
+ relativePath: string;
31
+ maximumBytes: number;
32
+ }) => Promise<ProjectFileInspection>;
33
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ export type RecorderAction = "click" | "longClick" | "inputText" | "swipe" | "scrollTo" | "back" | "wait" | "finish" | "cancel";
2
+ export type ScrollDecision = {
3
+ kind: "select";
4
+ id: string;
5
+ } | {
6
+ kind: "scrollMore";
7
+ } | {
8
+ kind: "cancel";
9
+ };
10
+ export interface RecorderTargetChoice {
11
+ id: string;
12
+ label: string;
13
+ }
14
+ export interface SwipeOptions {
15
+ distancePercent: number;
16
+ durationMs: number;
17
+ }
18
+ export interface RecorderPromptPort {
19
+ selectAction: () => Promise<RecorderAction>;
20
+ selectTarget: (choices: readonly RecorderTargetChoice[]) => Promise<string>;
21
+ inputText: () => Promise<string>;
22
+ selectSwipeDirection: () => Promise<"up" | "down" | "left" | "right">;
23
+ longClickDuration: () => Promise<number>;
24
+ swipeOptions: () => Promise<SwipeOptions>;
25
+ selectFallbackLabel: (annotatedScreenshotPath: string) => Promise<string | undefined>;
26
+ notifyFailure: (message: string) => Promise<void>;
27
+ selectScrollContainer: (choices: readonly RecorderTargetChoice[]) => Promise<string>;
28
+ scrollTargetDecision: (choices: readonly RecorderTargetChoice[]) => Promise<ScrollDecision>;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export interface SkillInstallResult {
2
+ readonly targetPath: string;
3
+ readonly skipped: boolean;
4
+ }
5
+ export interface SkillInstallerPort {
6
+ findPayload: () => Promise<string>;
7
+ installTo: (targetDir: string) => Promise<SkillInstallResult>;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "taphound",
3
+ "version": "0.2.0-dev.1",
4
+ "description": "Deterministic app journey recording and verification",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/caikaidev/TapHound.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/caikaidev/TapHound/issues"
12
+ },
13
+ "homepage": "https://github.com/caikaidev/TapHound#readme",
14
+ "keywords": [
15
+ "android",
16
+ "testing",
17
+ "regression-testing",
18
+ "journey",
19
+ "cli",
20
+ "ai-agent"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "tag": "dev"
25
+ },
26
+ "type": "module",
27
+ "bin": {
28
+ "taphound": "./dist/cli/main.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "assets/brand/taphound-mark.svg",
33
+ "assets/skills"
34
+ ],
35
+ "engines": {
36
+ "node": ">=22"
37
+ },
38
+ "scripts": {
39
+ "acceptance:device": "node scripts/acceptance-device.mjs",
40
+ "acceptance:generation": "node scripts/acceptance-generation.mjs",
41
+ "brand:render": "node scripts/render-brand-assets.mjs",
42
+ "build": "node scripts/build.mjs",
43
+ "coverage": "vitest run --coverage",
44
+ "lint": "eslint .",
45
+ "prepublishOnly": "npm test && npm run typecheck && npm run lint && npm run build",
46
+ "test": "vitest run",
47
+ "typecheck": "tsc -p tsconfig.json --noEmit"
48
+ },
49
+ "dependencies": {
50
+ "@inquirer/prompts": "^8.5.2",
51
+ "commander": "^15.0.0",
52
+ "zod": "^4.4.3"
53
+ },
54
+ "devDependencies": {
55
+ "@eslint/js": "^10.0.1",
56
+ "@types/node": "^26.1.1",
57
+ "@vitest/coverage-v8": "^4.1.10",
58
+ "eslint": "^10.7.0",
59
+ "sharp": "^0.35.3",
60
+ "typescript": "^6.0.3",
61
+ "typescript-eslint": "^8.64.0",
62
+ "vitest": "^4.1.10"
63
+ }
64
+ }