From 194339f71946ecf92b31d07b112d754cbe4fc1f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:14:28 +0000 Subject: [PATCH 1/7] =?UTF-8?q?feat(openspec):=20add-abapify-pilot=20spec?= =?UTF-8?q?=20=E2=80=94=20proposal,=20design,=20specs,=20tasks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/abapify/adt-cli/sessions/8c6e39c2-3ae6-4d7f-8cef-5ff4f1b5904d Co-authored-by: ThePlenkov <6381507+ThePlenkov@users.noreply.github.com> --- .../changes/add-abapify-pilot/.openspec.yaml | 2 + openspec/changes/add-abapify-pilot/design.md | 71 +++++++++++++++ .../changes/add-abapify-pilot/proposal.md | 31 +++++++ .../specs/abapify-pilot-agent/spec.md | 47 ++++++++++ .../specs/code-review-workflow/spec.md | 86 +++++++++++++++++++ openspec/changes/add-abapify-pilot/tasks.md | 59 +++++++++++++ 6 files changed, 296 insertions(+) create mode 100644 openspec/changes/add-abapify-pilot/.openspec.yaml create mode 100644 openspec/changes/add-abapify-pilot/design.md create mode 100644 openspec/changes/add-abapify-pilot/proposal.md create mode 100644 openspec/changes/add-abapify-pilot/specs/abapify-pilot-agent/spec.md create mode 100644 openspec/changes/add-abapify-pilot/specs/code-review-workflow/spec.md create mode 100644 openspec/changes/add-abapify-pilot/tasks.md diff --git a/openspec/changes/add-abapify-pilot/.openspec.yaml b/openspec/changes/add-abapify-pilot/.openspec.yaml new file mode 100644 index 00000000..0a064c1e --- /dev/null +++ b/openspec/changes/add-abapify-pilot/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-28 diff --git a/openspec/changes/add-abapify-pilot/design.md b/openspec/changes/add-abapify-pilot/design.md new file mode 100644 index 00000000..17180fc7 --- /dev/null +++ b/openspec/changes/add-abapify-pilot/design.md @@ -0,0 +1,71 @@ +## Context + +The repo already has a fully-featured `@abapify/adt-mcp` MCP server that exposes all ADT operations as MCP tools (`atc_run`, `cts_list_transports`, `list_package_objects`, etc.). Mastra AI provides a first-class `MCPClient` integration that lets an `Agent` call those tools natively. The goal is to compose these two pieces into a standalone `@abapify/adt-pilot` package without duplicating any ADT logic. + +## Goals / Non-Goals + +**Goals:** + +- New Nx publishable library `packages/adt-pilot` (`@abapify/adt-pilot`) +- Mastra `Harness` with a single agent mode: **review** +- `codeReviewWorkflow`: a `createWorkflow` workflow with two input paths: + - **Package mode** (`mode: 'package'`, `packageName: string`) — calls `list_package_objects` then `atc_run` per object + - **Transport mode** (`mode: 'transport'`, `transportNumber: string`) — calls `cts_get_transport` to resolve objects then `atc_run` per object +- Structured output: `CodeReviewReport` (`{ mode, target, findings[], summary }`). +- 100% Vitest test coverage via `@abapify/adt-fixtures` mock server. + +**Non-Goals:** + +- UI / TUI frontend (Harness is wired but no terminal renderer shipped in this change) +- LLM-generated natural-language summaries (the agent returns structured JSON findings; an LLM layer can be added later) +- Modifying any existing package + +## Decisions + +### 1. Package as a separate Nx library, not a sample + +**Decision**: `packages/adt-pilot` (publishable `@abapify/adt-pilot`). +**Rationale**: Makes the agent installable as a dependency, keeps it tested in CI like the rest of the monorepo, and follows the same package structure as `adt-mcp`. +**Alternative considered**: Add inside `samples/` — rejected because samples are not covered by the main CI test matrix and cannot be depended upon. + +### 2. Mastra MCP wiring via `MCPClient` in-process + +**Decision**: Use `@mastra/mcp`'s `MCPClient` with `InMemoryTransport` to connect to an in-process `@abapify/adt-mcp` server in tests; use `StdioServerParameters` to spawn the real binary in production. +**Rationale**: Avoids network overhead in tests; matches the pattern used by other Mastra-based agents. In production the pilot receives `baseUrl/username/password` as workflow input and passes them as tool arguments — no session state needed. +**Alternative considered**: Call ADT client directly without MCP — rejected because it would duplicate the tool layer and break the `adt-mcp ↔ adt-cli` parity invariant. + +### 3. Workflow over pure Agent for code review + +**Decision**: `codeReviewWorkflow` is a `createWorkflow` chain of typed steps rather than a freeform agent loop. +**Rationale**: Deterministic output schema, easier to test step-by-step, no LLM required for the core logic. The `Harness` wraps the workflow so an LLM can be plugged in later. +**Steps**: + +1. `resolveObjects` — call `list_package_objects` or `cts_get_transport` to get the list of objects +2. `runAtcChecks` — call `atc_run` for each object (sequential or parallel configurable) +3. `buildReport` — aggregate findings into `CodeReviewReport` + +### 4. `@mastra/mcp` for tool binding + +**Decision**: Add `@mastra/mcp` as a direct dependency (not `@mastra/core` alone) because `MCPClient` lives there. +**Rationale**: Keeps the dependency surface minimal; `@mastra/core` is a peer dep of `@mastra/mcp`. + +### 5. No LLM key required for tests + +**Decision**: Workflow steps execute deterministically (no LLM calls); tests use the fixture mock server. The Harness agent mode is configured with a placeholder model string and is not exercised in unit/integration tests. +**Rationale**: Keeps CI independent of external API keys. + +## Risks / Trade-offs + +- **`@mastra/core` API churn** — Mastra is on v1 with frequent releases. The workflow/step API used here (`createWorkflow`, `createStep`) is the stable "v-next" surface. Pin to a minor version range. + → Mitigation: snapshot the version in `package.json` as `"^1.28.0"`. + +- **Large transitive dependency graph** — `@mastra/core` pulls in `hono`, `ai`, `ajv`, etc. + → Mitigation: `adt-pilot` is a standalone package; it does not affect the bundle of `adt-cli` or `adt-mcp`. + +## Migration Plan + +No migration required — new package only. Deploy by publishing `@abapify/adt-pilot` independently. + +## Open Questions + +- Should `codeReviewWorkflow` run ATC checks in parallel per object? Default: sequential (safe); a `parallel` flag can be added later. diff --git a/openspec/changes/add-abapify-pilot/proposal.md b/openspec/changes/add-abapify-pilot/proposal.md new file mode 100644 index 00000000..3b6e8b9e --- /dev/null +++ b/openspec/changes/add-abapify-pilot/proposal.md @@ -0,0 +1,31 @@ +## Why + +AI-driven code review is a high-value workflow for SAP ABAP developers. By combining Mastra AI's agent framework with the existing `@abapify/adt-mcp` MCP server, we can ship a first-class agent — **abapify Pilot** — that runs ATC-based code review on an entire package hierarchy or a transport request without requiring any additional ADT integration code. + +## What Changes + +- New package `packages/adt-pilot` containing a Mastra `Harness`-based agent called **abapify Pilot**. +- The agent supports a **Code Review** workflow with two input modes: + - **Package mode** — given an ABAP package name, recursively reviews all objects including sub-packages. + - **Transport mode** — given a transport request number, reviews all objects included in that transport. +- The agent uses `@abapify/adt-mcp` tools (via Mastra `MCPClient`) as its only ADT integration layer. +- Full Vitest test suite with 100% coverage using `@abapify/adt-fixtures` payloads (mock ADT server pattern). + +## Capabilities + +### New Capabilities + +- `abapify-pilot-agent`: Mastra `Harness` + `Agent` setup for abapify Pilot, with connection config and MCP tool wiring. +- `code-review-workflow`: `createWorkflow` that accepts either `{ mode: 'package', packageName }` or `{ mode: 'transport', transportNumber }` as input and produces a structured code review report. + +### Modified Capabilities + + + +## Impact + +- **New package**: `packages/adt-pilot` (publishable, `@abapify/adt-pilot`) +- **Dependencies added**: `@mastra/core` (peer dep on `ai` SDK), `@abapify/adt-mcp`, `zod` +- **Dev dependencies**: `@abapify/adt-fixtures` (test fixtures), `vitest` +- **No breaking changes** to existing packages +- **Nx graph**: `adt-pilot` depends on `adt-mcp` and `adt-fixtures` (test only) diff --git a/openspec/changes/add-abapify-pilot/specs/abapify-pilot-agent/spec.md b/openspec/changes/add-abapify-pilot/specs/abapify-pilot-agent/spec.md new file mode 100644 index 00000000..2a29c194 --- /dev/null +++ b/openspec/changes/add-abapify-pilot/specs/abapify-pilot-agent/spec.md @@ -0,0 +1,47 @@ +## ADDED Requirements + +### Requirement: Package exports Harness and agent + +The `@abapify/adt-pilot` package SHALL export a `createAbapifyPilot(config)` factory that returns a configured Mastra `Harness` instance with the **review** mode wired to the `codeReviewWorkflow`. + +#### Scenario: Factory returns Harness + +- **WHEN** `createAbapifyPilot({ mcpServerParams, model })` is called +- **THEN** it returns a `Harness` instance with at least one mode whose `id` is `"review"` + +#### Scenario: Harness mode agent is configured + +- **WHEN** the returned Harness is initialised +- **THEN** the `review` mode agent has the MCP tools from `@abapify/adt-mcp` available + +### Requirement: Package exports workflow + +The `@abapify/adt-pilot` package SHALL export `codeReviewWorkflow` so it can be used outside the Harness context. + +#### Scenario: Direct workflow import + +- **WHEN** `import { codeReviewWorkflow } from '@abapify/adt-pilot'` is used +- **THEN** `codeReviewWorkflow` is a Mastra `Workflow` instance with `inputSchema` and `outputSchema` + +### Requirement: Connection config passed as workflow input + +The workflow and agent SHALL NOT store SAP connection credentials in package-level state. Credentials (`baseUrl`, `username`, `password`, `client?`) SHALL be passed as part of every workflow run's input. + +#### Scenario: Credentials forwarded to MCP tools + +- **WHEN** the workflow is executed with `{ baseUrl, username, password, mode, ... }` +- **THEN** all MCP tool calls include those credentials as arguments + +### Requirement: Package is a publishable Nx library + +The package SHALL be located at `packages/adt-pilot`, use the name `@abapify/adt-pilot`, and follow the same tsdown build + Vitest test setup used by other packages in the monorepo. + +#### Scenario: Build succeeds + +- **WHEN** `bunx nx build adt-pilot` is run +- **THEN** the build exits 0 and `packages/adt-pilot/dist/` is populated with `.mjs` and `.d.mts` files + +#### Scenario: Tests pass + +- **WHEN** `bunx nx test adt-pilot` is run +- **THEN** all tests pass and coverage reaches 100% of statements/branches diff --git a/openspec/changes/add-abapify-pilot/specs/code-review-workflow/spec.md b/openspec/changes/add-abapify-pilot/specs/code-review-workflow/spec.md new file mode 100644 index 00000000..64888782 --- /dev/null +++ b/openspec/changes/add-abapify-pilot/specs/code-review-workflow/spec.md @@ -0,0 +1,86 @@ +## ADDED Requirements + +### Requirement: Workflow accepts package mode input + +The `codeReviewWorkflow` SHALL accept `{ mode: 'package', packageName: string, baseUrl: string, username: string, password: string, client?: string }` as input and produce a `CodeReviewReport`. + +#### Scenario: Package mode resolves objects + +- **WHEN** the workflow is executed with `mode: 'package'` and a valid `packageName` +- **THEN** the `resolveObjects` step calls the `list_package_objects` MCP tool with that package name +- **THEN** the step result contains an array of ABAP object URIs from that package and its sub-packages + +#### Scenario: Package mode runs ATC on resolved objects + +- **WHEN** `resolveObjects` returns a non-empty object list +- **THEN** the `runAtcChecks` step calls `atc_run` for each object URI +- **THEN** the `buildReport` step aggregates all findings into a `CodeReviewReport` + +#### Scenario: Empty package produces empty report + +- **WHEN** `list_package_objects` returns zero objects +- **THEN** the workflow completes successfully with `findings: []` and `summary.totalFindings: 0` + +### Requirement: Workflow accepts transport mode input + +The `codeReviewWorkflow` SHALL accept `{ mode: 'transport', transportNumber: string, baseUrl: string, username: string, password: string, client?: string }` as input and produce a `CodeReviewReport`. + +#### Scenario: Transport mode resolves objects + +- **WHEN** the workflow is executed with `mode: 'transport'` and a valid `transportNumber` +- **THEN** the `resolveObjects` step calls the `cts_get_transport` MCP tool with that transport number +- **THEN** the step result contains the list of ABAP object URIs from that transport + +#### Scenario: Transport mode runs ATC on resolved objects + +- **WHEN** `resolveObjects` returns a non-empty object list +- **THEN** the `runAtcChecks` step calls `atc_run` for each resolved object URI +- **THEN** the `buildReport` step aggregates all findings into a `CodeReviewReport` + +### Requirement: CodeReviewReport output schema + +The workflow output SHALL conform to `CodeReviewReport`: + +```typescript +{ + mode: 'package' | 'transport'; + target: string; // packageName or transportNumber + objects: string[]; // resolved object URIs + findings: AtcFinding[]; // all ATC findings across objects + summary: { + totalObjects: number; + totalFindings: number; + bySeverity: Record; + }; +} +``` + +#### Scenario: Report structure is correct + +- **WHEN** the workflow completes +- **THEN** the output matches the `CodeReviewReport` schema +- **THEN** `summary.totalObjects` equals the number of objects resolved +- **THEN** `summary.totalFindings` equals the total number of findings across all objects + +### Requirement: Workflow is three steps + +The `codeReviewWorkflow` SHALL consist of exactly three named steps executed in sequence: + +1. `resolveObjects` +2. `runAtcChecks` +3. `buildReport` + +#### Scenario: Step chain is deterministic + +- **WHEN** the workflow is committed (`.commit()`) +- **THEN** the execution order is always resolveObjects → runAtcChecks → buildReport + +### Requirement: Workflow handles MCP tool errors gracefully + +If an MCP tool call fails for an individual object, the workflow SHALL continue processing remaining objects and include an error entry in `findings` for the failed object. + +#### Scenario: Partial ATC failure + +- **WHEN** `atc_run` fails for one object but succeeds for others +- **THEN** the failed object produces a finding with `priority: 'error'` and a `description` containing the error message +- **THEN** the workflow does not throw; it returns a completed `CodeReviewReport` diff --git a/openspec/changes/add-abapify-pilot/tasks.md b/openspec/changes/add-abapify-pilot/tasks.md new file mode 100644 index 00000000..ab6b8c9f --- /dev/null +++ b/openspec/changes/add-abapify-pilot/tasks.md @@ -0,0 +1,59 @@ +## 1. Scaffold `packages/adt-pilot` + +- [ ] 1.1 Create `packages/adt-pilot/` directory with `package.json` (`@abapify/adt-pilot`, version `0.3.6`, ESM, `exports: { ".": "./dist/index.mjs" }`) +- [ ] 1.2 Add `tsconfig.json` and `tsdown.config.ts` mirroring `packages/adt-mcp` +- [ ] 1.3 Add `project.json` for Nx with `build`, `test`, `lint`, `typecheck` targets +- [ ] 1.4 Add `vitest.config.ts` using the workspace preset +- [ ] 1.5 Create `src/index.ts` (empty barrel — fills in later tasks) + +## 2. Add dependencies + +- [ ] 2.1 Add `@mastra/core`, `@mastra/mcp` as runtime dependencies in `packages/adt-pilot/package.json` +- [ ] 2.2 Add `@abapify/adt-mcp` as a runtime dependency +- [ ] 2.3 Add `zod` as a runtime dependency +- [ ] 2.4 Add `@abapify/adt-fixtures` as a dev dependency +- [ ] 2.5 Run `bun install` to update the lockfile + +## 3. Implement types (`src/types.ts`) + +- [ ] 3.1 Define `ConnectionParams` (`baseUrl`, `username`, `password`, `client?`) +- [ ] 3.2 Define `AtcFinding` (`objectUri`, `priority`, `description`, `category?`, `checkName?`, `location?`) +- [ ] 3.3 Define `CodeReviewReport` (`mode`, `target`, `objects`, `findings`, `summary`) +- [ ] 3.4 Export all types from `src/index.ts` + +## 4. Implement `codeReviewWorkflow` (`src/workflow.ts`) + +- [ ] 4.1 Create `resolveObjects` step: accepts `{ mode, packageName?, transportNumber?, ...conn }`, calls `list_package_objects` or `cts_get_transport` via MCP client, returns `{ objects: string[] }` +- [ ] 4.2 Create `runAtcChecks` step: accepts `{ objects, ...conn }`, calls `atc_run` per object, returns `{ rawResults: AtcRunResult[] }` +- [ ] 4.3 Create `buildReport` step: accepts `{ mode, target, objects, rawResults }`, maps to `CodeReviewReport` +- [ ] 4.4 Wire steps with `createWorkflow(...).then(resolveObjects).then(runAtcChecks).then(buildReport).commit()` +- [ ] 4.5 Export `codeReviewWorkflow` from `src/index.ts` +- [ ] 4.6 Define and export Zod schemas for input and output + +## 5. Implement MCP client factory (`src/mcp-client.ts`) + +- [ ] 5.1 Create `createMcpToolSet(serverParams)` function using `@mastra/mcp`'s `MCPClient` that returns the tool set from the `@abapify/adt-mcp` server +- [ ] 5.2 Export `createMcpToolSet` from `src/index.ts` + +## 6. Implement Harness agent (`src/agent.ts` + `src/harness.ts`) + +- [ ] 6.1 Create `reviewAgent`: Mastra `Agent` with `id: 'review'`, instructions describing code review purpose, and MCP tools +- [ ] 6.2 Create `createAbapifyPilot(config)` factory returning a `Harness` with mode `{ id: 'review', default: true, agent: reviewAgent }` +- [ ] 6.3 Export `createAbapifyPilot` from `src/index.ts` + +## 7. Write tests (`tests/`) + +- [ ] 7.1 Create `tests/workflow.test.ts`: package mode happy path using `@abapify/adt-fixtures` mock server +- [ ] 7.2 Create `tests/workflow.test.ts` (continued): transport mode happy path +- [ ] 7.3 Create `tests/workflow.test.ts` (continued): empty package → empty report +- [ ] 7.4 Create `tests/workflow.test.ts` (continued): partial ATC failure → report with error finding +- [ ] 7.5 Create `tests/types.test.ts`: Zod schema validation for `CodeReviewReport` +- [ ] 7.6 Create `tests/mcp-client.test.ts`: mock MCPClient connect/disconnect lifecycle + +## 8. Verify & finalize + +- [ ] 8.1 Run `bunx nx build adt-pilot` — confirm build succeeds +- [ ] 8.2 Run `bunx nx test adt-pilot` — confirm all tests pass +- [ ] 8.3 Run `bunx nx lint adt-pilot` — confirm no lint errors +- [ ] 8.4 Run `bunx nx typecheck adt-pilot` — confirm no type errors +- [ ] 8.5 Run `bunx nx format:write` — format all changed files From 7713a2deb7ddb1b5d4f4967c90572649caa1fe04 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:39:18 +0000 Subject: [PATCH 2/7] =?UTF-8?q?feat(adt-pilot):=20scaffold=20package=20?= =?UTF-8?q?=E2=80=94=20source,=20tests,=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/abapify/adt-cli/sessions/8c6e39c2-3ae6-4d7f-8cef-5ff4f1b5904d Co-authored-by: ThePlenkov <6381507+ThePlenkov@users.noreply.github.com> --- packages/adt-pilot/package.json | 48 +++ packages/adt-pilot/project.json | 13 + packages/adt-pilot/src/agent.ts | 54 +++ packages/adt-pilot/src/harness.ts | 63 +++ packages/adt-pilot/src/index.ts | 59 +++ packages/adt-pilot/src/mcp-client.ts | 66 ++++ packages/adt-pilot/src/types.ts | 60 +++ packages/adt-pilot/src/workflow.ts | 401 ++++++++++++++++++++ packages/adt-pilot/tests/mcp-client.test.ts | 82 ++++ packages/adt-pilot/tests/types.test.ts | 127 +++++++ packages/adt-pilot/tests/workflow.test.ts | 292 ++++++++++++++ packages/adt-pilot/tsconfig.json | 10 + packages/adt-pilot/tsconfig.lib.json | 11 + packages/adt-pilot/tsdown.config.ts | 10 + packages/adt-pilot/vitest.config.ts | 8 + 15 files changed, 1304 insertions(+) create mode 100644 packages/adt-pilot/package.json create mode 100644 packages/adt-pilot/project.json create mode 100644 packages/adt-pilot/src/agent.ts create mode 100644 packages/adt-pilot/src/harness.ts create mode 100644 packages/adt-pilot/src/index.ts create mode 100644 packages/adt-pilot/src/mcp-client.ts create mode 100644 packages/adt-pilot/src/types.ts create mode 100644 packages/adt-pilot/src/workflow.ts create mode 100644 packages/adt-pilot/tests/mcp-client.test.ts create mode 100644 packages/adt-pilot/tests/types.test.ts create mode 100644 packages/adt-pilot/tests/workflow.test.ts create mode 100644 packages/adt-pilot/tsconfig.json create mode 100644 packages/adt-pilot/tsconfig.lib.json create mode 100644 packages/adt-pilot/tsdown.config.ts create mode 100644 packages/adt-pilot/vitest.config.ts diff --git a/packages/adt-pilot/package.json b/packages/adt-pilot/package.json new file mode 100644 index 00000000..5105ba69 --- /dev/null +++ b/packages/adt-pilot/package.json @@ -0,0 +1,48 @@ +{ + "name": "@abapify/adt-pilot", + "version": "0.3.6", + "description": "abapify Pilot — Mastra AI agent for ABAP code review via ADT MCP", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "type": "module", + "types": "./dist/index.d.mts", + "exports": { + ".": "./dist/index.mjs", + "./package.json": "./package.json" + }, + "files": [ + "dist", + "README.md" + ], + "keywords": [ + "sap", + "adt", + "abap", + "mastra", + "ai", + "agent", + "code-review", + "atc" + ], + "dependencies": { + "@mastra/core": "^1.28.0", + "@mastra/mcp": "^1.5.2", + "@abapify/adt-mcp": "0.3.6", + "zod": "^3.24.0" + }, + "devDependencies": { + "@abapify/adt-fixtures": "0.3.6", + "@modelcontextprotocol/sdk": "^1.27.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/abapify/adt-cli.git", + "directory": "packages/adt-pilot" + }, + "homepage": "https://github.com/abapify/adt-cli/tree/main/packages/adt-pilot#readme", + "bugs": { + "url": "https://github.com/abapify/adt-cli/issues" + } +} diff --git a/packages/adt-pilot/project.json b/packages/adt-pilot/project.json new file mode 100644 index 00000000..28d59f3e --- /dev/null +++ b/packages/adt-pilot/project.json @@ -0,0 +1,13 @@ +{ + "name": "adt-pilot", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/adt-pilot/src", + "projectType": "library", + "release": { + "version": { + "manifestRootsToUpdate": ["{projectRoot}"] + } + }, + "tags": ["scope:adt", "type:agent"], + "targets": {} +} diff --git a/packages/adt-pilot/src/agent.ts b/packages/adt-pilot/src/agent.ts new file mode 100644 index 00000000..ce9e133d --- /dev/null +++ b/packages/adt-pilot/src/agent.ts @@ -0,0 +1,54 @@ +/** + * abapify Pilot – Mastra Agent + * + * Creates the `review` agent that powers the Code Review mode in the Harness. + * The agent is wired with MCP tools from `@abapify/adt-mcp` so it can + * directly call any ADT operation. + */ + +import { Agent } from '@mastra/core/agent'; +import type { ToolsInput } from '@mastra/core/agent'; + +/** Configuration for the review agent */ +export interface ReviewAgentConfig { + /** + * Model identifier (e.g. "openai/gpt-4o", "anthropic/claude-3-5-sonnet"). + * Required by Mastra Agent but not exercised in the Code Review workflow + * (the workflow is deterministic and does not call the LLM). + */ + model: string; + /** MCP tools loaded from `@abapify/adt-mcp` */ + tools?: ToolsInput; +} + +/** System instructions for the review agent */ +const REVIEW_INSTRUCTIONS = ` +You are abapify Pilot, an expert ABAP code review assistant powered by SAP ADT. + +Your task is to analyse ABAP code quality using the ATC (ABAP Test Cockpit) framework. +You have access to ADT MCP tools that let you: +- List objects in a package (list_package_objects) +- Get details of a transport request (cts_get_transport) +- Run ATC checks on objects or transports (atc_run) + +When asked to review a package, use list_package_objects to enumerate the objects +and atc_run to run quality checks. For transports, use cts_get_transport first, +then atc_run on the transport URI. + +Always present findings grouped by severity and provide clear remediation guidance. +`.trim(); + +/** + * Create the Mastra `Agent` for the abapify Pilot review mode. + * + * @param config - Agent configuration (model + optional pre-loaded tools) + */ +export function createReviewAgent(config: ReviewAgentConfig): Agent { + return new Agent({ + id: 'review', + name: 'abapify Pilot – Review', + instructions: REVIEW_INSTRUCTIONS, + model: config.model, + ...(config.tools ? { tools: config.tools } : {}), + }); +} diff --git a/packages/adt-pilot/src/harness.ts b/packages/adt-pilot/src/harness.ts new file mode 100644 index 00000000..0649ac9f --- /dev/null +++ b/packages/adt-pilot/src/harness.ts @@ -0,0 +1,63 @@ +/** + * abapify Pilot – Harness factory + * + * Creates a Mastra `Harness` with the review agent mode. + * The Harness is the top-level orchestrator that a UI (TUI, web) can drive. + * + * @example + * ```typescript + * const pilot = createAbapifyPilot({ + * model: 'openai/gpt-4o', + * mcpTools: await mcpClient.listTools(), + * }); + * await pilot.init(); + * await pilot.selectOrCreateThread(); + * await pilot.sendMessage({ content: 'Review package ZPACKAGE on http://my-sap:8000' }); + * ``` + */ + +import { Harness } from '@mastra/core/harness'; +import type { ToolsInput } from '@mastra/core/agent'; +import { createReviewAgent } from './agent.js'; + +/** Configuration for the abapify Pilot Harness */ +export interface AbapifyPilotConfig { + /** + * Model identifier forwarded to the review agent. + * (e.g. "openai/gpt-4o", "anthropic/claude-3-5-sonnet") + */ + model: string; + /** + * MCP tools to inject into the agent. + * Load them from `@mastra/mcp`'s `MCPClient.listTools()` or pass an + * in-process tool set for testing. + */ + mcpTools?: ToolsInput; +} + +/** + * Create the abapify Pilot `Harness`. + * + * The Harness ships with a single **review** mode wired to the review agent. + * Additional modes (plan, fix, …) can be added here in future iterations. + * + * @param config - Harness configuration + */ +export function createAbapifyPilot(config: AbapifyPilotConfig): Harness { + const reviewAgent = createReviewAgent({ + model: config.model, + tools: config.mcpTools, + }); + + return new Harness({ + id: 'abapify-pilot', + modes: [ + { + id: 'review', + name: 'Code Review', + default: true, + agent: reviewAgent, + }, + ], + }); +} diff --git a/packages/adt-pilot/src/index.ts b/packages/adt-pilot/src/index.ts new file mode 100644 index 00000000..cf90e854 --- /dev/null +++ b/packages/adt-pilot/src/index.ts @@ -0,0 +1,59 @@ +/** + * @abapify/adt-pilot – abapify Pilot + * + * Mastra AI agent for ABAP code review via ADT MCP. + * + * @example + * ```typescript + * import { createAbapifyPilot, createCodeReviewWorkflow, connectMcpClient } from '@abapify/adt-pilot'; + * import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; + * import { createMcpServer } from '@abapify/adt-mcp'; + * + * // Set up MCP server + client + * const server = createMcpServer(); + * const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + * await server.connect(serverTransport); + * const { callTool } = await connectMcpClient(clientTransport); + * + * // Create and run the workflow + * const workflow = createCodeReviewWorkflow(callTool); + * const run = workflow.createRun(); + * const result = await run.start({ + * inputData: { + * mode: 'package', + * packageName: 'ZPACKAGE', + * baseUrl: 'http://sap:8000', + * username: 'DEVELOPER', + * password: 'secret', + * }, + * }); + * console.log(result.result); // CodeReviewReport + * ``` + */ + +// Types +export type { + ConnectionParams, + AtcFinding, + CodeReviewReport, + McpToolCaller, +} from './types.js'; + +// Workflow +export { + createCodeReviewWorkflow, + codeReviewInputSchema, + codeReviewOutputSchema, +} from './workflow.js'; +export type { CodeReviewInput } from './workflow.js'; + +// MCP client factory +export { createMcpToolCaller, connectMcpClient } from './mcp-client.js'; + +// Agent +export { createReviewAgent } from './agent.js'; +export type { ReviewAgentConfig } from './agent.js'; + +// Harness +export { createAbapifyPilot } from './harness.js'; +export type { AbapifyPilotConfig } from './harness.js'; diff --git a/packages/adt-pilot/src/mcp-client.ts b/packages/adt-pilot/src/mcp-client.ts new file mode 100644 index 00000000..90d5f224 --- /dev/null +++ b/packages/adt-pilot/src/mcp-client.ts @@ -0,0 +1,66 @@ +/** + * MCP client factory + * + * Wraps `@modelcontextprotocol/sdk`'s `Client` in the `McpToolCaller` + * interface used by `createCodeReviewWorkflow`. + * + * In production the client connects to the `adt-mcp` binary via stdio or + * HTTP. In tests an in-process `InMemoryTransport` is used instead. + */ + +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; +import type { McpToolCaller } from './types.js'; + +/** + * Wrap an already-connected MCP SDK `Client` as a `McpToolCaller`. + * + * The caller parses the first `text` content item of the tool response as + * JSON. If the tool returns `isError: true`, an `Error` is thrown. + * + * @param client - A connected `@modelcontextprotocol/sdk` `Client` + */ +export function createMcpToolCaller(client: Client): McpToolCaller { + return async (toolName: string, args: Record) => { + const result = await client.callTool({ + name: toolName, + arguments: args, + }); + + const content = result.content as Array<{ type: string; text: string }>; + const text = content[0]?.text ?? ''; + + // Propagate tool-level errors as thrown exceptions so the workflow can + // catch them and record error findings. + if (result.isError) { + throw new Error(text || `MCP tool "${toolName}" returned an error`); + } + + try { + return JSON.parse(text) as unknown; + } catch { + return text; + } + }; +} + +/** + * Connect a Mastra MCP `Client` to the given transport and return a + * `McpToolCaller` backed by that connection. + * + * The caller is responsible for closing the transport when done. + * + * @example + * ```typescript + * const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + * await mcpServer.connect(serverTransport); + * const callTool = await connectMcpClient(clientTransport); + * ``` + */ +export async function connectMcpClient( + transport: Transport, +): Promise<{ callTool: McpToolCaller; client: Client }> { + const client = new Client({ name: 'adt-pilot', version: '0.1.0' }); + await client.connect(transport); + return { callTool: createMcpToolCaller(client), client }; +} diff --git a/packages/adt-pilot/src/types.ts b/packages/adt-pilot/src/types.ts new file mode 100644 index 00000000..adaf0935 --- /dev/null +++ b/packages/adt-pilot/src/types.ts @@ -0,0 +1,60 @@ +/** + * abapify Pilot – types + * + * Shared types for the Code Review workflow and the Harness agent. + */ + +/** SAP ADT connection parameters – forwarded as tool arguments on every MCP call. */ +export interface ConnectionParams { + baseUrl: string; + username: string; + password: string; + /** SAP client number (e.g. "100"). Optional. */ + client?: string; +} + +/** A single ATC finding returned by the Code Review workflow. */ +export interface AtcFinding { + /** ADT URI of the object that triggered the finding */ + objectUri: string; + /** Severity / priority (e.g. "1", "2", "warning", "error") */ + priority: string; + /** Human-readable description of the finding */ + description: string; + /** ATC check category (e.g. "PERFORMANCE", "SECURITY") */ + category?: string; + /** Name of the ATC check that raised the finding */ + checkName?: string; + /** Optional source location within the object */ + location?: string; +} + +/** Structured output of the Code Review workflow. */ +export interface CodeReviewReport { + /** Input mode used for this review */ + mode: 'package' | 'transport'; + /** The reviewed target (package name or transport number) */ + target: string; + /** Resolved ADT object URIs that were checked */ + objects: string[]; + /** All ATC findings across all objects */ + findings: AtcFinding[]; + /** Aggregated statistics */ + summary: { + totalObjects: number; + totalFindings: number; + /** Findings grouped by priority/severity */ + bySeverity: Record; + }; +} + +/** + * Minimal interface for calling an MCP tool and getting its result. + * + * In production this is implemented by wrapping `@modelcontextprotocol/sdk`'s + * `Client.callTool()`. In tests it is backed by an in-process mock. + */ +export type McpToolCaller = ( + toolName: string, + args: Record, +) => Promise; diff --git a/packages/adt-pilot/src/workflow.ts b/packages/adt-pilot/src/workflow.ts new file mode 100644 index 00000000..44fc900c --- /dev/null +++ b/packages/adt-pilot/src/workflow.ts @@ -0,0 +1,401 @@ +/** + * Code Review Workflow + * + * A Mastra workflow that runs ATC-based code review on an ABAP package + * hierarchy or a transport request. + * + * Usage: + * // Package mode + * const workflow = createCodeReviewWorkflow(callTool); + * const result = await workflow.execute({ + * mode: 'package', packageName: 'ZPACKAGE', + * baseUrl: '...', username: '...', password: '...' + * }); + * + * // Transport mode + * const result = await workflow.execute({ + * mode: 'transport', transportNumber: 'DEVK900001', + * baseUrl: '...', username: '...', password: '...' + * }); + */ + +import { createWorkflow, createStep } from '@mastra/core/workflows'; +import { z } from 'zod'; +import type { AtcFinding, CodeReviewReport, McpToolCaller } from './types.js'; + +// --------------------------------------------------------------------------- +// Zod schemas +// --------------------------------------------------------------------------- + +const connectionSchema = z.object({ + baseUrl: z.string().url(), + username: z.string(), + password: z.string(), + client: z.string().optional(), +}); + +/** Workflow input schema */ +export const codeReviewInputSchema = z.discriminatedUnion('mode', [ + connectionSchema.extend({ + mode: z.literal('package'), + packageName: z.string(), + }), + connectionSchema.extend({ + mode: z.literal('transport'), + transportNumber: z.string(), + }), +]); + +export type CodeReviewInput = z.infer; + +/** Workflow output schema */ +export const codeReviewOutputSchema = z.object({ + mode: z.enum(['package', 'transport']), + target: z.string(), + objects: z.array(z.string()), + findings: z.array( + z.object({ + objectUri: z.string(), + priority: z.string(), + description: z.string(), + category: z.string().optional(), + checkName: z.string().optional(), + location: z.string().optional(), + }), + ), + summary: z.object({ + totalObjects: z.number(), + totalFindings: z.number(), + bySeverity: z.record(z.string(), z.number()), + }), +}); + +// --------------------------------------------------------------------------- +// Step 1 – resolveObjects +// --------------------------------------------------------------------------- + +/** + * Resolve the list of object URIs to check. + * + * - Package mode: calls `list_package_objects` and extracts URIs + * - Transport mode: calls `cts_get_transport` to validate, then uses the + * transport URI so ATC can run on the whole transport in one call + */ +const resolveObjectsInputSchema = z.object({ + mode: z.enum(['package', 'transport']), + packageName: z.string().optional(), + transportNumber: z.string().optional(), + baseUrl: z.string(), + username: z.string(), + password: z.string(), + client: z.string().optional(), +}); + +const resolveObjectsOutputSchema = z.object({ + mode: z.enum(['package', 'transport']), + target: z.string(), + objects: z.array(z.string()), + baseUrl: z.string(), + username: z.string(), + password: z.string(), + client: z.string().optional(), +}); + +function createResolveObjectsStep(callTool: McpToolCaller) { + return createStep({ + id: 'resolveObjects', + description: 'Resolve ABAP object URIs from package or transport', + inputSchema: resolveObjectsInputSchema, + outputSchema: resolveObjectsOutputSchema, + execute: async ({ inputData }) => { + const conn = { + baseUrl: inputData.baseUrl, + username: inputData.username, + password: inputData.password, + ...(inputData.client != null ? { client: inputData.client } : {}), + }; + + if (inputData.mode === 'package') { + const packageName = inputData.packageName ?? ''; + const result = (await callTool('list_package_objects', { + ...conn, + packageName, + })) as Record | null; + + const rawObjects = + result && Array.isArray(result.objects) ? result.objects : []; + + const objects = rawObjects + .map((o: unknown) => { + const obj = o as Record; + return typeof obj.uri === 'string' ? obj.uri : null; + }) + .filter((uri): uri is string => uri !== null); + + return { + mode: 'package' as const, + target: packageName, + objects, + ...conn, + }; + } else { + // Transport mode: validate transport exists, then use transport URI + const transportNumber = inputData.transportNumber ?? ''; + await callTool('cts_get_transport', { + ...conn, + transport: transportNumber, + }); + + const transportUri = `/sap/bc/adt/cts/transportrequests/${transportNumber}`; + return { + mode: 'transport' as const, + target: transportNumber, + objects: [transportUri], + ...conn, + }; + } + }, + }); +} + +// --------------------------------------------------------------------------- +// Step 2 – runAtcChecks +// --------------------------------------------------------------------------- + +const atcResultSchema = z.object({ + objectUri: z.string(), + status: z.enum(['success', 'error']), + worklist: z.unknown().optional(), + error: z.string().optional(), +}); + +const runAtcChecksInputSchema = z.object({ + mode: z.enum(['package', 'transport']), + target: z.string(), + objects: z.array(z.string()), + baseUrl: z.string(), + username: z.string(), + password: z.string(), + client: z.string().optional(), +}); + +const runAtcChecksOutputSchema = z.object({ + mode: z.enum(['package', 'transport']), + target: z.string(), + objects: z.array(z.string()), + atcResults: z.array(atcResultSchema), +}); + +function createRunAtcChecksStep(callTool: McpToolCaller) { + return createStep({ + id: 'runAtcChecks', + description: 'Run ATC checks on each resolved object URI', + inputSchema: runAtcChecksInputSchema, + outputSchema: runAtcChecksOutputSchema, + execute: async ({ inputData }) => { + const conn = { + baseUrl: inputData.baseUrl, + username: inputData.username, + password: inputData.password, + ...(inputData.client != null ? { client: inputData.client } : {}), + }; + + const atcResults = []; + + for (const objectUri of inputData.objects) { + try { + const result = (await callTool('atc_run', { + ...conn, + objectUri, + })) as Record | null; + + atcResults.push({ + objectUri, + status: 'success' as const, + worklist: result?.worklist, + }); + } catch (error) { + atcResults.push({ + objectUri, + status: 'error' as const, + error: error instanceof Error ? error.message : String(error), + }); + } + } + + return { + mode: inputData.mode, + target: inputData.target, + objects: inputData.objects, + atcResults, + }; + }, + }); +} + +// --------------------------------------------------------------------------- +// Step 3 – buildReport +// --------------------------------------------------------------------------- + +const buildReportInputSchema = z.object({ + mode: z.enum(['package', 'transport']), + target: z.string(), + objects: z.array(z.string()), + atcResults: z.array(atcResultSchema), +}); + +function createBuildReportStep() { + return createStep({ + id: 'buildReport', + description: 'Aggregate ATC results into a CodeReviewReport', + inputSchema: buildReportInputSchema, + outputSchema: codeReviewOutputSchema, + execute: async ({ inputData }) => { + const findings: AtcFinding[] = []; + + for (const result of inputData.atcResults) { + if (result.status === 'error') { + // Error finding – insert a synthetic finding so the caller knows + findings.push({ + objectUri: result.objectUri, + priority: 'error', + description: result.error ?? 'ATC check failed', + }); + continue; + } + + // Parse worklist findings + const extracted = extractFindings(result.objectUri, result.worklist); + findings.push(...extracted); + } + + // Severity summary + const bySeverity: Record = {}; + for (const f of findings) { + bySeverity[f.priority] = (bySeverity[f.priority] ?? 0) + 1; + } + + const report: CodeReviewReport = { + mode: inputData.mode, + target: inputData.target, + objects: inputData.objects, + findings, + summary: { + totalObjects: inputData.objects.length, + totalFindings: findings.length, + bySeverity, + }, + }; + + return report; + }, + }); +} + +// --------------------------------------------------------------------------- +// Worklist parsing helper +// --------------------------------------------------------------------------- + +/** + * Safely extract AtcFinding[] from a raw worklist response. + * + * The worklist may be shaped as: + * { worklist: { objects: { object: [...] } } } ← successful run + * { worklistRun: { ... } } ← run-only response (no findings yet) + * null / undefined ← no result + * + * We do best-effort parsing and return an empty array on failure. + */ +function extractFindings( + fallbackObjectUri: string, + worklist: unknown, +): AtcFinding[] { + if (!worklist || typeof worklist !== 'object') return []; + + const wl = worklist as Record; + + // Normalise: unwrap top-level "worklist" key if present + const inner = + wl.worklist && typeof wl.worklist === 'object' + ? (wl.worklist as Record) + : wl; + + const objectsContainer = inner.objects as Record | undefined; + if (!objectsContainer) return []; + + const rawObjects = objectsContainer.object; + const objectArray = Array.isArray(rawObjects) + ? rawObjects + : rawObjects != null + ? [rawObjects] + : []; + + const findings: AtcFinding[] = []; + + for (const obj of objectArray) { + const o = obj as Record; + const objectUri = typeof o.uri === 'string' ? o.uri : fallbackObjectUri; + + const findingsContainer = o.findings as Record | undefined; + if (!findingsContainer) continue; + + const rawFindings = findingsContainer.finding; + const findingArray = Array.isArray(rawFindings) + ? rawFindings + : rawFindings != null + ? [rawFindings] + : []; + + for (const f of findingArray) { + const finding = f as Record; + findings.push({ + objectUri, + priority: String(finding.priority ?? 'unknown'), + description: String( + finding.messageTitle ?? + finding.checkTitle ?? + finding.description ?? + '', + ), + category: + typeof finding.checkTitle === 'string' + ? finding.checkTitle + : undefined, + checkName: + typeof finding.checkId === 'string' ? finding.checkId : undefined, + location: + typeof finding.location === 'string' ? finding.location : undefined, + }); + } + } + + return findings; +} + +// --------------------------------------------------------------------------- +// Factory +// --------------------------------------------------------------------------- + +/** + * Create the Code Review workflow, bound to the given MCP tool caller. + * + * @param callTool Function that calls a named MCP tool and returns the parsed + * JSON response. + */ +export function createCodeReviewWorkflow(callTool: McpToolCaller) { + const resolveObjectsStep = createResolveObjectsStep(callTool); + const runAtcChecksStep = createRunAtcChecksStep(callTool); + const buildReportStep = createBuildReportStep(); + + return createWorkflow({ + id: 'code-review', + description: + 'Run ATC-based code review on a package hierarchy or transport request', + inputSchema: resolveObjectsInputSchema, + outputSchema: codeReviewOutputSchema, + }) + .then(resolveObjectsStep) + .then(runAtcChecksStep) + .then(buildReportStep) + .commit(); +} diff --git a/packages/adt-pilot/tests/mcp-client.test.ts b/packages/adt-pilot/tests/mcp-client.test.ts new file mode 100644 index 00000000..0cb0c3cf --- /dev/null +++ b/packages/adt-pilot/tests/mcp-client.test.ts @@ -0,0 +1,82 @@ +/** + * MCP client factory tests + */ + +import { describe, it, expect, vi } from 'vitest'; +import { createMcpToolCaller } from '../src/index.js'; + +// --------------------------------------------------------------------------- +// createMcpToolCaller tests +// --------------------------------------------------------------------------- + +describe('createMcpToolCaller', () => { + it('returns parsed JSON from the first text content block', async () => { + const mockClient = { + callTool: vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: '{"status":"ok","count":3}' }], + isError: false, + }), + }; + + const callTool = createMcpToolCaller(mockClient as never); + const result = await callTool('some_tool', { arg: 'value' }); + + expect(result).toEqual({ status: 'ok', count: 3 }); + expect(mockClient.callTool).toHaveBeenCalledWith({ + name: 'some_tool', + arguments: { arg: 'value' }, + }); + }); + + it('throws when the tool responds with isError: true', async () => { + const mockClient = { + callTool: vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'Tool failed: connection refused' }], + isError: true, + }), + }; + + const callTool = createMcpToolCaller(mockClient as never); + await expect(callTool('bad_tool', {})).rejects.toThrow( + 'Tool failed: connection refused', + ); + }); + + it('throws with generic message when isError is true and text is empty', async () => { + const mockClient = { + callTool: vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: '' }], + isError: true, + }), + }; + + const callTool = createMcpToolCaller(mockClient as never); + await expect(callTool('bad_tool', {})).rejects.toThrow('bad_tool'); + }); + + it('returns raw string when content is not valid JSON', async () => { + const mockClient = { + callTool: vi.fn().mockResolvedValue({ + content: [{ type: 'text', text: 'plain text response' }], + isError: false, + }), + }; + + const callTool = createMcpToolCaller(mockClient as never); + const result = await callTool('text_tool', {}); + expect(result).toBe('plain text response'); + }); + + it('returns empty string when content array is empty', async () => { + const mockClient = { + callTool: vi.fn().mockResolvedValue({ + content: [], + isError: false, + }), + }; + + const callTool = createMcpToolCaller(mockClient as never); + const result = await callTool('empty_tool', {}); + expect(result).toBe(''); + }); +}); diff --git a/packages/adt-pilot/tests/types.test.ts b/packages/adt-pilot/tests/types.test.ts new file mode 100644 index 00000000..2e700a7f --- /dev/null +++ b/packages/adt-pilot/tests/types.test.ts @@ -0,0 +1,127 @@ +/** + * Type validation tests for CodeReviewReport schema + */ + +import { describe, it, expect } from 'vitest'; +import { z } from 'zod'; +import { codeReviewOutputSchema, codeReviewInputSchema } from '../src/index.js'; + +describe('codeReviewOutputSchema', () => { + it('accepts a valid package-mode report', () => { + const report = { + mode: 'package', + target: 'ZPACKAGE', + objects: ['/sap/bc/adt/oo/classes/zcl_example'], + findings: [ + { + objectUri: '/sap/bc/adt/oo/classes/zcl_example', + priority: '3', + description: 'Avoid SELECT *', + checkName: 'CL_CI_TEST_SELECT_STAR', + location: '/sap/bc/adt/oo/classes/zcl_example/source/main#start=10,0', + }, + ], + summary: { + totalObjects: 1, + totalFindings: 1, + bySeverity: { '3': 1 }, + }, + } satisfies z.infer; + + expect(() => codeReviewOutputSchema.parse(report)).not.toThrow(); + }); + + it('accepts a valid transport-mode report', () => { + const report = { + mode: 'transport', + target: 'DEVK900001', + objects: ['/sap/bc/adt/cts/transportrequests/DEVK900001'], + findings: [], + summary: { totalObjects: 1, totalFindings: 0, bySeverity: {} }, + }; + expect(() => codeReviewOutputSchema.parse(report)).not.toThrow(); + }); + + it('accepts a report with no findings (empty package)', () => { + const report = { + mode: 'package', + target: 'ZEMPTY', + objects: [], + findings: [], + summary: { totalObjects: 0, totalFindings: 0, bySeverity: {} }, + }; + expect(() => codeReviewOutputSchema.parse(report)).not.toThrow(); + }); + + it('accepts a report with an error finding', () => { + const report = { + mode: 'package', + target: 'ZPACKAGE', + objects: ['/sap/bc/adt/oo/classes/zcl_fail'], + findings: [ + { + objectUri: '/sap/bc/adt/oo/classes/zcl_fail', + priority: 'error', + description: 'ATC service unavailable', + }, + ], + summary: { totalObjects: 1, totalFindings: 1, bySeverity: { error: 1 } }, + }; + expect(() => codeReviewOutputSchema.parse(report)).not.toThrow(); + }); + + it('rejects a report missing required fields', () => { + const incomplete = { + mode: 'package', + target: 'ZPACKAGE', + // missing objects, findings, summary + }; + expect(() => codeReviewOutputSchema.parse(incomplete)).toThrow(z.ZodError); + }); +}); + +describe('codeReviewInputSchema', () => { + it('accepts valid package mode input', () => { + const input = { + mode: 'package', + packageName: 'ZPACKAGE', + baseUrl: 'http://sap:8000', + username: 'DEVELOPER', + password: 'secret', + }; + expect(() => codeReviewInputSchema.parse(input)).not.toThrow(); + }); + + it('accepts valid transport mode input with optional client', () => { + const input = { + mode: 'transport', + transportNumber: 'DEVK900001', + baseUrl: 'http://sap:8000', + username: 'DEVELOPER', + password: 'secret', + client: '100', + }; + expect(() => codeReviewInputSchema.parse(input)).not.toThrow(); + }); + + it('rejects input with missing baseUrl', () => { + const input = { + mode: 'package', + packageName: 'ZPACKAGE', + username: 'DEVELOPER', + password: 'secret', + }; + expect(() => codeReviewInputSchema.parse(input)).toThrow(z.ZodError); + }); + + it('rejects input with invalid mode', () => { + const input = { + mode: 'invalid', + packageName: 'ZPACKAGE', + baseUrl: 'http://sap:8000', + username: 'DEVELOPER', + password: 'secret', + }; + expect(() => codeReviewInputSchema.parse(input)).toThrow(z.ZodError); + }); +}); diff --git a/packages/adt-pilot/tests/workflow.test.ts b/packages/adt-pilot/tests/workflow.test.ts new file mode 100644 index 00000000..c119363b --- /dev/null +++ b/packages/adt-pilot/tests/workflow.test.ts @@ -0,0 +1,292 @@ +/** + * Workflow integration tests for @abapify/adt-pilot + * + * Tests the three workflow modes: + * - Package mode happy path + * - Transport mode happy path + * - Empty package → empty report + * - Partial ATC failure → report with error finding + * + * Uses @abapify/adt-fixtures mock ADT server + @abapify/adt-mcp + * connected via InMemoryTransport (same pattern as adt-mcp integration tests). + */ + +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import { createMockAdtServer, type MockAdtServer } from '@abapify/adt-fixtures'; +import { createAdtClient } from '@abapify/adt-client'; +import { createMcpServer } from '@abapify/adt-mcp'; +import { createCodeReviewWorkflow, createMcpToolCaller } from '../src/index.js'; +import type { CodeReviewReport, McpToolCaller } from '../src/index.js'; + +// --------------------------------------------------------------------------- +// Test-level setup +// --------------------------------------------------------------------------- + +let mockAdt: MockAdtServer; +let mockPort: number; +let mcpClient: Client; +let callTool: McpToolCaller; + +const MOCK_PASSWORD = 'test-password-1234'; + +function connArgs() { + return { + baseUrl: `http://localhost:${mockPort}`, + username: 'DEVELOPER', + password: MOCK_PASSWORD, + client: '100', + }; +} + +beforeAll(async () => { + // 1. Start mock ADT HTTP server + mockAdt = createMockAdtServer(); + const info = await mockAdt.start(); + mockPort = info.port; + + // 2. Create MCP server backed by the mock ADT server + const server = createMcpServer({ + clientFactory: (params) => + createAdtClient({ + baseUrl: params.baseUrl, + username: params.username ?? '', + password: params.password ?? '', + client: params.client, + }), + }); + + // 3. Wire via InMemoryTransport + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + await server.connect(serverTransport); + + mcpClient = new Client({ name: 'adt-pilot-test', version: '0.0.1' }); + await mcpClient.connect(clientTransport); + + callTool = createMcpToolCaller(mcpClient); +}); + +afterAll(async () => { + await mcpClient.close(); + await mockAdt.stop(); +}); + +// --------------------------------------------------------------------------- +// Package mode tests +// --------------------------------------------------------------------------- + +describe('Code Review Workflow – package mode', () => { + it('returns a CodeReviewReport for a package with objects', async () => { + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), + }, + }); + + expect(result.status).toBe('success'); + const report = result.result as CodeReviewReport; + + // Basic structure + expect(report).toBeDefined(); + expect(report.mode).toBe('package'); + expect(report.target).toBe('ZPACKAGE'); + expect(Array.isArray(report.objects)).toBe(true); + expect(Array.isArray(report.findings)).toBe(true); + expect(typeof report.summary.totalObjects).toBe('number'); + expect(typeof report.summary.totalFindings).toBe('number'); + expect(typeof report.summary.bySeverity).toBe('object'); + }); + + it('summary.totalObjects matches the number of resolved objects', async () => { + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), + }, + }); + + const report = result.result as CodeReviewReport; + expect(report.summary.totalObjects).toBe(report.objects.length); + }); + + it('summary.totalFindings matches findings array length', async () => { + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), + }, + }); + + const report = result.result as CodeReviewReport; + // totalFindings counts only findings (not error entries) + const realFindings = report.findings.filter((f) => f.priority !== 'error'); + expect(report.summary.totalFindings).toBe(report.findings.length); + // The mock ATC worklist fixture has findings for ZCL_SAMPLE_CLASS + expect(realFindings.length).toBeGreaterThanOrEqual(0); + }); +}); + +// --------------------------------------------------------------------------- +// Transport mode tests +// --------------------------------------------------------------------------- + +describe('Code Review Workflow – transport mode', () => { + it('returns a CodeReviewReport for a transport', async () => { + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'transport', + transportNumber: 'DEVK900001', + ...connArgs(), + }, + }); + + expect(result.status).toBe('success'); + const report = result.result as CodeReviewReport; + + expect(report.mode).toBe('transport'); + expect(report.target).toBe('DEVK900001'); + expect(report.objects).toHaveLength(1); + expect(report.objects[0]).toContain('DEVK900001'); + expect(typeof report.summary.totalObjects).toBe('number'); + }); + + it('objects array contains the transport URI', async () => { + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'transport', + transportNumber: 'DEVK900001', + ...connArgs(), + }, + }); + + const report = result.result as CodeReviewReport; + expect(report.objects[0]).toBe( + '/sap/bc/adt/cts/transportrequests/DEVK900001', + ); + }); +}); + +// --------------------------------------------------------------------------- +// Empty package test +// --------------------------------------------------------------------------- + +describe('Code Review Workflow – empty package', () => { + it('returns an empty report when no objects are found', async () => { + // The mock always returns the same search results. + // Use a package name that doesn't match any fixture objects so the + // objects array comes back empty (the mock filters by packageName match). + const workflow = createCodeReviewWorkflow(callTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZEMPTY_PACKAGE_DOES_NOT_EXIST', + ...connArgs(), + }, + }); + + expect(result.status).toBe('success'); + const report = result.result as CodeReviewReport; + + // No objects → no findings + expect(report.objects).toHaveLength(0); + expect(report.findings).toHaveLength(0); + expect(report.summary.totalObjects).toBe(0); + expect(report.summary.totalFindings).toBe(0); + expect(Object.keys(report.summary.bySeverity)).toHaveLength(0); + }); +}); + +// --------------------------------------------------------------------------- +// Error handling test +// --------------------------------------------------------------------------- + +describe('Code Review Workflow – error handling', () => { + it('produces an error finding when atc_run fails, workflow still completes', async () => { + // Inject a tool caller that succeeds for list_package_objects but + // throws for atc_run, simulating a partial failure + const failingAtcCallTool: McpToolCaller = async (toolName, args) => { + if (toolName === 'list_package_objects') { + // Return a single fake object + return { + packageName: args.packageName, + count: 1, + objects: [{ uri: '/sap/bc/adt/oo/classes/zcl_fail_test' }], + }; + } + if (toolName === 'atc_run') { + throw new Error('ATC service unavailable'); + } + return callTool(toolName, args); + }; + + const workflow = createCodeReviewWorkflow(failingAtcCallTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), + }, + }); + + expect(result.status).toBe('success'); + const report = result.result as CodeReviewReport; + + expect(report.findings).toHaveLength(1); + expect(report.findings[0].priority).toBe('error'); + expect(report.findings[0].description).toContain('ATC service unavailable'); + expect(report.summary.totalFindings).toBe(1); + expect(report.summary.bySeverity['error']).toBe(1); + }); + + it('produces error findings for all objects when atc_run consistently fails', async () => { + const failingCallTool: McpToolCaller = async (toolName, args) => { + if (toolName === 'list_package_objects') { + return { + packageName: args.packageName, + count: 2, + objects: [ + { uri: '/sap/bc/adt/oo/classes/zcl_fail_1' }, + { uri: '/sap/bc/adt/oo/classes/zcl_fail_2' }, + ], + }; + } + if (toolName === 'atc_run') { + throw new Error('Network error'); + } + return callTool(toolName, args); + }; + + const workflow = createCodeReviewWorkflow(failingCallTool); + const run = workflow.createRun(); + const result = await run.start({ + inputData: { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), + }, + }); + + const report = result.result as CodeReviewReport; + expect(report.objects).toHaveLength(2); + expect(report.findings).toHaveLength(2); + expect(report.findings.every((f) => f.priority === 'error')).toBe(true); + }); +}); diff --git a/packages/adt-pilot/tsconfig.json b/packages/adt-pilot/tsconfig.json new file mode 100644 index 00000000..c23e61c8 --- /dev/null +++ b/packages/adt-pilot/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/adt-pilot/tsconfig.lib.json b/packages/adt-pilot/tsconfig.lib.json new file mode 100644 index 00000000..cf10527e --- /dev/null +++ b/packages/adt-pilot/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/packages/adt-pilot/tsdown.config.ts b/packages/adt-pilot/tsdown.config.ts new file mode 100644 index 00000000..bf7b6cba --- /dev/null +++ b/packages/adt-pilot/tsdown.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'tsdown'; +import baseConfig from '../../tsdown.config.ts'; + +export default defineConfig({ + ...baseConfig, + entry: { + index: 'src/index.ts', + }, + tsconfig: 'tsconfig.lib.json', +}); diff --git a/packages/adt-pilot/vitest.config.ts b/packages/adt-pilot/vitest.config.ts new file mode 100644 index 00000000..8e730d50 --- /dev/null +++ b/packages/adt-pilot/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + }, +}); From f51bebd16849887075a3c46deeff4d01be1d711b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:56:33 +0000 Subject: [PATCH 3/7] =?UTF-8?q?feat(adt-pilot):=20fix=20build=20=E2=80=94?= =?UTF-8?q?=20disable=20dts=20(OOM),=20explicit=20return=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/abapify/adt-cli/sessions/8c6e39c2-3ae6-4d7f-8cef-5ff4f1b5904d Co-authored-by: ThePlenkov <6381507+ThePlenkov@users.noreply.github.com> --- packages/adt-pilot/package.json | 1 - packages/adt-pilot/src/agent.ts | 2 +- packages/adt-pilot/src/index.ts | 2 +- packages/adt-pilot/src/workflow.ts | 23 +++++++++++++++++++++-- packages/adt-pilot/tsdown.config.ts | 5 +++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/adt-pilot/package.json b/packages/adt-pilot/package.json index 5105ba69..2040ff5e 100644 --- a/packages/adt-pilot/package.json +++ b/packages/adt-pilot/package.json @@ -7,7 +7,6 @@ "access": "public" }, "type": "module", - "types": "./dist/index.d.mts", "exports": { ".": "./dist/index.mjs", "./package.json": "./package.json" diff --git a/packages/adt-pilot/src/agent.ts b/packages/adt-pilot/src/agent.ts index ce9e133d..2b31384a 100644 --- a/packages/adt-pilot/src/agent.ts +++ b/packages/adt-pilot/src/agent.ts @@ -43,7 +43,7 @@ Always present findings grouped by severity and provide clear remediation guidan * * @param config - Agent configuration (model + optional pre-loaded tools) */ -export function createReviewAgent(config: ReviewAgentConfig): Agent { +export function createReviewAgent(config: ReviewAgentConfig): Agent { return new Agent({ id: 'review', name: 'abapify Pilot – Review', diff --git a/packages/adt-pilot/src/index.ts b/packages/adt-pilot/src/index.ts index cf90e854..31b0b835 100644 --- a/packages/adt-pilot/src/index.ts +++ b/packages/adt-pilot/src/index.ts @@ -45,7 +45,7 @@ export { codeReviewInputSchema, codeReviewOutputSchema, } from './workflow.js'; -export type { CodeReviewInput } from './workflow.js'; +export type { CodeReviewInput, CodeReviewWorkflowHandle } from './workflow.js'; // MCP client factory export { createMcpToolCaller, connectMcpClient } from './mcp-client.js'; diff --git a/packages/adt-pilot/src/workflow.ts b/packages/adt-pilot/src/workflow.ts index 44fc900c..673a7de0 100644 --- a/packages/adt-pilot/src/workflow.ts +++ b/packages/adt-pilot/src/workflow.ts @@ -376,13 +376,32 @@ function extractFindings( // Factory // --------------------------------------------------------------------------- +/** + * Minimal public type for the workflow handle returned by + * `createCodeReviewWorkflow`. + * + * Wraps the real Mastra `Workflow<...>` in a simple interface so the OXC + * declaration generator does not need to expand the deeply generic Mastra + * type parameters (which would cause OOM in the DTS build step). + */ +export interface CodeReviewWorkflowHandle { + createRun(): { + start(opts: { inputData: CodeReviewInput }): Promise<{ + status: string; + result: unknown; + }>; + }; +} + /** * Create the Code Review workflow, bound to the given MCP tool caller. * * @param callTool Function that calls a named MCP tool and returns the parsed * JSON response. */ -export function createCodeReviewWorkflow(callTool: McpToolCaller) { +export function createCodeReviewWorkflow( + callTool: McpToolCaller, +): CodeReviewWorkflowHandle { const resolveObjectsStep = createResolveObjectsStep(callTool); const runAtcChecksStep = createRunAtcChecksStep(callTool); const buildReportStep = createBuildReportStep(); @@ -397,5 +416,5 @@ export function createCodeReviewWorkflow(callTool: McpToolCaller) { .then(resolveObjectsStep) .then(runAtcChecksStep) .then(buildReportStep) - .commit(); + .commit() as unknown as CodeReviewWorkflowHandle; } diff --git a/packages/adt-pilot/tsdown.config.ts b/packages/adt-pilot/tsdown.config.ts index bf7b6cba..12b73b91 100644 --- a/packages/adt-pilot/tsdown.config.ts +++ b/packages/adt-pilot/tsdown.config.ts @@ -7,4 +7,9 @@ export default defineConfig({ index: 'src/index.ts', }, tsconfig: 'tsconfig.lib.json', + // @mastra/core has extremely complex generics + Zod schemas cannot be annotated + // for isolatedDeclarations. DTS generation is disabled to avoid OOM (same + // pattern as adt-mcp typecheck). TypeScript users can reference the source + // directly via moduleResolution:bundler. + dts: false, }); From 8fc313ee13b4c21d782bd5304c35059b2d7ec840 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:21:13 +0000 Subject: [PATCH 4/7] refactor(adt-pilot): rework workflow + harness modes for 100% accuracy Workflow mode (deterministic, no LLM): - Three-step pipeline: resolveObjects -> runAtcChecks -> buildReport - Discriminated-union input schema with 'package' or 'transport' modes - Package mode enumerates objects via list_package_objects MCP tool - Transport mode validates via cts_get_transport, uses transport URI as ATC target (server-side enumeration matches 'adt check --transport') - Per-object error capture: failed ATC runs become priority='error' findings; the workflow never aborts on a single failure Harness mode (interactive via Mastra Agent): - createAbapifyPilot(config) returns a Mastra Harness with a 'review' mode - Review agent wired with list_package_objects, atc_run, cts_get_transport - Optional custom instructions and pre-loaded MCP tools Type/build hygiene: - Type-erase Mastra workflow + step factories at the import boundary; Mastra's deeply generic types caused tsc OOM and DTS bloat - Public API surfaces narrow CodeReviewWorkflow / CodeReviewRun handles - typecheck target overridden to 'tsc -p tsconfig.lib.json --noEmit' so tsc does not follow project references (avoids adt-mcp typecheck OOM) - Move @abapify/adt-mcp from runtime deps to devDeps (only used in tests) - Promote @modelcontextprotocol/sdk to a runtime dep (used by mcp-client) Tests (28 total, all passing): - workflow.test.ts: rewritten for async createRun() API; covers package mode happy path + empty package, transport mode happy path + URI assertion, single + multi-object error paths, transport-lookup failure - types.test.ts: schema validation; credentials generated via randomBytes - harness.test.ts (new): factory smoke tests for Harness + Agent - mcp-client.test.ts: unchanged (still passing) Security: - All test credentials generated per-process with randomBytes (no more hardcoded 'secret'/'test-password' strings flagged by SonarCloud) Co-Authored-By: Petr Plenkov --- bun.lock | 376 +++++++++++---- packages/adt-pilot/package.json | 4 +- packages/adt-pilot/project.json | 19 +- packages/adt-pilot/src/agent.ts | 46 +- packages/adt-pilot/src/index.ts | 56 ++- packages/adt-pilot/src/mcp-client.ts | 96 +++- packages/adt-pilot/src/types.ts | 82 +++- packages/adt-pilot/src/workflow.ts | 549 ++++++++++++++-------- packages/adt-pilot/tests/harness.test.ts | 65 +++ packages/adt-pilot/tests/types.test.ts | 12 +- packages/adt-pilot/tests/workflow.test.ts | 261 +++++----- packages/adt-pilot/tsconfig.json | 3 + packages/adt-pilot/tsconfig.lib.json | 13 +- packages/adt-pilot/tsconfig.spec.json | 19 + tsconfig.json | 3 + 15 files changed, 1109 insertions(+), 495 deletions(-) create mode 100644 packages/adt-pilot/tests/harness.test.ts create mode 100644 packages/adt-pilot/tsconfig.spec.json diff --git a/bun.lock b/bun.lock index 0d55661f..643cb37e 100644 --- a/bun.lock +++ b/bun.lock @@ -102,16 +102,16 @@ "name": "@abapify/adk", "version": "0.3.6", "dependencies": { - "@abapify/adt-client": "workspace:*", - "@abapify/adt-locks": "workspace:*", - "@abapify/adt-schemas": "workspace:*", + "@abapify/adt-client": "0.3.6", + "@abapify/adt-locks": "0.3.6", + "@abapify/adt-schemas": "0.3.6", }, }, "packages/adt-atc": { "name": "@abapify/adt-atc", "version": "0.3.6", "dependencies": { - "@abapify/adt-plugin": "workspace:*", + "@abapify/adt-plugin": "0.3.6", "chalk": "^5.3.0", }, }, @@ -119,17 +119,17 @@ "name": "@abapify/adt-aunit", "version": "0.3.6", "dependencies": { - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-plugin": "workspace:*", - "@abapify/adt-plugin-abapgit": "workspace:*", - "@abapify/adt-schemas": "workspace:*", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-plugin": "0.3.6", + "@abapify/adt-plugin-abapgit": "0.3.6", + "@abapify/adt-schemas": "0.3.6", }, }, "packages/adt-auth": { "name": "@abapify/adt-auth", "version": "0.3.6", "dependencies": { - "@abapify/logger": "workspace:*", + "@abapify/logger": "0.3.6", "proxy-agent": "^6.4.0", }, }, @@ -140,25 +140,25 @@ "adt": "./dist/bin/adt.mjs", }, "dependencies": { - "@abapify/adk": "workspace:*", - "@abapify/adt-atc": "workspace:*", - "@abapify/adt-aunit": "workspace:*", - "@abapify/adt-auth": "workspace:*", - "@abapify/adt-client": "workspace:*", - "@abapify/adt-codegen": "workspace:*", - "@abapify/adt-config": "workspace:*", - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-diff": "workspace:*", - "@abapify/adt-export": "workspace:*", - "@abapify/adt-locks": "workspace:*", - "@abapify/adt-plugin": "workspace:*", - "@abapify/adt-plugin-abapgit": "workspace:*", - "@abapify/adt-plugin-gcts": "workspace:*", - "@abapify/adt-plugin-gcts-cli": "workspace:*", - "@abapify/adt-rfc": "workspace:*", - "@abapify/adt-schemas": "workspace:*", - "@abapify/adt-tui": "workspace:*", - "@abapify/logger": "workspace:*", + "@abapify/adk": "0.3.6", + "@abapify/adt-atc": "0.3.6", + "@abapify/adt-aunit": "0.3.6", + "@abapify/adt-auth": "0.3.6", + "@abapify/adt-client": "0.3.6", + "@abapify/adt-codegen": "0.3.6", + "@abapify/adt-config": "0.3.6", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-diff": "0.3.6", + "@abapify/adt-export": "0.3.6", + "@abapify/adt-locks": "0.3.6", + "@abapify/adt-plugin": "0.3.6", + "@abapify/adt-plugin-abapgit": "0.3.6", + "@abapify/adt-plugin-gcts": "0.3.6", + "@abapify/adt-plugin-gcts-cli": "0.3.6", + "@abapify/adt-rfc": "0.3.6", + "@abapify/adt-schemas": "0.3.6", + "@abapify/adt-tui": "0.3.6", + "@abapify/logger": "0.3.6", "@inquirer/prompts": "^7.9.0", "@xmldom/xmldom": "^0.9.9", "chalk": "^5.6.2", @@ -179,9 +179,9 @@ "name": "@abapify/adt-client", "version": "0.3.6", "dependencies": { - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-schemas": "workspace:*", - "@abapify/logger": "workspace:*", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-schemas": "0.3.6", + "@abapify/logger": "0.3.6", }, }, "packages/adt-codegen": { @@ -191,8 +191,8 @@ "adt-codegen": "./dist/cli.mjs", }, "dependencies": { - "@abapify/adt-config": "workspace:*", - "@abapify/adt-plugin": "workspace:*", + "@abapify/adt-config": "0.3.6", + "@abapify/adt-plugin": "0.3.6", "chalk": "^5.3.0", "commander": "^11.1.0", "fast-xml-parser": "^5.5.0", @@ -206,37 +206,37 @@ "name": "@abapify/adt-contracts", "version": "0.3.6", "dependencies": { - "@abapify/adt-schemas": "workspace:*", - "@abapify/speci": "workspace:*", + "@abapify/adt-schemas": "0.3.6", + "@abapify/speci": "0.3.6", }, "devDependencies": { - "@abapify/adt-fixtures": "workspace:*", + "@abapify/adt-fixtures": "0.3.6", }, }, "packages/adt-diff": { "name": "@abapify/adt-diff", "version": "0.3.6", "dependencies": { - "@abapify/adk": "workspace:*", - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-plugin": "workspace:*", - "@abapify/adt-plugin-abapgit": "workspace:*", + "@abapify/adk": "0.3.6", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-plugin": "0.3.6", + "@abapify/adt-plugin-abapgit": "0.3.6", "chalk": "^5.6.2", "diff": "^8.0.3", "fast-xml-parser": "^5.5.5", }, "devDependencies": { - "@abapify/adt-fixtures": "workspace:*", + "@abapify/adt-fixtures": "0.3.6", }, }, "packages/adt-export": { "name": "@abapify/adt-export", "version": "0.3.6", "dependencies": { - "@abapify/adk": "workspace:*", - "@abapify/adt-locks": "workspace:*", - "@abapify/adt-plugin": "workspace:*", - "@abapify/adt-plugin-abapgit": "workspace:*", + "@abapify/adk": "0.3.6", + "@abapify/adt-locks": "0.3.6", + "@abapify/adt-plugin": "0.3.6", + "@abapify/adt-plugin-abapgit": "0.3.6", "chalk": "^5.6.2", "diff": "^8.0.3", "fast-xml-parser": "^5.5.5", @@ -250,7 +250,7 @@ "name": "@abapify/adt-locks", "version": "0.3.6", "dependencies": { - "@abapify/adt-client": "workspace:*", + "@abapify/adt-client": "0.3.6", }, }, "packages/adt-mcp": { @@ -261,22 +261,36 @@ "adt-mcp-http": "./dist/bin/adt-mcp-http.mjs", }, "dependencies": { - "@abapify/adk": "workspace:*", - "@abapify/adt-aunit": "workspace:*", - "@abapify/adt-cli": "workspace:*", - "@abapify/adt-client": "workspace:*", - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-fixtures": "workspace:*", - "@abapify/adt-locks": "workspace:*", - "@abapify/adt-plugin-abapgit": "workspace:*", - "@abapify/adt-rfc": "workspace:*", - "@abapify/adt-schemas": "workspace:*", + "@abapify/adk": "0.3.6", + "@abapify/adt-aunit": "0.3.6", + "@abapify/adt-cli": "0.3.6", + "@abapify/adt-client": "0.3.6", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-fixtures": "0.3.6", + "@abapify/adt-locks": "0.3.6", + "@abapify/adt-plugin-abapgit": "0.3.6", + "@abapify/adt-rfc": "0.3.6", + "@abapify/adt-schemas": "0.3.6", "@modelcontextprotocol/sdk": "^1.27.0", "@xmldom/xmldom": "^0.9.9", "jose": "^6.2.1", "zod": "^3.24.0", }, }, + "packages/adt-pilot": { + "name": "@abapify/adt-pilot", + "version": "0.3.6", + "dependencies": { + "@mastra/core": "^1.28.0", + "@mastra/mcp": "^1.5.2", + "@modelcontextprotocol/sdk": "^1.27.0", + "zod": "^3.24.0", + }, + "devDependencies": { + "@abapify/adt-fixtures": "0.3.6", + "@abapify/adt-mcp": "0.3.6", + }, + }, "packages/adt-playwright": { "name": "@abapify/adt-playwright", "version": "0.3.6", @@ -294,35 +308,35 @@ "name": "@abapify/adt-plugin", "version": "0.3.6", "dependencies": { - "@abapify/adk": "workspace:*", + "@abapify/adk": "0.3.6", }, }, "packages/adt-plugin-abapgit": { "name": "@abapify/adt-plugin-abapgit", "version": "0.3.6", "dependencies": { - "@abapify/acds": "workspace:*", - "@abapify/adk": "workspace:*", - "@abapify/adt-atc": "workspace:*", - "@abapify/adt-plugin": "workspace:*", - "@abapify/adt-schemas": "workspace:*", - "@abapify/ts-xsd": "workspace:*", + "@abapify/acds": "0.3.6", + "@abapify/adk": "0.3.6", + "@abapify/adt-atc": "0.3.6", + "@abapify/adt-plugin": "0.3.6", + "@abapify/adt-schemas": "0.3.6", + "@abapify/ts-xsd": "0.3.6", }, }, "packages/adt-plugin-gcts": { "name": "@abapify/adt-plugin-gcts", "version": "0.3.6", "dependencies": { - "@abapify/adk": "workspace:*", - "@abapify/adt-plugin": "workspace:*", + "@abapify/adk": "0.3.6", + "@abapify/adt-plugin": "0.3.6", }, }, "packages/adt-plugin-gcts-cli": { "name": "@abapify/adt-plugin-gcts-cli", "version": "0.3.6", "dependencies": { - "@abapify/adt-contracts": "workspace:*", - "@abapify/adt-plugin": "workspace:*", + "@abapify/adt-contracts": "0.3.6", + "@abapify/adt-plugin": "0.3.6", "zod": "^3.23.8", }, }, @@ -347,18 +361,18 @@ "name": "@abapify/adt-schemas", "version": "0.3.6", "dependencies": { - "@abapify/ts-xsd": "workspace:*", + "@abapify/ts-xsd": "0.3.6", "zod": "^3.24.0 || ^4.0.0", }, "devDependencies": { - "@abapify/adt-fixtures": "workspace:*", + "@abapify/adt-fixtures": "0.3.6", }, }, "packages/adt-tui": { "name": "@abapify/adt-tui", "version": "0.3.6", "dependencies": { - "@abapify/adt-contracts": "workspace:*", + "@abapify/adt-contracts": "0.3.6", "fast-xml-parser": "^5.3.1", "ink": "5.1.0", "ink-select-input": "^6.0.0", @@ -370,7 +384,7 @@ "@types/react": "18.3.0", }, "peerDependencies": { - "@abapify/adt-client": "workspace:*", + "@abapify/adt-client": "0.3.6", }, }, "packages/asjson-parser": { @@ -492,6 +506,8 @@ "fast-xml-builder": "^1.0.0", }, "packages": { + "@a2a-js/sdk": ["@a2a-js/sdk@0.3.13", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "@bufbuild/protobuf": "^2.10.2", "@grpc/grpc-js": "^1.11.0", "express": "^4.21.2 || ^5.1.0" }, "optionalPeers": ["@bufbuild/protobuf", "@grpc/grpc-js", "express"] }, "sha512-BZr0f9JVNQs3GKOM9xINWCh6OKIJWZFPyqqVqTym5mxO2Eemc6I/0zL7zWnljHzGdaf5aZQyQN5xa6PSH62q+A=="], + "@abapify/abap-ast": ["@abapify/abap-ast@workspace:packages/abap-ast"], "@abapify/acds": ["@abapify/acds@workspace:packages/acds"], @@ -526,6 +542,8 @@ "@abapify/adt-mcp": ["@abapify/adt-mcp@workspace:packages/adt-mcp"], + "@abapify/adt-pilot": ["@abapify/adt-pilot@workspace:packages/adt-pilot"], + "@abapify/adt-playwright": ["@abapify/adt-playwright@workspace:packages/adt-playwright"], "@abapify/adt-plugin": ["@abapify/adt-plugin@workspace:packages/adt-plugin"], @@ -572,6 +590,20 @@ "@abaplint/core": ["@abaplint/core@2.119.1", "", { "dependencies": { "fast-xml-parser": "^5.7.1", "json5": "^2.2.3", "vscode-languageserver-types": "^3.17.5" } }, "sha512-dsAFecT5I5m+PiSPuNgB0hHFzilqFa+/S+YRHIhd12u3+tzmktwNtI2c1I9wm+2HXF+9bTeLxk6JekVuNfqs4w=="], + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], + + "@ai-sdk/provider-utils-v5": ["@ai-sdk/provider-utils@3.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-60GYsRj5wIJQRcq5YwYJq4KhwLeStceXEJiZdecP1miiH+6FMmrnc7lZDOJoQ6m9lrudEb+uI4LEwddLz5+rPQ=="], + + "@ai-sdk/provider-utils-v6": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + + "@ai-sdk/provider-v5": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + + "@ai-sdk/provider-v6": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + + "@ai-sdk/ui-utils-v5": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], "@algolia/abtesting": ["@algolia/abtesting@1.16.2", "", { "dependencies": { "@algolia/client-common": "5.50.2", "@algolia/requester-browser-xhr": "5.50.2", "@algolia/requester-fetch": "5.50.2", "@algolia/requester-node-http": "5.50.2" } }, "sha512-n9s6bEV6imdtIEd+BGP7WkA4pEZ5YTdgQ05JQhHwWawHg3hyjpNwC0TShGz6zWhv+jfLDGA/6FFNbySFS0P9cw=="], @@ -1164,6 +1196,8 @@ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@isaacs/ttlcache": ["@isaacs/ttlcache@2.1.4", "", {}, "sha512-7kMz0BJpMvgAMkyglums7B2vtrn5g0a0am77JY0GjkZZNetOBCFn7AG7gKCwT0QPiXyxW7YIQSgtARknUEOcxQ=="], + "@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="], "@istanbuljs/schema": ["@istanbuljs/schema@0.1.3", "", {}, "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="], @@ -1252,6 +1286,16 @@ "@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="], + "@lukeed/csprng": ["@lukeed/csprng@1.1.0", "", {}, "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA=="], + + "@lukeed/uuid": ["@lukeed/uuid@2.0.1", "", { "dependencies": { "@lukeed/csprng": "^1.1.0" } }, "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w=="], + + "@mastra/core": ["@mastra/core@1.29.0", "", { "dependencies": { "@a2a-js/sdk": "~0.3.13", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.23", "@ai-sdk/provider-utils-v6": "npm:@ai-sdk/provider-utils@4.0.23", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.1", "@ai-sdk/provider-v6": "npm:@ai-sdk/provider@3.0.8", "@ai-sdk/ui-utils-v5": "npm:@ai-sdk/ui-utils@1.2.11", "@isaacs/ttlcache": "^2.1.4", "@lukeed/uuid": "^2.0.1", "@mastra/schema-compat": "1.2.9", "@modelcontextprotocol/sdk": "^1.27.1", "@sindresorhus/slugify": "^2.2.1", "@standard-schema/spec": "^1.1.0", "ajv": "^8.18.0", "chat": "^4.24.0", "dotenv": "^17.3.1", "execa": "^9.6.1", "gray-matter": "^4.0.3", "hono": "^4.12.8", "hono-openapi": "^1.3.0", "ignore": "^7.0.5", "js-tiktoken": "^1.0.21", "json-schema": "^0.4.0", "lru-cache": "^11.2.7", "p-map": "^7.0.4", "p-retry": "^7.1.1", "picomatch": "^4.0.3", "radash": "^12.1.1", "tokenx": "^1.3.0", "ws": "^8.20.0", "xxhash-wasm": "^1.1.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-AD1aV2A71rNS3yWa7XFzLVFUIwo7Y7+1ZKxL65WDH6d/T+0YwIuTU25yb3d8T1lyHIehyaZr5SmwQGyjMOINkA=="], + + "@mastra/mcp": ["@mastra/mcp@1.6.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.27.1", "exit-hook": "^5.1.0", "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "@mastra/core": ">=1.0.0-0 <2.0.0-0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-o63itm5R5nD8Q574Sj5EbG8TjIhczGrZT4N3ZxVA8W42CLUc+2ZH+Xmj2ElaLx30HCN1gIiNkjuqlfxuq2zA+Q=="], + + "@mastra/schema-compat": ["@mastra/schema-compat@1.2.9", "", { "dependencies": { "json-schema-to-zod": "^2.7.0", "zod-from-json-schema": "^0.5.2", "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-1/RgazXqi1Wdyx8aR81CVS+sRyzlTGUL1YhhHkSULoEY8aXs58bvWkH/6iixlYsY0xGvn+0OPLCeSRkBCtDx4Q=="], + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], @@ -1564,6 +1608,8 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + "@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="], "@sideway/formula": ["@sideway/formula@3.0.1", "", {}, "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="], @@ -1574,12 +1620,22 @@ "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + + "@sindresorhus/slugify": ["@sindresorhus/slugify@2.2.1", "", { "dependencies": { "@sindresorhus/transliterate": "^1.0.0", "escape-string-regexp": "^5.0.0" } }, "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw=="], + + "@sindresorhus/transliterate": ["@sindresorhus/transliterate@1.6.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0" } }, "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ=="], + "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], "@sinonjs/fake-timers": ["@sinonjs/fake-timers@15.1.1", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw=="], "@slorber/remark-comment": ["@slorber/remark-comment@1.0.0", "", { "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.1.0", "micromark-util-symbol": "^1.0.1" } }, "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA=="], + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], + + "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.9", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@svgr/babel-plugin-add-jsx-attribute": ["@svgr/babel-plugin-add-jsx-attribute@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g=="], @@ -1954,6 +2010,8 @@ "@webassemblyjs/wast-printer": ["@webassemblyjs/wast-printer@1.14.1", "", { "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw=="], + "@workflow/serde": ["@workflow/serde@4.1.0-beta.2", "", {}, "sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww=="], + "@xhmikosr/archive-type": ["@xhmikosr/archive-type@7.1.0", "", { "dependencies": { "file-type": "^20.5.0" } }, "sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg=="], "@xhmikosr/bin-check": ["@xhmikosr/bin-check@7.1.0", "", { "dependencies": { "execa": "^5.1.1", "isexe": "^2.0.0" } }, "sha512-y1O95J4mnl+6MpVmKfMYXec17hMEwE/yeCglFNdx+QvLLtP0yN4rSYcbkXnth+lElBuKKek2NbvOfOGPpUXCvw=="], @@ -2228,6 +2286,8 @@ "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], + "chat": ["chat@4.26.0", "", { "dependencies": { "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" } }, "sha512-QToDnIEGpyb8yQA6YLMHOSRK30YVk4RtsyFyuWFYyB2c4jQlyIrSWtwVK7qyvmvqzQp9uDwCdJRAhS8GtCHAGQ=="], + "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], @@ -2620,7 +2680,9 @@ "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], - "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], + + "exit-hook": ["exit-hook@5.1.0", "", {}, "sha512-INjr2xyxHo7bhAqf5ong++GZPPnpcuBcaXUKt03yf7Fie9yWD7FapL4teOU0+awQazGs5ucBh7xWs/AD+6nhog=="], "exit-x": ["exit-x@0.2.2", "", {}, "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ=="], @@ -2764,7 +2826,7 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "get-them-args": ["get-them-args@1.3.2", "", {}, "sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw=="], @@ -2848,6 +2910,8 @@ "hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "hono-openapi": ["hono-openapi@1.3.0", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-xDvCWpWEIv0weEmnl3EjRQzqbHIO8LnfzMuYOCmbuyE5aes6aXxLg4vM3ybnoZD5TiTUkA6PuRQPJs3R7WRBig=="], + "hookable": ["hookable@6.0.1", "", {}, "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw=="], "hpack.js": ["hpack.js@2.1.6", "", { "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", "readable-stream": "^2.0.1", "wbuf": "^1.1.0" } }, "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ=="], @@ -2890,7 +2954,7 @@ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], @@ -3016,7 +3080,7 @@ "is-regexp": ["is-regexp@1.0.0", "", {}, "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA=="], - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], @@ -3112,6 +3176,8 @@ "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -3128,6 +3194,8 @@ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-to-zod": ["json-schema-to-zod@2.8.1", "", { "bin": { "json-schema-to-zod": "dist/cjs/cli.js" } }, "sha512-fRr1mHgZ7hboLKBUdR428gd9dIHUFGivUqOeiDcSmyXkNZCtB1uGaZLvsjZ4GaN5pwBIs+TGIOf6s+Rp5/R/zA=="], + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], @@ -3504,11 +3572,11 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], "p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="], - "p-retry": ["p-retry@6.2.1", "", { "dependencies": { "@types/retry": "0.12.2", "is-network-error": "^1.0.0", "retry": "^0.13.1" } }, "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ=="], + "p-retry": ["p-retry@7.1.1", "", { "dependencies": { "is-network-error": "^1.1.0" } }, "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w=="], "p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], @@ -3532,6 +3600,8 @@ "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + "parse-numeric-range": ["parse-numeric-range@1.3.0", "", {}, "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ=="], "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], @@ -3752,6 +3822,8 @@ "pretty-format": ["pretty-format@30.3.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ=="], + "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], + "pretty-time": ["pretty-time@1.1.0", "", {}, "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA=="], "prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="], @@ -3808,6 +3880,8 @@ "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], + "radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="], + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -3892,6 +3966,8 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "remend": ["remend@1.3.0", "", {}, "sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw=="], + "renderkid": ["renderkid@3.0.0", "", { "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", "htmlparser2": "^6.1.0", "lodash": "^4.17.21", "strip-ansi": "^6.0.1" } }, "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg=="], "repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="], @@ -4116,7 +4192,7 @@ "strip-dirs": ["strip-dirs@3.0.0", "", { "dependencies": { "inspect-with-kind": "^1.0.5", "is-plain-obj": "^1.1.0" } }, "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ=="], - "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], @@ -4202,6 +4278,8 @@ "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], + "tokenx": ["tokenx@1.3.0", "", {}, "sha512-NLdXTEZkKiO0gZuLtMoZKjCXTREXeZZt8nnnNeyoXtNZAfG/GKGSbQtLU5STspc0rMSwcA+UJfWZkbNU01iKmQ=="], + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], @@ -4280,6 +4358,8 @@ "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.2.0", "", {}, "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ=="], + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], "union": ["union@0.5.0", "", { "dependencies": { "qs": "^6.4.0" } }, "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA=="], @@ -4434,6 +4514,8 @@ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], @@ -4450,35 +4532,45 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + "yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="], "yoga-wasm-web": ["yoga-wasm-web@0.3.3", "", {}, "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA=="], "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod-from-json-schema": ["zod-from-json-schema@0.5.2", "", { "dependencies": { "zod": "^4.0.17" } }, "sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g=="], + + "zod-from-json-schema-v3": ["zod-from-json-schema@0.0.5", "", { "dependencies": { "zod": "^3.24.2" } }, "sha512-zYEoo86M1qpA1Pq6329oSyHLS785z/mTwfr9V1Xf/ZLhuuBGaMlDGu/pDVGVUe4H4oa1EFgWZT53DP0U3oT9CQ=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@a2a-js/sdk/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "@abapify/adt-cli/@xmldom/xmldom": ["@xmldom/xmldom@0.9.9", "", {}, "sha512-qycIHAucxy/LXAYIjmLmtQ8q9GPnMbnjG1KXhWm9o5sCr6pOYDATkMPiTNa6/v8eELyqOQ2FsEqeoFYmgv/gJg=="], "@abapify/adt-codegen/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], - "@abapify/adt-codegen/fast-xml-parser": ["fast-xml-parser@5.5.9", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g=="], + "@abapify/adt-codegen/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="], - "@abapify/adt-diff/fast-xml-parser": ["fast-xml-parser@5.5.9", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g=="], + "@abapify/adt-diff/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="], - "@abapify/adt-export/fast-xml-parser": ["fast-xml-parser@5.5.9", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g=="], + "@abapify/adt-export/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="], "@abapify/adt-mcp/@xmldom/xmldom": ["@xmldom/xmldom@0.9.9", "", {}, "sha512-qycIHAucxy/LXAYIjmLmtQ8q9GPnMbnjG1KXhWm9o5sCr6pOYDATkMPiTNa6/v8eELyqOQ2FsEqeoFYmgv/gJg=="], "@abapify/adt-mcp/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@abapify/adt-pilot/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@abapify/adt-playwright/@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="], "@abapify/adt-plugin-gcts-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@abapify/adt-tui/fast-xml-parser": ["fast-xml-parser@5.5.9", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g=="], + "@abapify/adt-tui/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="], "@abapify/asjson-parser/jsonc-eslint-parser": ["jsonc-eslint-parser@2.4.2", "", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "semver": "^7.3.5" } }, "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA=="], @@ -4492,6 +4584,14 @@ "@abaplint/core/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="], + "@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + + "@ai-sdk/provider-utils/secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], + + "@ai-sdk/provider-utils-v6/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + + "@ai-sdk/ui-utils-v5/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -4530,8 +4630,12 @@ "@docusaurus/core/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], + "@docusaurus/core/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "@docusaurus/core/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], + "@docusaurus/core/p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], + "@docusaurus/logger/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@docusaurus/plugin-content-blog/cheerio": ["cheerio@1.0.0-rc.12", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "htmlparser2": "^8.0.1", "parse5": "^7.0.0", "parse5-htmlparser2-tree-adapter": "^7.0.0" } }, "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q=="], @@ -4540,6 +4644,8 @@ "@docusaurus/types/webpack-merge": ["webpack-merge@5.10.0", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.0" } }, "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA=="], + "@docusaurus/utils/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "@easyops-cn/docusaurus-search-local/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], @@ -4648,6 +4754,18 @@ "@jsonjoy.com/util/@jsonjoy.com/buffers": ["@jsonjoy.com/buffers@1.2.1", "", { "peerDependencies": { "tslib": "2" } }, "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA=="], + "@mastra/core/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "@mastra/core/hono": ["hono@4.12.15", "", {}, "sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg=="], + + "@mastra/core/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@mastra/core/lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="], + + "@mastra/core/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "@mastra/core/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + "@mdx-js/mdx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], @@ -4674,6 +4792,10 @@ "@rollup/pluginutils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "@sindresorhus/slugify/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "@sindresorhus/transliterate/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "@svgr/core/cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], "@svgr/hast-util-to-babel-ast/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -4750,12 +4872,24 @@ "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "@xhmikosr/bin-check/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + + "@xhmikosr/decompress-tar/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "@xhmikosr/decompress-tar/tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], + "@xhmikosr/decompress-tarbz2/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@xhmikosr/decompress-targz/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@xhmikosr/decompress-unzip/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "@xhmikosr/decompress-unzip/yauzl": ["yauzl@3.2.1", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A=="], "@xhmikosr/downloader/defaults": ["defaults@2.0.2", "", {}, "sha512-cuIw0PImdp76AOfgkjbW4VhQODRmNNcKR73vdCH5cLd/ifj7aamfoXvYgfGkEAjNJZ3ozMIy9Gu2LutUkGEPbA=="], + "@xhmikosr/downloader/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "@yarnpkg/parsers/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], @@ -4784,6 +4918,8 @@ "basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "bin-version/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "body-parser/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], @@ -4856,6 +4992,12 @@ "estree-util-build-jsx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "execa/figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "execa/npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], + + "execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "expect/jest-util": ["jest-util@30.3.0", "", { "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.3" } }, "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg=="], "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -4898,12 +5040,16 @@ "got/form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="], + "got/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "got/lowercase-keys": ["lowercase-keys@3.0.0", "", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], "got/p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="], "got/responselike": ["responselike@3.0.0", "", { "dependencies": { "lowercase-keys": "^3.0.0" } }, "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg=="], + "got-cjs/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], "handlebars/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -4946,6 +5092,8 @@ "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "jest-changed-files/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "jest-circus/@jest/environment": ["@jest/environment@30.3.0", "", { "dependencies": { "@jest/fake-timers": "30.3.0", "@jest/types": "30.3.0", "@types/node": "*", "jest-mock": "30.3.0" } }, "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw=="], "jest-circus/@jest/types": ["@jest/types@30.3.0", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw=="], @@ -5422,6 +5570,8 @@ "webpack-dev-server/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + "webpack-dev-server/p-retry": ["p-retry@6.2.1", "", { "dependencies": { "@types/retry": "0.12.2", "is-network-error": "^1.0.0", "retry": "^0.13.1" } }, "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ=="], + "webpackbar/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "webpackbar/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -5442,23 +5592,25 @@ "wsl-utils/is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], - "@abapify/adt-codegen/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.2.0", "", {}, "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ=="], + "zod-from-json-schema-v3/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@abapify/adt-codegen/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="], + "@abapify/adt-codegen/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], - "@abapify/adt-diff/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.2.0", "", {}, "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ=="], + "@abapify/adt-codegen/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="], - "@abapify/adt-diff/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="], + "@abapify/adt-diff/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], - "@abapify/adt-export/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.2.0", "", {}, "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ=="], + "@abapify/adt-diff/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="], - "@abapify/adt-export/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="], + "@abapify/adt-export/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], + + "@abapify/adt-export/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="], "@abapify/adt-playwright/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@abapify/adt-tui/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.2.0", "", {}, "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ=="], + "@abapify/adt-tui/fast-xml-parser/path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], - "@abapify/adt-tui/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="], + "@abapify/adt-tui/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="], "@abapify/asjson-parser/jsonc-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], @@ -5480,12 +5632,28 @@ "@docusaurus/core/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@docusaurus/core/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "@docusaurus/core/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "@docusaurus/core/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@docusaurus/core/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "@docusaurus/core/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], "@docusaurus/logger/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@docusaurus/plugin-content-blog/cheerio/htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], + "@docusaurus/utils/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "@docusaurus/utils/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "@docusaurus/utils/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@docusaurus/utils/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "@fission-ai/openspec/ora/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], "@fission-ai/openspec/ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], @@ -5686,6 +5854,14 @@ "@verdaccio/utils/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@xhmikosr/bin-check/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "@xhmikosr/bin-check/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "@xhmikosr/bin-check/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@xhmikosr/bin-check/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "@yarnpkg/parsers/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "ast-kit/@babel/parser/@babel/types": ["@babel/types@8.0.0-rc.2", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0-rc.2", "@babel/helper-validator-identifier": "^8.0.0-rc.2" } }, "sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw=="], @@ -5696,6 +5872,14 @@ "babel-plugin-macros/cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + "bin-version/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "bin-version/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "bin-version/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "bin-version/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "boxen/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -5734,6 +5918,8 @@ "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "expect/jest-util/@jest/types": ["@jest/types@30.3.0", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw=="], "expect/jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -5782,6 +5968,14 @@ "ink/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "jest-changed-files/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "jest-changed-files/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "jest-changed-files/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "jest-changed-files/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "jest-circus/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-circus/jest-runtime/@jest/globals": ["@jest/globals@30.3.0", "", { "dependencies": { "@jest/environment": "30.3.0", "@jest/expect": "30.3.0", "@jest/types": "30.3.0", "jest-mock": "30.3.0" } }, "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA=="], @@ -5938,6 +6132,8 @@ "package-json/got/form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="], + "package-json/got/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "package-json/got/lowercase-keys": ["lowercase-keys@3.0.0", "", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], "package-json/got/p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="], diff --git a/packages/adt-pilot/package.json b/packages/adt-pilot/package.json index 2040ff5e..bce7ecf5 100644 --- a/packages/adt-pilot/package.json +++ b/packages/adt-pilot/package.json @@ -28,12 +28,12 @@ "dependencies": { "@mastra/core": "^1.28.0", "@mastra/mcp": "^1.5.2", - "@abapify/adt-mcp": "0.3.6", + "@modelcontextprotocol/sdk": "^1.27.0", "zod": "^3.24.0" }, "devDependencies": { "@abapify/adt-fixtures": "0.3.6", - "@modelcontextprotocol/sdk": "^1.27.0" + "@abapify/adt-mcp": "0.3.6" }, "repository": { "type": "git", diff --git a/packages/adt-pilot/project.json b/packages/adt-pilot/project.json index 28d59f3e..fdd99c83 100644 --- a/packages/adt-pilot/project.json +++ b/packages/adt-pilot/project.json @@ -9,5 +9,22 @@ } }, "tags": ["scope:adt", "type:agent"], - "targets": {} + "targets": { + "typecheck": { + "executor": "nx:run-commands", + "dependsOn": [], + "inputs": [ + "{projectRoot}/src/**/*", + "{projectRoot}/tsconfig.json", + "{projectRoot}/tsconfig.lib.json", + "{workspaceRoot}/tsconfig.base.json" + ], + "outputs": ["{workspaceRoot}/dist/out-tsc/packages/adt-pilot/**"], + "cache": true, + "options": { + "cwd": "packages/adt-pilot", + "command": "tsc -p tsconfig.lib.json --noEmit" + } + } + } } diff --git a/packages/adt-pilot/src/agent.ts b/packages/adt-pilot/src/agent.ts index 2b31384a..9cb9ed20 100644 --- a/packages/adt-pilot/src/agent.ts +++ b/packages/adt-pilot/src/agent.ts @@ -1,28 +1,49 @@ /** * abapify Pilot – Mastra Agent * - * Creates the `review` agent that powers the Code Review mode in the Harness. - * The agent is wired with MCP tools from `@abapify/adt-mcp` so it can - * directly call any ADT operation. + * Creates the `review` Mastra Agent that powers the Code Review mode in + * the Harness. The agent is wired with MCP tools loaded from + * `@abapify/adt-mcp`, so an LLM can call any ADT operation directly. + * + * The agent is _optional_ for code review use cases: the deterministic + * {@link createCodeReviewWorkflow} performs the same task without an LLM. + * Use the agent when you want natural-language interaction or planning + * on top of the review workflow. */ import { Agent } from '@mastra/core/agent'; import type { ToolsInput } from '@mastra/core/agent'; -/** Configuration for the review agent */ +/** Configuration for the review agent. */ export interface ReviewAgentConfig { /** - * Model identifier (e.g. "openai/gpt-4o", "anthropic/claude-3-5-sonnet"). - * Required by Mastra Agent but not exercised in the Code Review workflow - * (the workflow is deterministic and does not call the LLM). + * Model identifier (e.g. `"openai/gpt-4o"`, + * `"anthropic/claude-3-5-sonnet"`). + * + * Required by Mastra `Agent`. The model is _not_ exercised by the + * deterministic Code Review workflow — it is only invoked when the + * agent is driven directly via the Harness or Mastra runtime. */ model: string; - /** MCP tools loaded from `@abapify/adt-mcp` */ + + /** + * MCP tools made available to the agent. Typically loaded via + * `@mastra/mcp`'s `MCPClient.listTools()` (one entry per `adt-mcp` + * tool such as `list_package_objects`, `atc_run`, `cts_get_transport`). + * + * Pass `undefined` to keep the agent text-only. + */ tools?: ToolsInput; + + /** + * Optional override for the agent's system instructions. Defaults to + * the abapify Pilot system prompt focused on ATC code review. + */ + instructions?: string; } -/** System instructions for the review agent */ -const REVIEW_INSTRUCTIONS = ` +/** Default system instructions for the review agent. */ +export const REVIEW_AGENT_INSTRUCTIONS = ` You are abapify Pilot, an expert ABAP code review assistant powered by SAP ADT. Your task is to analyse ABAP code quality using the ATC (ABAP Test Cockpit) framework. @@ -41,13 +62,14 @@ Always present findings grouped by severity and provide clear remediation guidan /** * Create the Mastra `Agent` for the abapify Pilot review mode. * - * @param config - Agent configuration (model + optional pre-loaded tools) + * @param config Agent configuration — model, optional pre-loaded tools, + * and optional custom instructions. */ export function createReviewAgent(config: ReviewAgentConfig): Agent { return new Agent({ id: 'review', name: 'abapify Pilot – Review', - instructions: REVIEW_INSTRUCTIONS, + instructions: config.instructions ?? REVIEW_AGENT_INSTRUCTIONS, model: config.model, ...(config.tools ? { tools: config.tools } : {}), }); diff --git a/packages/adt-pilot/src/index.ts b/packages/adt-pilot/src/index.ts index 31b0b835..a8330d46 100644 --- a/packages/adt-pilot/src/index.ts +++ b/packages/adt-pilot/src/index.ts @@ -1,23 +1,37 @@ /** * @abapify/adt-pilot – abapify Pilot * - * Mastra AI agent for ABAP code review via ADT MCP. + * Mastra-powered ABAP code review tooling. The package exposes two + * complementary modes that share the same MCP-backed implementation: * - * @example + * 1. **Workflow mode** — a deterministic Mastra `Workflow` + * ({@link createCodeReviewWorkflow}) that runs ATC checks on a package + * or a transport request and returns a structured + * {@link CodeReviewReport}. No LLM is required. + * 2. **Harness mode** — a Mastra `Harness` ({@link createAbapifyPilot}) that + * wraps a `review` Agent so a user can drive code review interactively + * via natural language. The Agent is wired with the same MCP tools + * used by the workflow. + * + * Both modes accept either a package or a transport as the review target. + * + * @example Workflow mode * ```typescript - * import { createAbapifyPilot, createCodeReviewWorkflow, connectMcpClient } from '@abapify/adt-pilot'; + * import { + * createCodeReviewWorkflow, + * connectMcpClient, + * } from '@abapify/adt-pilot'; * import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; * import { createMcpServer } from '@abapify/adt-mcp'; * - * // Set up MCP server + client * const server = createMcpServer(); - * const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + * const [clientTransport, serverTransport] = + * InMemoryTransport.createLinkedPair(); * await server.connect(serverTransport); * const { callTool } = await connectMcpClient(clientTransport); * - * // Create and run the workflow * const workflow = createCodeReviewWorkflow(callTool); - * const run = workflow.createRun(); + * const run = await workflow.createRun(); * const result = await run.start({ * inputData: { * mode: 'package', @@ -27,7 +41,22 @@ * password: 'secret', * }, * }); - * console.log(result.result); // CodeReviewReport + * if (result.status === 'success') { + * console.log(result.result); // CodeReviewReport + * } + * ``` + * + * @example Harness mode + * ```typescript + * import { createAbapifyPilot } from '@abapify/adt-pilot'; + * + * const pilot = createAbapifyPilot({ + * model: 'openai/gpt-4o', + * mcpTools: await mcpClient.listTools(), + * }); + * await pilot.init(); + * await pilot.selectOrCreateThread(); + * await pilot.sendMessage({ content: 'Review package ZPACKAGE on http://sap:8000' }); * ``` */ @@ -35,6 +64,8 @@ export type { ConnectionParams, AtcFinding, + AtcStepResult, + CodeReviewMode, CodeReviewReport, McpToolCaller, } from './types.js'; @@ -45,13 +76,18 @@ export { codeReviewInputSchema, codeReviewOutputSchema, } from './workflow.js'; -export type { CodeReviewInput, CodeReviewWorkflowHandle } from './workflow.js'; +export type { + CodeReviewInput, + CodeReviewWorkflow, + CodeReviewRun, + CodeReviewRunResult, +} from './workflow.js'; // MCP client factory export { createMcpToolCaller, connectMcpClient } from './mcp-client.js'; // Agent -export { createReviewAgent } from './agent.js'; +export { createReviewAgent, REVIEW_AGENT_INSTRUCTIONS } from './agent.js'; export type { ReviewAgentConfig } from './agent.js'; // Harness diff --git a/packages/adt-pilot/src/mcp-client.ts b/packages/adt-pilot/src/mcp-client.ts index 90d5f224..d9647138 100644 --- a/packages/adt-pilot/src/mcp-client.ts +++ b/packages/adt-pilot/src/mcp-client.ts @@ -1,24 +1,38 @@ /** * MCP client factory * - * Wraps `@modelcontextprotocol/sdk`'s `Client` in the `McpToolCaller` - * interface used by `createCodeReviewWorkflow`. + * Wraps `@modelcontextprotocol/sdk`'s `Client` in the {@link McpToolCaller} + * interface used by the Code Review workflow. * - * In production the client connects to the `adt-mcp` binary via stdio or - * HTTP. In tests an in-process `InMemoryTransport` is used instead. + * In production a process-spawning transport (stdio) or HTTP transport + * connects to the `adt-mcp` binary. In tests the + * `InMemoryTransport.createLinkedPair()` pattern lets you wire an + * in-process `adt-mcp` server directly to the client — see + * `tests/workflow.test.ts` for the canonical example. */ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import type { McpToolCaller } from './types.js'; +interface TextContentBlock { + type: 'text'; + text: string; +} + /** - * Wrap an already-connected MCP SDK `Client` as a `McpToolCaller`. + * Wrap an already-connected MCP SDK `Client` as a {@link McpToolCaller}. * - * The caller parses the first `text` content item of the tool response as - * JSON. If the tool returns `isError: true`, an `Error` is thrown. + * The wrapper: + * - Reads the first `text` content item of the tool response. + * - Throws an `Error` (with the response text as message) when the tool + * sets `isError: true`, so the workflow's per-object error path can + * record it as a synthetic `priority: 'error'` finding. + * - Returns the parsed JSON object when the response body is valid JSON. + * - Returns the raw string otherwise (including when the response body + * is empty). * - * @param client - A connected `@modelcontextprotocol/sdk` `Client` + * @param client A connected `@modelcontextprotocol/sdk` `Client`. */ export function createMcpToolCaller(client: Client): McpToolCaller { return async (toolName: string, args: Record) => { @@ -27,40 +41,72 @@ export function createMcpToolCaller(client: Client): McpToolCaller { arguments: args, }); - const content = result.content as Array<{ type: string; text: string }>; - const text = content[0]?.text ?? ''; + const text = extractFirstTextBlock(result.content); - // Propagate tool-level errors as thrown exceptions so the workflow can - // catch them and record error findings. if (result.isError) { throw new Error(text || `MCP tool "${toolName}" returned an error`); } - try { - return JSON.parse(text) as unknown; - } catch { - return text; - } + return tryParseJson(text); }; } /** - * Connect a Mastra MCP `Client` to the given transport and return a - * `McpToolCaller` backed by that connection. - * - * The caller is responsible for closing the transport when done. + * Connect a fresh `@modelcontextprotocol/sdk` `Client` to the given + * transport and return both the resulting {@link McpToolCaller} and the + * underlying client. The caller is responsible for closing the client + * (and transport) when finished. * * @example - * ```typescript - * const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + * ```ts + * const [clientTransport, serverTransport] = + * InMemoryTransport.createLinkedPair(); * await mcpServer.connect(serverTransport); - * const callTool = await connectMcpClient(clientTransport); + * + * const { callTool, client } = await connectMcpClient(clientTransport); + * try { + * await callTool('list_package_objects', { packageName: 'ZPACKAGE', ... }); + * } finally { + * await client.close(); + * } * ``` */ export async function connectMcpClient( transport: Transport, + clientInfo: { name?: string; version?: string } = {}, ): Promise<{ callTool: McpToolCaller; client: Client }> { - const client = new Client({ name: 'adt-pilot', version: '0.1.0' }); + const client = new Client({ + name: clientInfo.name ?? 'adt-pilot', + version: clientInfo.version ?? '0.1.0', + }); await client.connect(transport); return { callTool: createMcpToolCaller(client), client }; } + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function extractFirstTextBlock(content: unknown): string { + if (!Array.isArray(content)) return ''; + for (const block of content) { + if ( + block && + typeof block === 'object' && + (block as TextContentBlock).type === 'text' && + typeof (block as TextContentBlock).text === 'string' + ) { + return (block as TextContentBlock).text; + } + } + return ''; +} + +function tryParseJson(text: string): unknown { + if (text === '') return ''; + try { + return JSON.parse(text) as unknown; + } catch { + return text; + } +} diff --git a/packages/adt-pilot/src/types.ts b/packages/adt-pilot/src/types.ts index adaf0935..69ac0d12 100644 --- a/packages/adt-pilot/src/types.ts +++ b/packages/adt-pilot/src/types.ts @@ -1,58 +1,104 @@ /** - * abapify Pilot – types + * abapify Pilot – public types * - * Shared types for the Code Review workflow and the Harness agent. + * Shared types for the Code Review workflow and the Mastra Harness agent. */ -/** SAP ADT connection parameters – forwarded as tool arguments on every MCP call. */ +/** + * SAP ADT connection parameters. + * + * Forwarded as MCP tool arguments on every call. The package never persists + * these credentials — they must be passed as part of every workflow input. + */ export interface ConnectionParams { + /** Base URL of the SAP system (e.g. `http://sap:8000`) */ baseUrl: string; + /** SAP user name */ username: string; + /** SAP user password */ password: string; - /** SAP client number (e.g. "100"). Optional. */ + /** Optional SAP client number (e.g. `"100"`) */ client?: string; } -/** A single ATC finding returned by the Code Review workflow. */ +/** + * Discriminator for {@link CodeReviewReport.mode} and the workflow input. + */ +export type CodeReviewMode = 'package' | 'transport'; + +/** A single ATC finding produced by the Code Review workflow. */ export interface AtcFinding { /** ADT URI of the object that triggered the finding */ objectUri: string; - /** Severity / priority (e.g. "1", "2", "warning", "error") */ + /** + * Severity / priority. Either a numeric ATC priority (`"1"`, `"2"`, + * `"3"`) or `"error"` for synthetic findings created when an MCP tool + * call failed. + */ priority: string; /** Human-readable description of the finding */ description: string; - /** ATC check category (e.g. "PERFORMANCE", "SECURITY") */ + /** Optional ATC check category (e.g. `PERFORMANCE`, `SECURITY`) */ category?: string; - /** Name of the ATC check that raised the finding */ + /** Optional name of the ATC check that raised the finding */ checkName?: string; /** Optional source location within the object */ location?: string; } +/** + * Per-object intermediate result produced by the `runAtcChecks` step. + * + * On `success` the raw `worklist` is preserved so {@link AtcFinding} + * extraction can run in the next step. On `error` we capture the failure + * message so the workflow can surface it as a synthetic + * `priority: 'error'` finding. + */ +export interface AtcStepResult { + /** Object URI the ATC check ran against */ + objectUri: string; + /** Whether the per-object ATC call succeeded or threw */ + status: 'success' | 'error'; + /** Raw worklist response (only present when `status === 'success'`) */ + worklist?: unknown; + /** Error message captured when `status === 'error'` */ + error?: string; +} + /** Structured output of the Code Review workflow. */ export interface CodeReviewReport { - /** Input mode used for this review */ - mode: 'package' | 'transport'; - /** The reviewed target (package name or transport number) */ + /** Mode used for this run */ + mode: CodeReviewMode; + /** The reviewed target — package name or transport number */ target: string; - /** Resolved ADT object URIs that were checked */ + /** Resolved ADT object URIs that ATC ran against */ objects: string[]; - /** All ATC findings across all objects */ + /** All ATC findings collected across every checked object */ findings: AtcFinding[]; - /** Aggregated statistics */ + /** Aggregated statistics for quick consumption */ summary: { + /** Number of object URIs ATC was invoked on */ totalObjects: number; + /** Total number of findings (including synthetic error entries) */ totalFindings: number; - /** Findings grouped by priority/severity */ + /** Findings grouped by `priority` */ bySeverity: Record; }; } /** - * Minimal interface for calling an MCP tool and getting its result. + * Minimal interface for invoking an MCP tool and returning its parsed + * response. Implementations should: + * + * 1. Forward the response body as-is when it is plain text. + * 2. `JSON.parse()` it when the tool returned a JSON document. + * 3. Throw an `Error` whenever the tool's `isError` flag is set so the + * workflow can record the failure. * - * In production this is implemented by wrapping `@modelcontextprotocol/sdk`'s - * `Client.callTool()`. In tests it is backed by an in-process mock. + * In production this is implemented by wrapping + * `@modelcontextprotocol/sdk`'s `Client.callTool()` (see + * {@link createMcpToolCaller}). In tests it can be implemented inline as + * a stub. */ export type McpToolCaller = ( toolName: string, diff --git a/packages/adt-pilot/src/workflow.ts b/packages/adt-pilot/src/workflow.ts index 673a7de0..8376df37 100644 --- a/packages/adt-pilot/src/workflow.ts +++ b/packages/adt-pilot/src/workflow.ts @@ -1,68 +1,137 @@ /** * Code Review Workflow * - * A Mastra workflow that runs ATC-based code review on an ABAP package - * hierarchy or a transport request. + * A Mastra workflow that runs ATC-based code review on either an ABAP + * package hierarchy or a transport request. The workflow is deterministic + * and does not require an LLM — it orchestrates calls to the + * `@abapify/adt-mcp` MCP server via a thin {@link McpToolCaller} interface. * - * Usage: - * // Package mode - * const workflow = createCodeReviewWorkflow(callTool); - * const result = await workflow.execute({ - * mode: 'package', packageName: 'ZPACKAGE', - * baseUrl: '...', username: '...', password: '...' - * }); + * The workflow has exactly three steps executed in sequence: * - * // Transport mode - * const result = await workflow.execute({ - * mode: 'transport', transportNumber: 'DEVK900001', - * baseUrl: '...', username: '...', password: '...' - * }); + * resolveObjects → runAtcChecks → buildReport + * + * Inputs are validated against a Zod discriminated union and the output + * conforms to the {@link CodeReviewReport} schema. + * + * @example + * ```ts + * const workflow = createCodeReviewWorkflow(callTool); + * const run = await workflow.createRun(); + * const result = await run.start({ + * inputData: { + * mode: 'package', + * packageName: 'ZPACKAGE', + * baseUrl: 'http://sap:8000', + * username: 'DEVELOPER', + * password: 'secret', + * }, + * }); + * if (result.status === 'success') { + * console.log(result.result); // CodeReviewReport + * } + * ``` */ -import { createWorkflow, createStep } from '@mastra/core/workflows'; +import { + createWorkflow as createWorkflowRaw, + createStep as createStepRaw, +} from '@mastra/core/workflows'; import { z } from 'zod'; -import type { AtcFinding, CodeReviewReport, McpToolCaller } from './types.js'; +import type { + AtcFinding, + AtcStepResult, + CodeReviewReport, + McpToolCaller, +} from './types.js'; + +// Type-erase the Mastra workflow/step factories at the import boundary. +// See "Internal type-erasure aliases" below for the rationale. +const createWorkflow = createWorkflowRaw as unknown as (config: { + id: string; + description: string; + inputSchema: unknown; + outputSchema: unknown; +}) => AnyWorkflowBuilder; + +// The `execute` callback is intentionally typed permissively. Our helper +// functions infer `inputData` from each step's Zod input schema and return +// the appropriate output object — the schemas remain the source of truth +// at runtime. +type AnyStepExecute = (args: { inputData: never }) => Promise; + +const createStep = createStepRaw as unknown as (config: { + id: string; + description: string; + inputSchema: unknown; + outputSchema: unknown; + execute: AnyStepExecute; +}) => AnyStep; + +// --------------------------------------------------------------------------- +// Internal type-erasure aliases +// --------------------------------------------------------------------------- +// +// Mastra's `Workflow`/`Step` types are extremely deeply generic. Letting +// TypeScript fully instantiate them in this file blows past the default +// `tsc` heap (4 GB) and bloats the generated `.d.ts`. We treat steps and +// the workflow builder as opaque values here — the runtime correctness +// of the chain is enforced by the Zod schemas, and the public surface +// is exposed via the narrower {@link CodeReviewWorkflow} interface. + +type AnyStep = unknown; +interface AnyWorkflowBuilder { + then(step: AnyStep): AnyWorkflowBuilder; + commit(): unknown; +} // --------------------------------------------------------------------------- -// Zod schemas +// Public schemas // --------------------------------------------------------------------------- -const connectionSchema = z.object({ +const connectionShape = { baseUrl: z.string().url(), - username: z.string(), - password: z.string(), + username: z.string().min(1), + password: z.string().min(1), client: z.string().optional(), -}); +} as const; -/** Workflow input schema */ +/** + * Workflow input schema — discriminated union on `mode`. + * + * `package` mode requires `packageName`, `transport` mode requires + * `transportNumber`. Connection credentials are required on every run. + */ export const codeReviewInputSchema = z.discriminatedUnion('mode', [ - connectionSchema.extend({ + z.object({ mode: z.literal('package'), - packageName: z.string(), + packageName: z.string().min(1), + ...connectionShape, }), - connectionSchema.extend({ + z.object({ mode: z.literal('transport'), - transportNumber: z.string(), + transportNumber: z.string().min(1), + ...connectionShape, }), ]); +/** Inferred TypeScript type of the workflow input. */ export type CodeReviewInput = z.infer; -/** Workflow output schema */ +const findingSchema = z.object({ + objectUri: z.string(), + priority: z.string(), + description: z.string(), + category: z.string().optional(), + checkName: z.string().optional(), + location: z.string().optional(), +}); + +/** Workflow output schema (CodeReviewReport). */ export const codeReviewOutputSchema = z.object({ mode: z.enum(['package', 'transport']), target: z.string(), objects: z.array(z.string()), - findings: z.array( - z.object({ - objectUri: z.string(), - priority: z.string(), - description: z.string(), - category: z.string().optional(), - checkName: z.string().optional(), - location: z.string().optional(), - }), - ), + findings: z.array(findingSchema), summary: z.object({ totalObjects: z.number(), totalFindings: z.number(), @@ -71,66 +140,67 @@ export const codeReviewOutputSchema = z.object({ }); // --------------------------------------------------------------------------- -// Step 1 – resolveObjects +// Internal step schemas // --------------------------------------------------------------------------- -/** - * Resolve the list of object URIs to check. - * - * - Package mode: calls `list_package_objects` and extracts URIs - * - Transport mode: calls `cts_get_transport` to validate, then uses the - * transport URI so ATC can run on the whole transport in one call - */ -const resolveObjectsInputSchema = z.object({ +/** Output of step 1 = input of step 2. */ +const resolveObjectsOutputSchema = z.object({ mode: z.enum(['package', 'transport']), - packageName: z.string().optional(), - transportNumber: z.string().optional(), - baseUrl: z.string(), - username: z.string(), - password: z.string(), - client: z.string().optional(), + target: z.string(), + objects: z.array(z.string()), + ...connectionShape, }); -const resolveObjectsOutputSchema = z.object({ +const atcStepResultSchema = z.object({ + objectUri: z.string(), + status: z.enum(['success', 'error']), + worklist: z.unknown().optional(), + error: z.string().optional(), +}); + +/** Output of step 2 = input of step 3. */ +const runAtcChecksOutputSchema = z.object({ mode: z.enum(['package', 'transport']), target: z.string(), objects: z.array(z.string()), - baseUrl: z.string(), - username: z.string(), - password: z.string(), - client: z.string().optional(), + atcResults: z.array(atcStepResultSchema), }); +// --------------------------------------------------------------------------- +// Step 1 – resolveObjects +// --------------------------------------------------------------------------- + +/** + * Resolve the list of object URIs to check. + * + * - **Package mode** — calls `list_package_objects` and extracts every + * `uri` field from the returned `objects[]` array. + * - **Transport mode** — calls `cts_get_transport` to validate that the + * transport exists. The transport request URI itself is then used as + * the ATC target — SAP ATC enumerates contained objects server-side. + * This matches the behaviour of `adt check --transport `. + */ function createResolveObjectsStep(callTool: McpToolCaller) { return createStep({ id: 'resolveObjects', description: 'Resolve ABAP object URIs from package or transport', - inputSchema: resolveObjectsInputSchema, + inputSchema: codeReviewInputSchema, outputSchema: resolveObjectsOutputSchema, - execute: async ({ inputData }) => { - const conn = { - baseUrl: inputData.baseUrl, - username: inputData.username, - password: inputData.password, - ...(inputData.client != null ? { client: inputData.client } : {}), - }; + execute: async ({ + inputData, + }: { + inputData: z.infer; + }) => { + const conn = stripConnection(inputData); if (inputData.mode === 'package') { - const packageName = inputData.packageName ?? ''; - const result = (await callTool('list_package_objects', { + const packageName = inputData.packageName; + const result = await callTool('list_package_objects', { ...conn, packageName, - })) as Record | null; - - const rawObjects = - result && Array.isArray(result.objects) ? result.objects : []; + }); - const objects = rawObjects - .map((o: unknown) => { - const obj = o as Record; - return typeof obj.uri === 'string' ? obj.uri : null; - }) - .filter((uri): uri is string => uri !== null); + const objects = extractObjectUris(result); return { mode: 'package' as const, @@ -138,22 +208,23 @@ function createResolveObjectsStep(callTool: McpToolCaller) { objects, ...conn, }; - } else { - // Transport mode: validate transport exists, then use transport URI - const transportNumber = inputData.transportNumber ?? ''; - await callTool('cts_get_transport', { - ...conn, - transport: transportNumber, - }); - - const transportUri = `/sap/bc/adt/cts/transportrequests/${transportNumber}`; - return { - mode: 'transport' as const, - target: transportNumber, - objects: [transportUri], - ...conn, - }; } + + // Transport mode — validate the transport exists, then use the + // transport URI as the ATC target. + const transportNumber = inputData.transportNumber; + await callTool('cts_get_transport', { + ...conn, + transport: transportNumber, + }); + + const transportUri = `/sap/bc/adt/cts/transportrequests/${transportNumber}`; + return { + mode: 'transport' as const, + target: transportNumber, + objects: [transportUri], + ...conn, + }; }, }); } @@ -162,62 +233,41 @@ function createResolveObjectsStep(callTool: McpToolCaller) { // Step 2 – runAtcChecks // --------------------------------------------------------------------------- -const atcResultSchema = z.object({ - objectUri: z.string(), - status: z.enum(['success', 'error']), - worklist: z.unknown().optional(), - error: z.string().optional(), -}); - -const runAtcChecksInputSchema = z.object({ - mode: z.enum(['package', 'transport']), - target: z.string(), - objects: z.array(z.string()), - baseUrl: z.string(), - username: z.string(), - password: z.string(), - client: z.string().optional(), -}); - -const runAtcChecksOutputSchema = z.object({ - mode: z.enum(['package', 'transport']), - target: z.string(), - objects: z.array(z.string()), - atcResults: z.array(atcResultSchema), -}); - +/** + * Run `atc_run` for each resolved object URI. + * + * The MCP tool layer returns a worklist on success or throws on failure. + * We capture failures as per-object error entries so the workflow always + * completes — partial failures are surfaced as `priority: 'error'` + * findings in the final report. + */ function createRunAtcChecksStep(callTool: McpToolCaller) { return createStep({ id: 'runAtcChecks', description: 'Run ATC checks on each resolved object URI', - inputSchema: runAtcChecksInputSchema, + inputSchema: resolveObjectsOutputSchema, outputSchema: runAtcChecksOutputSchema, - execute: async ({ inputData }) => { - const conn = { - baseUrl: inputData.baseUrl, - username: inputData.username, - password: inputData.password, - ...(inputData.client != null ? { client: inputData.client } : {}), - }; - - const atcResults = []; + execute: async ({ + inputData, + }: { + inputData: z.infer; + }) => { + const conn = stripConnection(inputData); + const atcResults: AtcStepResult[] = []; for (const objectUri of inputData.objects) { try { - const result = (await callTool('atc_run', { - ...conn, - objectUri, - })) as Record | null; - + const result = await callTool('atc_run', { ...conn, objectUri }); + const worklist = pickWorklist(result); atcResults.push({ objectUri, - status: 'success' as const, - worklist: result?.worklist, + status: 'success', + ...(worklist !== undefined ? { worklist } : {}), }); } catch (error) { atcResults.push({ objectUri, - status: 'error' as const, + status: 'error', error: error instanceof Error ? error.message : String(error), }); } @@ -237,25 +287,28 @@ function createRunAtcChecksStep(callTool: McpToolCaller) { // Step 3 – buildReport // --------------------------------------------------------------------------- -const buildReportInputSchema = z.object({ - mode: z.enum(['package', 'transport']), - target: z.string(), - objects: z.array(z.string()), - atcResults: z.array(atcResultSchema), -}); - +/** + * Aggregate ATC step results into a {@link CodeReviewReport}. + * + * Each successful ATC result is parsed for findings via {@link extractFindings}. + * Each error result produces a synthetic `priority: 'error'` finding + * containing the captured error message. + */ function createBuildReportStep() { return createStep({ id: 'buildReport', description: 'Aggregate ATC results into a CodeReviewReport', - inputSchema: buildReportInputSchema, + inputSchema: runAtcChecksOutputSchema, outputSchema: codeReviewOutputSchema, - execute: async ({ inputData }) => { + execute: async ({ + inputData, + }: { + inputData: z.infer; + }) => { const findings: AtcFinding[] = []; for (const result of inputData.atcResults) { if (result.status === 'error') { - // Error finding – insert a synthetic finding so the caller knows findings.push({ objectUri: result.objectUri, priority: 'error', @@ -264,12 +317,9 @@ function createBuildReportStep() { continue; } - // Parse worklist findings - const extracted = extractFindings(result.objectUri, result.worklist); - findings.push(...extracted); + findings.push(...extractFindings(result.objectUri, result.worklist)); } - // Severity summary const bySeverity: Record = {}; for (const f of findings) { bySeverity[f.priority] = (bySeverity[f.priority] ?? 0) + 1; @@ -293,18 +343,82 @@ function createBuildReportStep() { } // --------------------------------------------------------------------------- -// Worklist parsing helper +// Helpers // --------------------------------------------------------------------------- +/** Pull the connection shape out of a step input, dropping mode-specific fields. */ +function stripConnection(input: { + baseUrl: string; + username: string; + password: string; + client?: string | undefined; +}): { baseUrl: string; username: string; password: string; client?: string } { + return { + baseUrl: input.baseUrl, + username: input.username, + password: input.password, + ...(input.client !== undefined ? { client: input.client } : {}), + }; +} + /** - * Safely extract AtcFinding[] from a raw worklist response. + * Extract `objects[].uri` from the `list_package_objects` response. * - * The worklist may be shaped as: - * { worklist: { objects: { object: [...] } } } ← successful run - * { worklistRun: { ... } } ← run-only response (no findings yet) - * null / undefined ← no result + * The MCP tool returns: + * ``` + * { packageName, count, objects: [{ uri, name?, type?, packageName? }, ...] } + * ``` * - * We do best-effort parsing and return an empty array on failure. + * Anything unrecognised is silently ignored — the goal is best-effort + * resilience against minor response shape changes. + */ +function extractObjectUris(response: unknown): string[] { + if (!response || typeof response !== 'object') return []; + const root = response as Record; + const rawObjects = Array.isArray(root.objects) ? root.objects : []; + + const uris: string[] = []; + for (const obj of rawObjects) { + if (obj && typeof obj === 'object') { + const rec = obj as Record; + if (typeof rec.uri === 'string' && rec.uri.length > 0) { + uris.push(rec.uri); + } + } + } + return uris; +} + +/** + * Pull the `worklist` field out of an ATC tool response. + * + * The `atc_run` MCP tool returns one of: + * ``` + * { status: 'completed', worklist: { ... } } // findings present + * { status: 'completed', findings: [], raw: { ... } } // no worklist id + * ``` + */ +function pickWorklist(response: unknown): unknown { + if (!response || typeof response !== 'object') return undefined; + const rec = response as Record; + return rec.worklist; +} + +/** + * Best-effort extraction of {@link AtcFinding} entries from an ATC worklist. + * + * The worklist may take any of these shapes (depending on parser): + * ``` + * { worklist: { objects: { object: [...] } } } + * { objects: { object: [...] } } + * { objects: { object: { ... } } } // single object collapsed + * ``` + * + * Within each `object` entry, findings may live under `findings.finding` + * either as an array or a single object. We normalise both shapes. + * + * Returns an empty array on any unexpected structure rather than throwing + * — the caller surfaces tool errors via the `error` path instead. */ function extractFindings( fallbackObjectUri: string, @@ -313,40 +427,30 @@ function extractFindings( if (!worklist || typeof worklist !== 'object') return []; const wl = worklist as Record; - - // Normalise: unwrap top-level "worklist" key if present const inner = wl.worklist && typeof wl.worklist === 'object' ? (wl.worklist as Record) : wl; - const objectsContainer = inner.objects as Record | undefined; - if (!objectsContainer) return []; + const objectsContainer = inner.objects; + if (!objectsContainer || typeof objectsContainer !== 'object') return []; - const rawObjects = objectsContainer.object; - const objectArray = Array.isArray(rawObjects) - ? rawObjects - : rawObjects != null - ? [rawObjects] - : []; + const rawObjects = (objectsContainer as Record).object; + const objectArray = toArray(rawObjects); const findings: AtcFinding[] = []; - for (const obj of objectArray) { + if (!obj || typeof obj !== 'object') continue; const o = obj as Record; - const objectUri = typeof o.uri === 'string' ? o.uri : fallbackObjectUri; - - const findingsContainer = o.findings as Record | undefined; - if (!findingsContainer) continue; + const objectUri = + typeof o.uri === 'string' && o.uri.length > 0 ? o.uri : fallbackObjectUri; - const rawFindings = findingsContainer.finding; - const findingArray = Array.isArray(rawFindings) - ? rawFindings - : rawFindings != null - ? [rawFindings] - : []; + const findingsContainer = o.findings; + if (!findingsContainer || typeof findingsContainer !== 'object') continue; - for (const f of findingArray) { + const rawFindings = (findingsContainer as Record).finding; + for (const f of toArray(rawFindings)) { + if (!f || typeof f !== 'object') continue; const finding = f as Record; findings.push({ objectUri, @@ -357,64 +461,99 @@ function extractFindings( finding.description ?? '', ), - category: - typeof finding.checkTitle === 'string' - ? finding.checkTitle - : undefined, - checkName: - typeof finding.checkId === 'string' ? finding.checkId : undefined, - location: - typeof finding.location === 'string' ? finding.location : undefined, + ...(typeof finding.checkTitle === 'string' + ? { category: finding.checkTitle } + : {}), + ...(typeof finding.checkId === 'string' + ? { checkName: finding.checkId } + : {}), + ...(typeof finding.location === 'string' + ? { location: finding.location } + : {}), }); } } - return findings; } +/** Normalise SAP-style "single-or-array" XML mappings into a plain array. */ +function toArray(value: unknown): unknown[] { + if (Array.isArray(value)) return value; + if (value === undefined || value === null) return []; + return [value]; +} + // --------------------------------------------------------------------------- -// Factory +// Public workflow handle type // --------------------------------------------------------------------------- /** - * Minimal public type for the workflow handle returned by - * `createCodeReviewWorkflow`. + * Result returned by {@link CodeReviewRun.start}. Mirrors the relevant + * subset of Mastra's `WorkflowResult` so consumers don't need to import + * `@mastra/core`. + */ +export type CodeReviewRunResult = + | { status: 'success'; result: CodeReviewReport } + | { status: 'failed'; error: Error } + | { status: 'suspended' | 'paused' | 'tripwire' }; + +/** + * Public handle for an in-flight workflow run. * - * Wraps the real Mastra `Workflow<...>` in a simple interface so the OXC - * declaration generator does not need to expand the deeply generic Mastra - * type parameters (which would cause OOM in the DTS build step). + * Wraps Mastra's `Run` instance with a narrow surface so DTS generation + * doesn't have to expand the deeply generic Mastra type parameters + * (which causes OOM in `rolldown-plugin-dts`). */ -export interface CodeReviewWorkflowHandle { - createRun(): { - start(opts: { inputData: CodeReviewInput }): Promise<{ - status: string; - result: unknown; - }>; - }; +export interface CodeReviewRun { + start(args: { inputData: CodeReviewInput }): Promise; } +/** + * Public handle for a committed workflow. + * + * Calling {@link CodeReviewWorkflow.createRun} returns a {@link CodeReviewRun} + * (asynchronously) — Mastra needs to acquire a runId before returning. + */ +export interface CodeReviewWorkflow { + createRun(): Promise; +} + +// --------------------------------------------------------------------------- +// Factory +// --------------------------------------------------------------------------- + /** * Create the Code Review workflow, bound to the given MCP tool caller. * - * @param callTool Function that calls a named MCP tool and returns the parsed - * JSON response. + * The returned workflow can be executed many times — each call to + * `createRun()` produces an independent run. The workflow is stateless; + * connection credentials must be passed as part of every input. + * + * @param callTool Function that invokes a named MCP tool and returns the + * parsed JSON response. Implementations should throw on + * tool-level errors so the workflow can record them. */ export function createCodeReviewWorkflow( callTool: McpToolCaller, -): CodeReviewWorkflowHandle { +): CodeReviewWorkflow { const resolveObjectsStep = createResolveObjectsStep(callTool); const runAtcChecksStep = createRunAtcChecksStep(callTool); const buildReportStep = createBuildReportStep(); - return createWorkflow({ + const workflow = createWorkflow({ id: 'code-review', description: 'Run ATC-based code review on a package hierarchy or transport request', - inputSchema: resolveObjectsInputSchema, + inputSchema: codeReviewInputSchema, outputSchema: codeReviewOutputSchema, }) .then(resolveObjectsStep) .then(runAtcChecksStep) .then(buildReportStep) - .commit() as unknown as CodeReviewWorkflowHandle; + .commit(); + + // The runtime methods we expose match Mastra's `Workflow` exactly — we + // simply present a narrower public type to avoid leaking the deeply + // generic Mastra type parameters into the DTS bundle. + return workflow as unknown as CodeReviewWorkflow; } diff --git a/packages/adt-pilot/tests/harness.test.ts b/packages/adt-pilot/tests/harness.test.ts new file mode 100644 index 00000000..f40d756d --- /dev/null +++ b/packages/adt-pilot/tests/harness.test.ts @@ -0,0 +1,65 @@ +/** + * Harness factory smoke tests. + * + * The Mastra Harness needs storage to fully initialise; we don't + * exercise that here. These tests validate that: + * - the factory accepts the documented config shape, + * - it returns a real `Harness` instance, + * - the `review` mode exists and is wired to a Mastra `Agent`. + * + * Anything that requires network or LLM access is intentionally out of + * scope — those flows are covered by the workflow integration tests. + */ + +import { describe, it, expect } from 'vitest'; +import { Harness } from '@mastra/core/harness'; +import { Agent } from '@mastra/core/agent'; +import { + createAbapifyPilot, + createReviewAgent, + REVIEW_AGENT_INSTRUCTIONS, +} from '../src/index.js'; + +describe('createAbapifyPilot', () => { + it('returns a Harness instance', () => { + const pilot = createAbapifyPilot({ model: 'openai/gpt-4o' }); + + expect(pilot).toBeInstanceOf(Harness); + // The internal mode list is private, but `getDisplayState()` is part + // of the public surface and must work without initialisation — + // serves as a smoke test that the Harness was constructed correctly. + expect(() => pilot.getDisplayState()).not.toThrow(); + }); + + it('accepts MCP tools without throwing', () => { + const pilot = createAbapifyPilot({ + model: 'openai/gpt-4o', + mcpTools: {}, + }); + expect(pilot).toBeInstanceOf(Harness); + }); +}); + +describe('createReviewAgent', () => { + it('returns a Mastra Agent with the documented defaults', () => { + const agent = createReviewAgent({ model: 'openai/gpt-4o' }); + + expect(agent).toBeInstanceOf(Agent); + expect(agent.id).toBe('review'); + expect(agent.name).toBe('abapify Pilot – Review'); + }); + + it('uses the default REVIEW_AGENT_INSTRUCTIONS when none are provided', () => { + expect(REVIEW_AGENT_INSTRUCTIONS).toContain('abapify Pilot'); + expect(REVIEW_AGENT_INSTRUCTIONS).toContain('list_package_objects'); + expect(REVIEW_AGENT_INSTRUCTIONS).toContain('atc_run'); + }); + + it('honours a custom instructions override', () => { + const agent = createReviewAgent({ + model: 'openai/gpt-4o', + instructions: 'You are a strict ABAP code reviewer.', + }); + expect(agent).toBeInstanceOf(Agent); + }); +}); diff --git a/packages/adt-pilot/tests/types.test.ts b/packages/adt-pilot/tests/types.test.ts index 2e700a7f..b40275dc 100644 --- a/packages/adt-pilot/tests/types.test.ts +++ b/packages/adt-pilot/tests/types.test.ts @@ -2,10 +2,14 @@ * Type validation tests for CodeReviewReport schema */ +import { randomBytes } from 'node:crypto'; import { describe, it, expect } from 'vitest'; import { z } from 'zod'; import { codeReviewOutputSchema, codeReviewInputSchema } from '../src/index.js'; +// Generated per-process; never a real credential. +const TEST_SECRET = randomBytes(8).toString('hex'); + describe('codeReviewOutputSchema', () => { it('accepts a valid package-mode report', () => { const report = { @@ -87,7 +91,7 @@ describe('codeReviewInputSchema', () => { packageName: 'ZPACKAGE', baseUrl: 'http://sap:8000', username: 'DEVELOPER', - password: 'secret', + password: TEST_SECRET, }; expect(() => codeReviewInputSchema.parse(input)).not.toThrow(); }); @@ -98,7 +102,7 @@ describe('codeReviewInputSchema', () => { transportNumber: 'DEVK900001', baseUrl: 'http://sap:8000', username: 'DEVELOPER', - password: 'secret', + password: TEST_SECRET, client: '100', }; expect(() => codeReviewInputSchema.parse(input)).not.toThrow(); @@ -109,7 +113,7 @@ describe('codeReviewInputSchema', () => { mode: 'package', packageName: 'ZPACKAGE', username: 'DEVELOPER', - password: 'secret', + password: TEST_SECRET, }; expect(() => codeReviewInputSchema.parse(input)).toThrow(z.ZodError); }); @@ -120,7 +124,7 @@ describe('codeReviewInputSchema', () => { packageName: 'ZPACKAGE', baseUrl: 'http://sap:8000', username: 'DEVELOPER', - password: 'secret', + password: TEST_SECRET, }; expect(() => codeReviewInputSchema.parse(input)).toThrow(z.ZodError); }); diff --git a/packages/adt-pilot/tests/workflow.test.ts b/packages/adt-pilot/tests/workflow.test.ts index c119363b..996351e9 100644 --- a/packages/adt-pilot/tests/workflow.test.ts +++ b/packages/adt-pilot/tests/workflow.test.ts @@ -1,16 +1,19 @@ /** * Workflow integration tests for @abapify/adt-pilot * - * Tests the three workflow modes: - * - Package mode happy path - * - Transport mode happy path - * - Empty package → empty report - * - Partial ATC failure → report with error finding + * Covers both code-review modes end-to-end: + * - **Package mode** — happy path + empty-package edge case. + * - **Transport mode** — happy path + transport-URI assertion. + * - **Partial / total ATC failure** — workflow continues and surfaces + * error findings. * - * Uses @abapify/adt-fixtures mock ADT server + @abapify/adt-mcp - * connected via InMemoryTransport (same pattern as adt-mcp integration tests). + * The setup wires `@abapify/adt-mcp` to an in-memory `@abapify/adt-fixtures` + * mock ADT server through `InMemoryTransport`, then drives the workflow + * via the public `createCodeReviewWorkflow` factory. No real network + * traffic and no LLM calls. */ +import { randomBytes } from 'node:crypto'; import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; @@ -29,13 +32,17 @@ let mockPort: number; let mcpClient: Client; let callTool: McpToolCaller; -const MOCK_PASSWORD = 'test-password-1234'; +// The mock ADT server accepts any credential — we generate a random +// secret per test process so no literal credentials live in the repo +// (also keeps SonarCloud security-hotspot scanners happy). +const MOCK_USER = 'DEVELOPER'; +const MOCK_SECRET = randomBytes(16).toString('hex'); function connArgs() { return { baseUrl: `http://localhost:${mockPort}`, - username: 'DEVELOPER', - password: MOCK_PASSWORD, + username: MOCK_USER, + password: MOCK_SECRET, client: '100', }; } @@ -73,27 +80,48 @@ afterAll(async () => { await mockAdt.stop(); }); +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +async function runReview( + caller: McpToolCaller, + inputData: Parameters< + Awaited< + ReturnType['createRun']> + >['start'] + >[0]['inputData'], +) { + const workflow = createCodeReviewWorkflow(caller); + const run = await workflow.createRun(); + return run.start({ inputData }); +} + +function expectSuccess( + result: Awaited>, +): CodeReviewReport { + expect(result.status).toBe('success'); + // Narrow without assertions in the assertion: only `success` carries `result`. + if (result.status !== 'success') { + throw new Error(`Expected success, got ${result.status}`); + } + return result.result; +} + // --------------------------------------------------------------------------- // Package mode tests // --------------------------------------------------------------------------- describe('Code Review Workflow – package mode', () => { it('returns a CodeReviewReport for a package with objects', async () => { - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZPACKAGE', - ...connArgs(), - }, + const result = await runReview(callTool, { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), }); - expect(result.status).toBe('success'); - const report = result.result as CodeReviewReport; + const report = expectSuccess(result); - // Basic structure - expect(report).toBeDefined(); expect(report.mode).toBe('package'); expect(report.target).toBe('ZPACKAGE'); expect(Array.isArray(report.objects)).toBe(true); @@ -104,37 +132,42 @@ describe('Code Review Workflow – package mode', () => { }); it('summary.totalObjects matches the number of resolved objects', async () => { - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZPACKAGE', - ...connArgs(), - }, + const result = await runReview(callTool, { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), }); - const report = result.result as CodeReviewReport; + const report = expectSuccess(result); expect(report.summary.totalObjects).toBe(report.objects.length); }); - it('summary.totalFindings matches findings array length', async () => { - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZPACKAGE', - ...connArgs(), - }, + it('summary.totalFindings matches the findings array length', async () => { + const result = await runReview(callTool, { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), }); - const report = result.result as CodeReviewReport; - // totalFindings counts only findings (not error entries) - const realFindings = report.findings.filter((f) => f.priority !== 'error'); + const report = expectSuccess(result); expect(report.summary.totalFindings).toBe(report.findings.length); - // The mock ATC worklist fixture has findings for ZCL_SAMPLE_CLASS - expect(realFindings.length).toBeGreaterThanOrEqual(0); + }); + + it('returns an empty report when the package has no objects', async () => { + // The mock filters search results by packageName; an unknown name + // produces an empty objects array. + const result = await runReview(callTool, { + mode: 'package', + packageName: 'ZEMPTY_PACKAGE_DOES_NOT_EXIST', + ...connArgs(), + }); + + const report = expectSuccess(result); + expect(report.objects).toHaveLength(0); + expect(report.findings).toHaveLength(0); + expect(report.summary.totalObjects).toBe(0); + expect(report.summary.totalFindings).toBe(0); + expect(Object.keys(report.summary.bySeverity)).toHaveLength(0); }); }); @@ -144,86 +177,45 @@ describe('Code Review Workflow – package mode', () => { describe('Code Review Workflow – transport mode', () => { it('returns a CodeReviewReport for a transport', async () => { - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'transport', - transportNumber: 'DEVK900001', - ...connArgs(), - }, + const result = await runReview(callTool, { + mode: 'transport', + transportNumber: 'DEVK900001', + ...connArgs(), }); - expect(result.status).toBe('success'); - const report = result.result as CodeReviewReport; - + const report = expectSuccess(result); expect(report.mode).toBe('transport'); expect(report.target).toBe('DEVK900001'); expect(report.objects).toHaveLength(1); - expect(report.objects[0]).toContain('DEVK900001'); - expect(typeof report.summary.totalObjects).toBe('number'); - }); - - it('objects array contains the transport URI', async () => { - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'transport', - transportNumber: 'DEVK900001', - ...connArgs(), - }, - }); - - const report = result.result as CodeReviewReport; expect(report.objects[0]).toBe( '/sap/bc/adt/cts/transportrequests/DEVK900001', ); }); -}); - -// --------------------------------------------------------------------------- -// Empty package test -// --------------------------------------------------------------------------- -describe('Code Review Workflow – empty package', () => { - it('returns an empty report when no objects are found', async () => { - // The mock always returns the same search results. - // Use a package name that doesn't match any fixture objects so the - // objects array comes back empty (the mock filters by packageName match). - const workflow = createCodeReviewWorkflow(callTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZEMPTY_PACKAGE_DOES_NOT_EXIST', - ...connArgs(), - }, + it('aggregates ATC findings (or errors) into the summary', async () => { + const result = await runReview(callTool, { + mode: 'transport', + transportNumber: 'DEVK900001', + ...connArgs(), }); - expect(result.status).toBe('success'); - const report = result.result as CodeReviewReport; - - // No objects → no findings - expect(report.objects).toHaveLength(0); - expect(report.findings).toHaveLength(0); - expect(report.summary.totalObjects).toBe(0); - expect(report.summary.totalFindings).toBe(0); - expect(Object.keys(report.summary.bySeverity)).toHaveLength(0); + const report = expectSuccess(result); + const sumFromBuckets = Object.values(report.summary.bySeverity).reduce( + (acc, n) => acc + n, + 0, + ); + expect(sumFromBuckets).toBe(report.summary.totalFindings); }); }); // --------------------------------------------------------------------------- -// Error handling test +// Error handling // --------------------------------------------------------------------------- describe('Code Review Workflow – error handling', () => { - it('produces an error finding when atc_run fails, workflow still completes', async () => { - // Inject a tool caller that succeeds for list_package_objects but - // throws for atc_run, simulating a partial failure + it('produces a single error finding when atc_run fails', async () => { const failingAtcCallTool: McpToolCaller = async (toolName, args) => { if (toolName === 'list_package_objects') { - // Return a single fake object return { packageName: args.packageName, count: 1, @@ -236,27 +228,23 @@ describe('Code Review Workflow – error handling', () => { return callTool(toolName, args); }; - const workflow = createCodeReviewWorkflow(failingAtcCallTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZPACKAGE', - ...connArgs(), - }, + const result = await runReview(failingAtcCallTool, { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), }); - expect(result.status).toBe('success'); - const report = result.result as CodeReviewReport; - + const report = expectSuccess(result); expect(report.findings).toHaveLength(1); - expect(report.findings[0].priority).toBe('error'); - expect(report.findings[0].description).toContain('ATC service unavailable'); + expect(report.findings[0]?.priority).toBe('error'); + expect(report.findings[0]?.description).toContain( + 'ATC service unavailable', + ); expect(report.summary.totalFindings).toBe(1); expect(report.summary.bySeverity['error']).toBe(1); }); - it('produces error findings for all objects when atc_run consistently fails', async () => { + it('produces error findings for every object when atc_run consistently fails', async () => { const failingCallTool: McpToolCaller = async (toolName, args) => { if (toolName === 'list_package_objects') { return { @@ -274,19 +262,38 @@ describe('Code Review Workflow – error handling', () => { return callTool(toolName, args); }; - const workflow = createCodeReviewWorkflow(failingCallTool); - const run = workflow.createRun(); - const result = await run.start({ - inputData: { - mode: 'package', - packageName: 'ZPACKAGE', - ...connArgs(), - }, + const result = await runReview(failingCallTool, { + mode: 'package', + packageName: 'ZPACKAGE', + ...connArgs(), }); - const report = result.result as CodeReviewReport; + const report = expectSuccess(result); expect(report.objects).toHaveLength(2); expect(report.findings).toHaveLength(2); expect(report.findings.every((f) => f.priority === 'error')).toBe(true); + expect(report.summary.bySeverity['error']).toBe(2); + }); + + it('still completes when transport lookup fails (transport mode error path)', async () => { + const failingTransportCallTool: McpToolCaller = async (toolName) => { + if (toolName === 'cts_get_transport') { + throw new Error('Transport not found'); + } + throw new Error(`Unexpected tool call: ${toolName}`); + }; + + const result = await runReview(failingTransportCallTool, { + mode: 'transport', + transportNumber: 'DOES_NOT_EXIST', + ...connArgs(), + }); + + // Transport lookup failure happens inside step 1 — the workflow run + // ends in `failed`. Tolerate either shape for forward-compat. + expect(['failed', 'success']).toContain(result.status); + if (result.status === 'failed') { + expect(result.error.message).toContain('Transport not found'); + } }); }); diff --git a/packages/adt-pilot/tsconfig.json b/packages/adt-pilot/tsconfig.json index c23e61c8..62ebbd94 100644 --- a/packages/adt-pilot/tsconfig.json +++ b/packages/adt-pilot/tsconfig.json @@ -5,6 +5,9 @@ "references": [ { "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" } ] } diff --git a/packages/adt-pilot/tsconfig.lib.json b/packages/adt-pilot/tsconfig.lib.json index cf10527e..d24b18cb 100644 --- a/packages/adt-pilot/tsconfig.lib.json +++ b/packages/adt-pilot/tsconfig.lib.json @@ -7,5 +7,16 @@ "types": ["node"] }, "include": ["src/**/*.ts"], - "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"] + "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"], + "references": [ + { + "path": "../adt-client" + }, + { + "path": "../adt-mcp" + }, + { + "path": "../adt-fixtures" + } + ] } diff --git a/packages/adt-pilot/tsconfig.spec.json b/packages/adt-pilot/tsconfig.spec.json new file mode 100644 index 00000000..eef1dddd --- /dev/null +++ b/packages/adt-pilot/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vitest.config.ts", + "tests/**/*.test.ts", + "tests/**/*.spec.ts", + "src/**/*.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index de915a89..617e06bf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -113,6 +113,9 @@ }, { "path": "./packages/aclass" + }, + { + "path": "./packages/adt-pilot" } ] } From f7246654f605e25709ba08c137654d9fc8f32b46 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 15:56:19 +0000 Subject: [PATCH 5/7] fix(adt-pilot): use https in test/example URLs to clear SonarCloud hotspots SonarCloud (rule typescript:S5332) flagged 'http://sap:8000' string literals in tests/types.test.ts as security hotspots. Switch the example baseUrls in both tests and JSDoc to 'https://sap.example.com' / 'https://my-sap.example.com'. The localhost URL in workflow.test.ts is intentional (in-process mock server) and is not flagged by the rule. Co-Authored-By: Petr Plenkov --- packages/adt-pilot/src/harness.ts | 2 +- packages/adt-pilot/src/index.ts | 4 ++-- packages/adt-pilot/src/types.ts | 2 +- packages/adt-pilot/src/workflow.ts | 2 +- packages/adt-pilot/tests/types.test.ts | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/adt-pilot/src/harness.ts b/packages/adt-pilot/src/harness.ts index 0649ac9f..0b49dfe4 100644 --- a/packages/adt-pilot/src/harness.ts +++ b/packages/adt-pilot/src/harness.ts @@ -12,7 +12,7 @@ * }); * await pilot.init(); * await pilot.selectOrCreateThread(); - * await pilot.sendMessage({ content: 'Review package ZPACKAGE on http://my-sap:8000' }); + * await pilot.sendMessage({ content: 'Review package ZPACKAGE on https://my-sap.example.com' }); * ``` */ diff --git a/packages/adt-pilot/src/index.ts b/packages/adt-pilot/src/index.ts index a8330d46..f2dabbd2 100644 --- a/packages/adt-pilot/src/index.ts +++ b/packages/adt-pilot/src/index.ts @@ -36,7 +36,7 @@ * inputData: { * mode: 'package', * packageName: 'ZPACKAGE', - * baseUrl: 'http://sap:8000', + * baseUrl: 'https://sap.example.com', * username: 'DEVELOPER', * password: 'secret', * }, @@ -56,7 +56,7 @@ * }); * await pilot.init(); * await pilot.selectOrCreateThread(); - * await pilot.sendMessage({ content: 'Review package ZPACKAGE on http://sap:8000' }); + * await pilot.sendMessage({ content: 'Review package ZPACKAGE on https://sap.example.com' }); * ``` */ diff --git a/packages/adt-pilot/src/types.ts b/packages/adt-pilot/src/types.ts index 69ac0d12..4b642a44 100644 --- a/packages/adt-pilot/src/types.ts +++ b/packages/adt-pilot/src/types.ts @@ -11,7 +11,7 @@ * these credentials — they must be passed as part of every workflow input. */ export interface ConnectionParams { - /** Base URL of the SAP system (e.g. `http://sap:8000`) */ + /** Base URL of the SAP system (e.g. `https://sap.example.com`) */ baseUrl: string; /** SAP user name */ username: string; diff --git a/packages/adt-pilot/src/workflow.ts b/packages/adt-pilot/src/workflow.ts index 8376df37..dacb911c 100644 --- a/packages/adt-pilot/src/workflow.ts +++ b/packages/adt-pilot/src/workflow.ts @@ -21,7 +21,7 @@ * inputData: { * mode: 'package', * packageName: 'ZPACKAGE', - * baseUrl: 'http://sap:8000', + * baseUrl: 'https://sap.example.com', * username: 'DEVELOPER', * password: 'secret', * }, diff --git a/packages/adt-pilot/tests/types.test.ts b/packages/adt-pilot/tests/types.test.ts index b40275dc..07107ccf 100644 --- a/packages/adt-pilot/tests/types.test.ts +++ b/packages/adt-pilot/tests/types.test.ts @@ -89,7 +89,7 @@ describe('codeReviewInputSchema', () => { const input = { mode: 'package', packageName: 'ZPACKAGE', - baseUrl: 'http://sap:8000', + baseUrl: 'https://sap.example.com', username: 'DEVELOPER', password: TEST_SECRET, }; @@ -100,7 +100,7 @@ describe('codeReviewInputSchema', () => { const input = { mode: 'transport', transportNumber: 'DEVK900001', - baseUrl: 'http://sap:8000', + baseUrl: 'https://sap.example.com', username: 'DEVELOPER', password: TEST_SECRET, client: '100', @@ -122,7 +122,7 @@ describe('codeReviewInputSchema', () => { const input = { mode: 'invalid', packageName: 'ZPACKAGE', - baseUrl: 'http://sap:8000', + baseUrl: 'https://sap.example.com', username: 'DEVELOPER', password: TEST_SECRET, }; From d819a8de3dc338eeea94c74bf50b6563ea98753e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 03:49:05 +0000 Subject: [PATCH 6/7] fix(adt-pilot): use extensionless internal imports per bundler-imports rule Removes .js extensions from all internal relative imports in adt-pilot src and tests to comply with the mandatory bundler-imports rule (since tsconfig.base.json uses moduleResolution: bundler). Matches the convention used in every other package in the monorepo. Addresses Devin Review findings: - BUG_pr-review-job-f33d242b3be645e096ee9c409f2ad9e4_0001 (src/index.ts) - BUG_pr-review-job-f33d242b3be645e096ee9c409f2ad9e4_0002 (src/workflow.ts) - BUG_pr-review-job-f33d242b3be645e096ee9c409f2ad9e4_0003 (src/mcp-client.ts) - BUG_pr-review-job-f33d242b3be645e096ee9c409f2ad9e4_0004 (src/harness.ts) Co-Authored-By: Petr Plenkov --- packages/adt-pilot/src/harness.ts | 2 +- packages/adt-pilot/src/index.ts | 16 ++++++++-------- packages/adt-pilot/src/mcp-client.ts | 2 +- packages/adt-pilot/src/workflow.ts | 2 +- packages/adt-pilot/tests/harness.test.ts | 2 +- packages/adt-pilot/tests/mcp-client.test.ts | 2 +- packages/adt-pilot/tests/types.test.ts | 2 +- packages/adt-pilot/tests/workflow.test.ts | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/adt-pilot/src/harness.ts b/packages/adt-pilot/src/harness.ts index 0b49dfe4..cf659658 100644 --- a/packages/adt-pilot/src/harness.ts +++ b/packages/adt-pilot/src/harness.ts @@ -18,7 +18,7 @@ import { Harness } from '@mastra/core/harness'; import type { ToolsInput } from '@mastra/core/agent'; -import { createReviewAgent } from './agent.js'; +import { createReviewAgent } from './agent'; /** Configuration for the abapify Pilot Harness */ export interface AbapifyPilotConfig { diff --git a/packages/adt-pilot/src/index.ts b/packages/adt-pilot/src/index.ts index f2dabbd2..16f582b0 100644 --- a/packages/adt-pilot/src/index.ts +++ b/packages/adt-pilot/src/index.ts @@ -68,28 +68,28 @@ export type { CodeReviewMode, CodeReviewReport, McpToolCaller, -} from './types.js'; +} from './types'; // Workflow export { createCodeReviewWorkflow, codeReviewInputSchema, codeReviewOutputSchema, -} from './workflow.js'; +} from './workflow'; export type { CodeReviewInput, CodeReviewWorkflow, CodeReviewRun, CodeReviewRunResult, -} from './workflow.js'; +} from './workflow'; // MCP client factory -export { createMcpToolCaller, connectMcpClient } from './mcp-client.js'; +export { createMcpToolCaller, connectMcpClient } from './mcp-client'; // Agent -export { createReviewAgent, REVIEW_AGENT_INSTRUCTIONS } from './agent.js'; -export type { ReviewAgentConfig } from './agent.js'; +export { createReviewAgent, REVIEW_AGENT_INSTRUCTIONS } from './agent'; +export type { ReviewAgentConfig } from './agent'; // Harness -export { createAbapifyPilot } from './harness.js'; -export type { AbapifyPilotConfig } from './harness.js'; +export { createAbapifyPilot } from './harness'; +export type { AbapifyPilotConfig } from './harness'; diff --git a/packages/adt-pilot/src/mcp-client.ts b/packages/adt-pilot/src/mcp-client.ts index d9647138..43f465bb 100644 --- a/packages/adt-pilot/src/mcp-client.ts +++ b/packages/adt-pilot/src/mcp-client.ts @@ -13,7 +13,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; -import type { McpToolCaller } from './types.js'; +import type { McpToolCaller } from './types'; interface TextContentBlock { type: 'text'; diff --git a/packages/adt-pilot/src/workflow.ts b/packages/adt-pilot/src/workflow.ts index dacb911c..4e2c4dae 100644 --- a/packages/adt-pilot/src/workflow.ts +++ b/packages/adt-pilot/src/workflow.ts @@ -42,7 +42,7 @@ import type { AtcStepResult, CodeReviewReport, McpToolCaller, -} from './types.js'; +} from './types'; // Type-erase the Mastra workflow/step factories at the import boundary. // See "Internal type-erasure aliases" below for the rationale. diff --git a/packages/adt-pilot/tests/harness.test.ts b/packages/adt-pilot/tests/harness.test.ts index f40d756d..8bfee87e 100644 --- a/packages/adt-pilot/tests/harness.test.ts +++ b/packages/adt-pilot/tests/harness.test.ts @@ -18,7 +18,7 @@ import { createAbapifyPilot, createReviewAgent, REVIEW_AGENT_INSTRUCTIONS, -} from '../src/index.js'; +} from '../src/index'; describe('createAbapifyPilot', () => { it('returns a Harness instance', () => { diff --git a/packages/adt-pilot/tests/mcp-client.test.ts b/packages/adt-pilot/tests/mcp-client.test.ts index 0cb0c3cf..1944712a 100644 --- a/packages/adt-pilot/tests/mcp-client.test.ts +++ b/packages/adt-pilot/tests/mcp-client.test.ts @@ -3,7 +3,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { createMcpToolCaller } from '../src/index.js'; +import { createMcpToolCaller } from '../src/index'; // --------------------------------------------------------------------------- // createMcpToolCaller tests diff --git a/packages/adt-pilot/tests/types.test.ts b/packages/adt-pilot/tests/types.test.ts index 07107ccf..2f598677 100644 --- a/packages/adt-pilot/tests/types.test.ts +++ b/packages/adt-pilot/tests/types.test.ts @@ -5,7 +5,7 @@ import { randomBytes } from 'node:crypto'; import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { codeReviewOutputSchema, codeReviewInputSchema } from '../src/index.js'; +import { codeReviewOutputSchema, codeReviewInputSchema } from '../src/index'; // Generated per-process; never a real credential. const TEST_SECRET = randomBytes(8).toString('hex'); diff --git a/packages/adt-pilot/tests/workflow.test.ts b/packages/adt-pilot/tests/workflow.test.ts index 996351e9..e7ed0a5f 100644 --- a/packages/adt-pilot/tests/workflow.test.ts +++ b/packages/adt-pilot/tests/workflow.test.ts @@ -20,8 +20,8 @@ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; import { createMockAdtServer, type MockAdtServer } from '@abapify/adt-fixtures'; import { createAdtClient } from '@abapify/adt-client'; import { createMcpServer } from '@abapify/adt-mcp'; -import { createCodeReviewWorkflow, createMcpToolCaller } from '../src/index.js'; -import type { CodeReviewReport, McpToolCaller } from '../src/index.js'; +import { createCodeReviewWorkflow, createMcpToolCaller } from '../src/index'; +import type { CodeReviewReport, McpToolCaller } from '../src/index'; // --------------------------------------------------------------------------- // Test-level setup From ffa6346f0a322b514746587de1b14f09e663e955 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 03:55:25 +0000 Subject: [PATCH 7/7] fix(adt-pilot): align declared deps with actual usage - Add `@abapify/adt-client` as a devDependency. Tests import `createAdtClient` from it (tests/workflow.test.ts), and the project reference list in tsconfig.lib.json already points at it. Without an explicit declaration the package was only resolvable via bun workspace hoisting, which fails in stricter resolvers. - Drop `@mastra/mcp` from runtime dependencies. No source file under `src/` imports it; all Mastra surfaces in use are from `@mastra/core/*`, and all MCP transport code is built on `@modelcontextprotocol/sdk`. JSDoc comments referencing `@mastra/mcp` describe an optional consumer choice for sourcing `mcpTools` and remain accurate. Addresses Devin Review findings: - BUG_pr-review-job-5092d9235b51447997e11b8d8766108a_0001 (missing @abapify/adt-client devDep) - BUG_pr-review-job-5092d9235b51447997e11b8d8766108a_0002 (phantom @mastra/mcp runtime dep) Co-Authored-By: Petr Plenkov --- bun.lock | 6 +----- packages/adt-pilot/package.json | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index 643cb37e..c16d5ec9 100644 --- a/bun.lock +++ b/bun.lock @@ -282,11 +282,11 @@ "version": "0.3.6", "dependencies": { "@mastra/core": "^1.28.0", - "@mastra/mcp": "^1.5.2", "@modelcontextprotocol/sdk": "^1.27.0", "zod": "^3.24.0", }, "devDependencies": { + "@abapify/adt-client": "0.3.6", "@abapify/adt-fixtures": "0.3.6", "@abapify/adt-mcp": "0.3.6", }, @@ -1292,8 +1292,6 @@ "@mastra/core": ["@mastra/core@1.29.0", "", { "dependencies": { "@a2a-js/sdk": "~0.3.13", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.23", "@ai-sdk/provider-utils-v6": "npm:@ai-sdk/provider-utils@4.0.23", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.1", "@ai-sdk/provider-v6": "npm:@ai-sdk/provider@3.0.8", "@ai-sdk/ui-utils-v5": "npm:@ai-sdk/ui-utils@1.2.11", "@isaacs/ttlcache": "^2.1.4", "@lukeed/uuid": "^2.0.1", "@mastra/schema-compat": "1.2.9", "@modelcontextprotocol/sdk": "^1.27.1", "@sindresorhus/slugify": "^2.2.1", "@standard-schema/spec": "^1.1.0", "ajv": "^8.18.0", "chat": "^4.24.0", "dotenv": "^17.3.1", "execa": "^9.6.1", "gray-matter": "^4.0.3", "hono": "^4.12.8", "hono-openapi": "^1.3.0", "ignore": "^7.0.5", "js-tiktoken": "^1.0.21", "json-schema": "^0.4.0", "lru-cache": "^11.2.7", "p-map": "^7.0.4", "p-retry": "^7.1.1", "picomatch": "^4.0.3", "radash": "^12.1.1", "tokenx": "^1.3.0", "ws": "^8.20.0", "xxhash-wasm": "^1.1.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-AD1aV2A71rNS3yWa7XFzLVFUIwo7Y7+1ZKxL65WDH6d/T+0YwIuTU25yb3d8T1lyHIehyaZr5SmwQGyjMOINkA=="], - "@mastra/mcp": ["@mastra/mcp@1.6.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.27.1", "exit-hook": "^5.1.0", "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "@mastra/core": ">=1.0.0-0 <2.0.0-0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-o63itm5R5nD8Q574Sj5EbG8TjIhczGrZT4N3ZxVA8W42CLUc+2ZH+Xmj2ElaLx30HCN1gIiNkjuqlfxuq2zA+Q=="], - "@mastra/schema-compat": ["@mastra/schema-compat@1.2.9", "", { "dependencies": { "json-schema-to-zod": "^2.7.0", "zod-from-json-schema": "^0.5.2", "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-1/RgazXqi1Wdyx8aR81CVS+sRyzlTGUL1YhhHkSULoEY8aXs58bvWkH/6iixlYsY0xGvn+0OPLCeSRkBCtDx4Q=="], "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], @@ -2682,8 +2680,6 @@ "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], - "exit-hook": ["exit-hook@5.1.0", "", {}, "sha512-INjr2xyxHo7bhAqf5ong++GZPPnpcuBcaXUKt03yf7Fie9yWD7FapL4teOU0+awQazGs5ucBh7xWs/AD+6nhog=="], - "exit-x": ["exit-x@0.2.2", "", {}, "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ=="], "expect": ["expect@30.3.0", "", { "dependencies": { "@jest/expect-utils": "30.3.0", "@jest/get-type": "30.1.0", "jest-matcher-utils": "30.3.0", "jest-message-util": "30.3.0", "jest-mock": "30.3.0", "jest-util": "30.3.0" } }, "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q=="], diff --git a/packages/adt-pilot/package.json b/packages/adt-pilot/package.json index bce7ecf5..980c0d32 100644 --- a/packages/adt-pilot/package.json +++ b/packages/adt-pilot/package.json @@ -27,11 +27,11 @@ ], "dependencies": { "@mastra/core": "^1.28.0", - "@mastra/mcp": "^1.5.2", "@modelcontextprotocol/sdk": "^1.27.0", "zod": "^3.24.0" }, "devDependencies": { + "@abapify/adt-client": "0.3.6", "@abapify/adt-fixtures": "0.3.6", "@abapify/adt-mcp": "0.3.6" },