@vitest-agent/sdk 3.1.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +23 -19
  2. package/dispatch.d.ts +25 -6
  3. package/formatters/markdown.js +1 -0
  4. package/formatters/terminal.js +4 -3
  5. package/index.d.ts +454 -2343
  6. package/index.js +10 -68
  7. package/internal-inject-env.js +5 -7
  8. package/package.json +3 -19
  9. package/schemas/{run-report-file-1.0.0.json → 5.0/run.json} +1 -1
  10. package/schemas/RunReportFile.js +6 -2
  11. package/sidecar-dispatch.js +7 -5
  12. package/utils/format-console.js +9 -10
  13. package/utils/format-terminal.js +7 -7
  14. package/utils/posix-path.js +52 -0
  15. package/utils/test-location.js +11 -5
  16. package/version.js +17 -0
  17. package/layers/ConfigLive.js +0 -46
  18. package/layers/DataReaderLive.js +0 -1923
  19. package/layers/DataStoreLive.js +0 -1287
  20. package/layers/DetailResolverLive.js +0 -9
  21. package/layers/DiscoveryRegistryLive.js +0 -70
  22. package/layers/EnvironmentDetectorLive.js +0 -21
  23. package/layers/EnvironmentDetectorTest.js +0 -13
  24. package/layers/ExecutorResolverLive.js +0 -9
  25. package/layers/FormatSelectorLive.js +0 -20
  26. package/layers/HistoryTrackerLive.js +0 -49
  27. package/layers/HistoryTrackerTest.js +0 -25
  28. package/layers/LoggerLive.js +0 -64
  29. package/layers/OutputPipelineLive.js +0 -13
  30. package/layers/OutputRendererLive.js +0 -28
  31. package/layers/PathResolutionLive.js +0 -40
  32. package/layers/PerClientSessionMapLive.js +0 -145
  33. package/layers/ProjectDiscoveryLive.js +0 -68
  34. package/layers/ProjectDiscoveryTest.js +0 -12
  35. package/layers/ProjectIdentityLive.js +0 -96
  36. package/layers/RunContextLive.js +0 -81
  37. package/lib/format-triage.js +0 -103
  38. package/lib/format-wrapup.js +0 -67
  39. package/migrations/0001_initial.js +0 -824
  40. package/migrations/0002_test_artifacts.js +0 -30
  41. package/migrations/index.js +0 -26
  42. package/migrations/registry_0001_initial.js +0 -39
  43. package/migrations/session_map_0001_initial.js +0 -38
  44. package/services/Config.js +0 -15
  45. package/services/DataReader.js +0 -8
  46. package/services/DataStore.js +0 -15
  47. package/services/DetailResolver.js +0 -8
  48. package/services/DiscoveryRegistry.js +0 -8
  49. package/services/EnvironmentDetector.js +0 -8
  50. package/services/ExecutorResolver.js +0 -8
  51. package/services/FormatSelector.js +0 -8
  52. package/services/HistoryTracker.js +0 -20
  53. package/services/OutputRenderer.js +0 -8
  54. package/services/PerClientSessionMap.js +0 -22
  55. package/services/ProjectDiscovery.js +0 -8
  56. package/services/ProjectIdentity.js +0 -63
  57. package/services/RunContext.js +0 -53
  58. package/services/idempotency.js +0 -58
  59. package/sql/assemblers.js +0 -69
  60. package/testing/layers.js +0 -21
  61. package/testing.d.ts +0 -1862
  62. package/testing.js +0 -254
  63. package/utils/ensure-migrated.js +0 -52
  64. package/utils/failure-signature.js +0 -34
  65. package/utils/resolve-data-path.js +0 -65
  66. package/utils/resolve-project-key-from-cwd.js +0 -53
  67. package/utils/resolve-workspace-key.js +0 -41
package/index.js CHANGED
@@ -16,57 +16,10 @@ import { MarkdownFormatter } from "./formatters/markdown.js";
16
16
  import { SilentFormatter } from "./formatters/silent.js";
17
17
  import { compressLines } from "./utils/compress-lines.js";
18
18
  import { osc8 } from "./utils/hyperlink.js";
19
+ import { basenamePosix, joinPosix, relativePosix, toPosix } from "./utils/posix-path.js";
19
20
  import { TerminalFormatter } from "./formatters/terminal.js";
20
- import { VitestAgentConfig } from "./schemas/Config.js";
21
- import { VitestAgentConfigFile } from "./services/Config.js";
22
- import { ConfigLive } from "./layers/ConfigLive.js";
23
- import { DataReader } from "./services/DataReader.js";
24
- import { HistoryTracker, historyKey } from "./services/HistoryTracker.js";
25
- import { DataReaderLive } from "./layers/DataReaderLive.js";
26
21
  import { ActorType, AgentId, ChatId, ConversationId, HostKind, ProjectKey, TddTaskId } from "./schemas/Identity.js";
27
22
  import { Agent, IdempotencyHit } from "./schemas/Agent.js";
28
- import { DataStore, INLINE_ATTACHMENT_BODY_CAP_BYTES } from "./services/DataStore.js";
29
- import { coerceErrorField, coerceErrorText } from "./utils/coerce-error-text.js";
30
- import { DataStoreLive } from "./layers/DataStoreLive.js";
31
- import { DetailResolver } from "./services/DetailResolver.js";
32
- import { DetailResolverLive } from "./layers/DetailResolverLive.js";
33
- import { DiscoveryRegistry } from "./services/DiscoveryRegistry.js";
34
- import { DiscoveryRegistryLive } from "./layers/DiscoveryRegistryLive.js";
35
- import { EnvironmentDetector } from "./services/EnvironmentDetector.js";
36
- import { EnvironmentDetectorLive } from "./layers/EnvironmentDetectorLive.js";
37
- import { EnvironmentDetectorTest } from "./layers/EnvironmentDetectorTest.js";
38
- import { ExecutorResolver } from "./services/ExecutorResolver.js";
39
- import { ExecutorResolverLive } from "./layers/ExecutorResolverLive.js";
40
- import { FormatSelector } from "./services/FormatSelector.js";
41
- import { FormatSelectorLive } from "./layers/FormatSelectorLive.js";
42
- import { classifyTest } from "./utils/classify-test.js";
43
- import { HistoryTrackerLive } from "./layers/HistoryTrackerLive.js";
44
- import { HistoryTrackerTest } from "./layers/HistoryTrackerTest.js";
45
- import { LoggerLive, resolveLogFile, resolveLogLevel } from "./layers/LoggerLive.js";
46
- import { OutputRenderer } from "./services/OutputRenderer.js";
47
- import { OutputRendererLive } from "./layers/OutputRendererLive.js";
48
- import { OutputPipelineLive } from "./layers/OutputPipelineLive.js";
49
- import { PathResolutionLive } from "./layers/PathResolutionLive.js";
50
- import { PerClientSessionMapReader, PerClientSessionMapWriter } from "./services/PerClientSessionMap.js";
51
- import { PerClientSessionMapReaderLive, PerClientSessionMapWriterLive } from "./layers/PerClientSessionMapLive.js";
52
- import { ProjectDiscovery } from "./services/ProjectDiscovery.js";
53
- import { BATS_FILE_GLOB_SUFFIX, NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, classifyTestPath, findOwningWorkspace, isTestFileName } from "./utils/test-location.js";
54
- import { ProjectDiscoveryLive } from "./layers/ProjectDiscoveryLive.js";
55
- import { ProjectDiscoveryTest } from "./layers/ProjectDiscoveryTest.js";
56
- import { canonicalizeGitUrl, gitUrlToProjectKey } from "./utils/canonicalize-git-url.js";
57
- import { normalizeWorkspaceKey } from "./utils/normalize-workspace-key.js";
58
- import { ProjectIdentity, resolveProjectIdentityFromCandidates } from "./services/ProjectIdentity.js";
59
- import { ProjectIdentityLive, ProjectIdentityTest } from "./layers/ProjectIdentityLive.js";
60
- import { AgentContext, RunContext, RunContext$, RunContextService } from "./services/RunContext.js";
61
- import { probeHostMetadataFromEnv } from "./utils/probe-host-metadata.js";
62
- import { RunContextLive, RunContextTest } from "./layers/RunContextLive.js";
63
- import { formatTriageEffect } from "./lib/format-triage.js";
64
- import { formatWrapupEffect } from "./lib/format-wrapup.js";
65
- import migration from "./migrations/0001_initial.js";
66
- import migration$1 from "./migrations/0002_test_artifacts.js";
67
- import { PROJECT_MIGRATIONS } from "./migrations/index.js";
68
- import migration$2 from "./migrations/registry_0001_initial.js";
69
- import migration$3 from "./migrations/session_map_0001_initial.js";
70
23
  import { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode, OutputFormat, PackageManager, ReportError, TestClassification, TestRunReason, TestState } from "./schemas/Common.js";
71
24
  import { ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests } from "./schemas/ConsoleLeaks.js";
72
25
  import { MetricThresholds, PatternMetricThresholds, PatternThresholds, PerFileThresholds, ResolvedThresholds } from "./schemas/Thresholds.js";
@@ -75,6 +28,7 @@ import { AgentReport, ModuleReport, ReportSummary, TagCountEntry, TestReport } f
75
28
  import { CoverageBaselines } from "./schemas/Baselines.js";
76
29
  import { CacheManifest, CacheManifestEntry } from "./schemas/CacheManifest.js";
77
30
  import { BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorsReadyEvent, BlockedEvent, ChannelEvent, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalStartedEvent, GoalsReadyEvent, PhaseTransitionEvent, SessionCompleteEvent, SessionOutcome } from "./schemas/ChannelEvent.js";
31
+ import { VitestAgentConfig } from "./schemas/Config.js";
78
32
  import { CoverageLevel, resolveCoverageInput, validateCoverageConfig } from "./schemas/CoverageLevel.js";
79
33
  import { CoverageTargets, CoverageTargetsMetrics } from "./schemas/CoverageTargets.js";
80
34
  import { HistoryRecord, TestHistory, TestRun } from "./schemas/History.js";
@@ -94,36 +48,24 @@ import { ToolCallPayload } from "./schemas/turns/ToolCallPayload.js";
94
48
  import { ToolResultPayload } from "./schemas/turns/ToolResultPayload.js";
95
49
  import { UserPromptPayload } from "./schemas/turns/UserPromptPayload.js";
96
50
  import { TurnPayload } from "./schemas/turns/index.js";
97
- import { IDEMPOTENCY_ROOT_SENTINEL, deriveIdempotencyKey } from "./services/idempotency.js";
98
- import { assembleHistoryRecord, assembleManifest } from "./sql/assemblers.js";
51
+ import { coerceErrorField, coerceErrorText } from "./utils/coerce-error-text.js";
99
52
  import { buildAgentReport, countSuiteFailures } from "./utils/build-report.js";
53
+ import { canonicalizeGitUrl, gitUrlToProjectKey } from "./utils/canonicalize-git-url.js";
54
+ import { classifyTest } from "./utils/classify-test.js";
100
55
  import { computeTrend, getRecentDirection, hashTargets } from "./utils/compute-trend.js";
101
56
  import { buildConsoleLeaks, collectConsoleLeakEntries } from "./utils/console-leaks.js";
102
57
  import { detectNonDefaultDiscoverStrategy } from "./utils/detect-non-default-discover-strategy.js";
103
58
  import { detectPackageManager, getRunCommand } from "./utils/detect-pm.js";
104
59
  import { isTimeoutError } from "./utils/detect-timeout.js";
105
- import { _resetMigrationCacheForTesting, ensureMigrated } from "./utils/ensure-migrated.js";
106
- import { computeFailureSignature, normalizeAssertionShape } from "./utils/failure-signature.js";
107
60
  import { formatFatalError } from "./utils/format-fatal-error.js";
108
61
  import { findFunctionBoundary } from "./utils/function-boundary.js";
109
62
  import { buildEnvPrefix, detectVitestScripts, isVitestInvocation, rewriteBashCommand } from "./utils/match-vitest-command.js";
110
- import { resolveProjectKeyFromCwd } from "./utils/resolve-project-key-from-cwd.js";
111
- import { DATABASE_FILENAME, resolveDataPath } from "./utils/resolve-data-path.js";
112
- import { resolveWorkspaceKey } from "./utils/resolve-workspace-key.js";
63
+ import { normalizeWorkspaceKey } from "./utils/normalize-workspace-key.js";
64
+ import { probeHostMetadataFromEnv } from "./utils/probe-host-metadata.js";
113
65
  import { safeFilename } from "./utils/safe-filename.js";
66
+ import { BATS_FILE_GLOB_SUFFIX, NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, classifyTestPath, findOwningWorkspace, isTestFileName } from "./utils/test-location.js";
114
67
  import { validateCoverageTargetsShape } from "./utils/validate-coverage-targets-shape.js";
115
68
  import { requiredArtifactForTransition, transitionEnforcesBehaviorMatch, validatePhaseTransition } from "./utils/validate-phase-transition.js";
69
+ import { CURRENT_SDK_VERSION } from "./version.js";
116
70
 
117
- //#region src/index.ts
118
- /**
119
- * The version of this package. Inlined at build time from
120
- * package.json#version via rslib-builder's __PACKAGE_VERSION__ substitution.
121
- * Source-level reads (workspace `exports: "./src/index.ts"` during dev)
122
- * see the `"0.0.0"` fallback — a clear signal the build pipeline has not
123
- * substituted yet. Exported for version introspection by downstream tooling.
124
- * @public
125
- */
126
- const CURRENT_SDK_VERSION = "3.1.2";
127
-
128
- //#endregion
129
- export { ActionSeverity, ActorType, Agent, AgentConsoleMode, AgentContext, AgentId, AgentNotFoundError, AgentPluginOptions, AgentReport, AgentReporterOptions, BATS_FILE_GLOB_SUFFIX, BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorStatus, BehaviorsReadyEvent, BlockedEvent, CURRENT_SDK_VERSION, CacheManifest, CacheManifestEntry, ChannelEvent, ChatId, CiConsoleMode, ConfigLive, ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests, ConsoleLogLevel, ConsoleMode, ConsoleOutputMode, ConsoleOutputs, ConversationId, ConversationNotFoundError, CoverageBaselines, FileCoverageReport as CoverageFile, CoverageGap, CoverageLevel, CoverageMetric, CoverageRenderState, CoverageReport, CoverageTargets, CoverageTargetsMetrics, CoverageTotals, DATABASE_FILENAME, DataReader, DataReaderLive, DataStore, DataStoreError, DataStoreLive, DetailLevel, DetailResolver, DetailResolverLive, DiscoveryError, DiscoveryRegistry, DiscoveryRegistryLive, Environment, EnvironmentDetector, EnvironmentDetectorLive, EnvironmentDetectorTest, Executor, ExecutorResolver, ExecutorResolverLive, FailureRecord, FileCoverageReport, FileEditPayload, FormatSelector, FormatSelectorLive, GfmFormatter, GitCommandError, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HistoryTracker, HistoryTrackerLive, HistoryTrackerTest, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IDEMPOTENCY_ROOT_SENTINEL, INLINE_ATTACHMENT_BODY_CAP_BYTES, IdempotencyHit, IllegalStatusTransitionError, JsonFormatter, LoggerLive, MarkdownFormatter, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload, OutputFormat, OutputPipelineLive, OutputRenderer, OutputRendererLive, PROJECT_MIGRATIONS, PackageManager, PathResolutionError, PathResolutionLive, PatternMetricThresholds, PatternThresholds, PerClientSessionMapReader, PerClientSessionMapReaderLive, PerClientSessionMapWriter, PerClientSessionMapWriterLive, PerFileThresholds, PhaseTransitionEvent, ProbeMissError, ProjectDiscovery, ProjectDiscoveryLive, ProjectDiscoveryTest, ProjectIdentity, ProjectIdentityLive, ProjectIdentityNotResolvableError, ProjectIdentityTest, ProjectKey, RUN_REPORT_FILE_SCHEMA_URL, RegistrationConflictError, RenderState, RenderTotals, ReportError, ReportOption, ReportSettings, ReportSummary, ResolvedThresholds, RunContext, RunContext$, RunContextLive, RunContextService, RunContextTest, RunEvent, RunReportFile, SRC_DIR, SessionCompleteEvent, SessionNotFoundError, SessionOutcome, SidecarTimeoutError, SilentFormatter, SuggestedActionRecord, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, TagCountEntry, TddTaskAlreadyEndedError, TddTaskId, TddTaskNotFoundError, TerminalFormatter, TestAnnotation, TestArtifact, TestArtifactLocation, TestAttachment, TestClassification, TestHistory, TestRecord, TestReport, TestRun, TestRunReason, TestState, ToolCallPayload, ToolResultPayload, Transport, TrendEntry, TrendRecord, TurnPayload, UserPromptPayload, VitestAgentConfig, VitestAgentConfigFile, _resetMigrationCacheForTesting, ansi, assembleHistoryRecord, assembleManifest, buildAgentReport, buildConsoleLeaks, buildEnvPrefix, canonicalizeGitUrl, ciAnnotationsFormatter, classifyTest, classifyTestPath, coerceErrorField, coerceErrorText, collectConsoleLeakEntries, compressLines, computeFailureSignature, computeTrend, countSuiteFailures, deriveIdempotencyKey, detectNonDefaultDiscoverStrategy, detectPackageManager, detectVitestScripts, determineTier, ensureMigrated, extractSqlReason, findFunctionBoundary, findOwningWorkspace, formatConsoleMarkdown, formatFatalError, formatGfm, formatScopedCoverageNote, formatTriageEffect, formatWrapupEffect, getRecentDirection, getRunCommand, getWorstMetric, gitUrlToProjectKey, hashTargets, historyKey, initialRenderState, isTestFileName, isTimeoutError, isVitestInvocation, migration as migration0001, migration$1 as migration0002, normalizeAssertionShape, normalizeWorkspaceKey, osc8, probeHostMetadataFromEnv, migration$2 as registryMigration0001, relativePath, requiredArtifactForTransition, resolveCoverageInput, resolveDataPath, resolveLogFile, resolveLogLevel, resolveProjectIdentityFromCandidates, resolveProjectKeyFromCwd, resolveWorkspaceKey, rewriteBashCommand, safeFilename, migration$3 as sessionMapMigration0001, stripAnsi, transitionEnforcesBehaviorMatch, validateCoverageConfig, validateCoverageTargetsShape, validatePhaseTransition };
71
+ export { ActionSeverity, ActorType, Agent, AgentConsoleMode, AgentId, AgentNotFoundError, AgentPluginOptions, AgentReport, AgentReporterOptions, BATS_FILE_GLOB_SUFFIX, BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorStatus, BehaviorsReadyEvent, BlockedEvent, CURRENT_SDK_VERSION, CacheManifest, CacheManifestEntry, ChannelEvent, ChatId, CiConsoleMode, ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests, ConsoleLogLevel, ConsoleMode, ConsoleOutputMode, ConsoleOutputs, ConversationId, ConversationNotFoundError, CoverageBaselines, FileCoverageReport as CoverageFile, CoverageGap, CoverageLevel, CoverageMetric, CoverageRenderState, CoverageReport, CoverageTargets, CoverageTargetsMetrics, CoverageTotals, DataStoreError, DetailLevel, DiscoveryError, Environment, Executor, FailureRecord, FileCoverageReport, FileEditPayload, GfmFormatter, GitCommandError, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IdempotencyHit, IllegalStatusTransitionError, JsonFormatter, MarkdownFormatter, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload, OutputFormat, PackageManager, PathResolutionError, PatternMetricThresholds, PatternThresholds, PerFileThresholds, PhaseTransitionEvent, ProbeMissError, ProjectIdentityNotResolvableError, ProjectKey, RUN_REPORT_FILE_SCHEMA_URL, RegistrationConflictError, RenderState, RenderTotals, ReportError, ReportOption, ReportSettings, ReportSummary, ResolvedThresholds, RunEvent, RunReportFile, SRC_DIR, SessionCompleteEvent, SessionNotFoundError, SessionOutcome, SidecarTimeoutError, SilentFormatter, SuggestedActionRecord, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, TagCountEntry, TddTaskAlreadyEndedError, TddTaskId, TddTaskNotFoundError, TerminalFormatter, TestAnnotation, TestArtifact, TestArtifactLocation, TestAttachment, TestClassification, TestHistory, TestRecord, TestReport, TestRun, TestRunReason, TestState, ToolCallPayload, ToolResultPayload, Transport, TrendEntry, TrendRecord, TurnPayload, UserPromptPayload, VitestAgentConfig, ansi, basenamePosix, buildAgentReport, buildConsoleLeaks, buildEnvPrefix, canonicalizeGitUrl, ciAnnotationsFormatter, classifyTest, classifyTestPath, coerceErrorField, coerceErrorText, collectConsoleLeakEntries, compressLines, computeTrend, countSuiteFailures, detectNonDefaultDiscoverStrategy, detectPackageManager, detectVitestScripts, determineTier, extractSqlReason, findFunctionBoundary, findOwningWorkspace, formatConsoleMarkdown, formatFatalError, formatGfm, formatScopedCoverageNote, getRecentDirection, getRunCommand, getWorstMetric, gitUrlToProjectKey, hashTargets, initialRenderState, isTestFileName, isTimeoutError, isVitestInvocation, joinPosix, normalizeWorkspaceKey, osc8, probeHostMetadataFromEnv, relativePath, relativePosix, requiredArtifactForTransition, resolveCoverageInput, rewriteBashCommand, safeFilename, stripAnsi, toPosix, transitionEnforcesBehaviorMatch, validateCoverageConfig, validateCoverageTargetsShape, validatePhaseTransition };
@@ -1,11 +1,9 @@
1
1
  import { detectVitestScripts, rewriteBashCommand } from "./utils/match-vitest-command.js";
2
- import { join } from "node:path";
3
- import { readFileSync } from "node:fs";
4
2
 
5
3
  //#region src/internal-inject-env.ts
6
- const readPackageScripts = (cwd) => {
4
+ const readPackageScripts = (cwd, readFile) => {
7
5
  try {
8
- const raw = readFileSync(join(cwd, "package.json"), "utf-8");
6
+ const raw = readFile(`${cwd}/package.json`);
9
7
  return JSON.parse(raw).scripts ?? {};
10
8
  } catch {
11
9
  return {};
@@ -19,15 +17,15 @@ const readPackageScripts = (cwd) => {
19
17
  * - `VITEST_AGENT_CONVERSATION_ID` or `VITEST_AGENT_AGENT_ID` is
20
18
  * missing from env (no agent context to attribute to)
21
19
  *
22
- * Always synchronous — the package.json read is the only I/O and is
23
- * fast enough not to need Effect wrapping.
20
+ * Always synchronous — the package.json read is the only I/O, goes through
21
+ * the injected `readFile`, and is fast enough not to need Effect wrapping.
24
22
  * @public
25
23
  */
26
24
  const injectEnv = (input) => {
27
25
  const conversationId = input.env.VITEST_AGENT_CONVERSATION_ID;
28
26
  const agentId = input.env.VITEST_AGENT_AGENT_ID;
29
27
  if (conversationId === void 0 || agentId === void 0) return input.command;
30
- const scripts = readPackageScripts(input.cwd);
28
+ const scripts = readPackageScripts(input.cwd, input.readFile);
31
29
  const vitestScripts = detectVitestScripts(scripts);
32
30
  const parentAgentId = input.env.VITEST_AGENT_PARENT_AGENT_ID;
33
31
  return rewriteBashCommand({
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@vitest-agent/sdk",
3
- "version": "3.1.2",
3
+ "version": "5.0.0",
4
4
  "private": false,
5
- "description": "Shared Effect Schema, SQLite migrations, data layer, services, formatters, and utilities for vitest-agent packages.",
5
+ "description": "Platform-free core for the vitest-agent package family: Effect Schema definitions, reporter and dispatcher contracts, errors, formatters, and pure utilities.",
6
6
  "keywords": [
7
7
  "vitest",
8
8
  "agent",
@@ -37,29 +37,13 @@
37
37
  "import": "./dispatch.js",
38
38
  "default": "./dispatch.js"
39
39
  },
40
- "./testing": {
41
- "types": "./testing.d.ts",
42
- "import": "./testing.js",
43
- "default": "./testing.js"
44
- },
45
40
  "./schemas/*.json": "./schemas/*.json",
46
41
  "./package.json": "./package.json"
47
42
  },
48
43
  "dependencies": {
49
- "@effect/platform-node": "4.0.0-rc.115",
50
- "@effect/sql-sqlite-node": "4.0.0-rc.115",
51
- "@effected/config-file": "^0.8.0",
52
- "@effected/jsonc": "^0.11.0",
53
- "@effected/toml": "^0.7.0",
54
- "@effected/walker": "^0.8.0",
55
- "@effected/workspaces": "^0.21.0",
56
- "@effected/xdg": "^0.5.0",
57
- "@effected/yaml": "^0.15.0",
58
44
  "acorn": "^8.18.0",
59
45
  "acorn-typescript": "^1.4.13",
60
- "ajv": "^8.20.0",
61
- "effect": "4.0.0-rc.115",
62
- "std-env": "^4.2.0"
46
+ "effect": "4.0.0-rc.115"
63
47
  },
64
48
  "engines": {
65
49
  "node": ">=24.11.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://vitest-agent.dev/schemas/run-report-file-1.0.0.json",
3
+ "$id": "https://raw.githubusercontent.com/spencerbeggs/vitest-agent/main/schemas/5.0/run.json",
4
4
  "$ref": "#/$defs/RunReportFile",
5
5
  "$defs": {
6
6
  "IsoInstant": {
@@ -11,14 +11,18 @@ import { Schema } from "effect";
11
11
  * `AgentReport` per Vitest project in the run.
12
12
  */
13
13
  /**
14
- * Canonical `$schema` URL for `RunReportFile` version 1.
14
+ * Canonical `$schema` URL for `RunReportFile` — the `schemas/5.0/run.json`
15
+ * document GitHub serves raw from this repository.
15
16
  *
16
17
  * Written as the first key of `run.json` so an editor or a JSON Schema
17
18
  * validator can resolve the contract without knowing about vitest-agent.
19
+ * The same value is derived in `lib/configs/run-report-schema.ts` from
20
+ * `HostedSchema.github` (this module cannot import `@effected/*`); the drift
21
+ * test pins the two together.
18
22
  *
19
23
  * @public
20
24
  */
21
- const RUN_REPORT_FILE_SCHEMA_URL = "https://vitest-agent.dev/schemas/run-report-file-1.0.0.json";
25
+ const RUN_REPORT_FILE_SCHEMA_URL = "https://raw.githubusercontent.com/spencerbeggs/vitest-agent/main/schemas/5.0/run.json";
22
26
  /**
23
27
  * ISO-8601 instant, the shape `Date.prototype.toISOString` produces.
24
28
  * `Date.parse` alone is too permissive — it accepts `"Sep 7 2026"` and
@@ -37,17 +37,18 @@ const messageFromError = (err) => {
37
37
  };
38
38
  /**
39
39
  * Dispatch one argv invocation. `argv` is the post-`node post-bin`
40
- * slice — i.e. `process.argv.slice(2)`. Never throws: every failure is
41
- * folded into the returned {@link DispatchResult}.
40
+ * slice — i.e. `process.argv.slice(2)` — and `io` carries the process
41
+ * facts (cwd, env, a file reader) the runner owns. Never throws: every
42
+ * failure is folded into the returned {@link DispatchResult}.
42
43
  * @public
43
44
  */
44
- const dispatch = async (argv) => {
45
+ const dispatch = async (argv, io) => {
45
46
  const subcommand = argv[0];
46
47
  const rest = argv.slice(1);
47
48
  if (subcommand === "inject-env") {
48
49
  const flags = parseFlags(rest);
49
50
  const command = flags.command;
50
- const cwd = flags.cwd ?? process.cwd();
51
+ const cwd = flags.cwd ?? io.cwd;
51
52
  if (command === void 0) return {
52
53
  stdout: "",
53
54
  stderr: `${exitCodeForTag("Defect")} Defect: inject-env requires --command\n`,
@@ -58,7 +59,8 @@ const dispatch = async (argv) => {
58
59
  stdout: `${injectEnv({
59
60
  command,
60
61
  cwd,
61
- env: process.env
62
+ env: io.env,
63
+ readFile: io.readFile
62
64
  })}\n`,
63
65
  stderr: "",
64
66
  code: 0
@@ -40,15 +40,14 @@ function getWorstMetric(f) {
40
40
  * project-relative path for display.
41
41
  *
42
42
  * @param filePath - Absolute or relative file path
43
- * @param cwd - Working directory to strip; defaults to `process.cwd()`
43
+ * @param cwd - Working directory to strip
44
44
  * @returns Project-relative path string
45
45
  *
46
46
  * @internal
47
47
  */
48
48
  function relativePath(filePath, cwd) {
49
- const root = cwd ?? process.cwd();
50
- if (filePath.startsWith(root)) {
51
- const rel = filePath.slice(root.length);
49
+ if (filePath.startsWith(cwd)) {
50
+ const rel = filePath.slice(cwd.length);
52
51
  return rel.startsWith("/") ? rel.slice(1) : rel;
53
52
  }
54
53
  return filePath;
@@ -93,7 +92,7 @@ function determineTier(report) {
93
92
  * @internal
94
93
  */
95
94
  function formatConsoleMarkdown(report, options) {
96
- const { consoleOutput, coverageConsoleLimit, noColor } = options;
95
+ const { consoleOutput, coverageConsoleLimit, noColor, cwd } = options;
97
96
  if (consoleOutput === "silent") return "";
98
97
  const ao = { noColor };
99
98
  const lines = [];
@@ -130,7 +129,7 @@ function formatConsoleMarkdown(report, options) {
130
129
  }
131
130
  }
132
131
  if (hasFailures) for (const mod of report.failed) {
133
- const file = relativePath(mod.file);
132
+ const file = relativePath(mod.file, cwd);
134
133
  lines.push(`### ${ansi("✗", "red", ao)} \`${file}\``);
135
134
  lines.push("");
136
135
  if (mod.errors && mod.errors.length > 0) for (const err of mod.errors) {
@@ -171,7 +170,7 @@ function formatConsoleMarkdown(report, options) {
171
170
  lines.push(`### Coverage gaps`);
172
171
  lines.push("");
173
172
  for (const f of filesToShow) {
174
- const file = relativePath(f.file);
173
+ const file = relativePath(f.file, cwd);
175
174
  const worst = getWorstMetric(f);
176
175
  const thresholdValue = globalThresholds[worst.label === "Stmts" ? "statements" : worst.label === "Branch" ? "branches" : worst.label === "Funcs" ? "functions" : "lines"];
177
176
  const thresholdSuffix = thresholdValue != null ? ` (threshold: ${thresholdValue}%)` : "";
@@ -183,7 +182,7 @@ function formatConsoleMarkdown(report, options) {
183
182
  lines.push(`### Coverage improvements needed`);
184
183
  lines.push("");
185
184
  for (const f of cov.belowTarget.slice(0, coverageConsoleLimit)) {
186
- const file = relativePath(f.file);
185
+ const file = relativePath(f.file, cwd);
187
186
  const worst = getWorstMetric(f);
188
187
  const metricKey = worst.label === "Stmts" ? "statements" : worst.label === "Branch" ? "branches" : worst.label === "Funcs" ? "functions" : "lines";
189
188
  const targetValue = cov.targets.global[metricKey];
@@ -202,12 +201,12 @@ function formatConsoleMarkdown(report, options) {
202
201
  const persistent = allFailedTests.filter((t) => t.classification === "persistent");
203
202
  const flaky = allFailedTests.filter((t) => t.classification === "flaky");
204
203
  if (newFailures.length > 0) {
205
- const files = [...new Set(report.failed.filter((m) => m.tests.some((t) => t.classification === "new-failure")).map((m) => relativePath(m.file)))];
204
+ const files = [...new Set(report.failed.filter((m) => m.tests.some((t) => t.classification === "new-failure")).map((m) => relativePath(m.file, cwd)))];
206
205
  lines.push(`- Fix ${newFailures.length} new failure${newFailures.length > 1 ? "s" : ""} in ${files.map((f) => `\`${f}\``).join(", ")} (likely caused by recent changes)`);
207
206
  }
208
207
  if (persistent.length > 0) lines.push(`- ${persistent.length} persistent failure${persistent.length > 1 ? "s" : ""} (pre-existing, may not be yours)`);
209
208
  if (flaky.length > 0) lines.push(`- ${flaky.length} flaky test${flaky.length > 1 ? "s" : ""} (may pass on retry)`);
210
- for (const file of report.failedFiles) lines.push(`- Re-run: \`vitest run ${relativePath(file)}\``);
209
+ for (const file of report.failedFiles) lines.push(`- Re-run: \`vitest run ${relativePath(file, cwd)}\``);
211
210
  if (options.mcp) {
212
211
  lines.push("- Use `test_history` for failure trends");
213
212
  lines.push("- Use `test_errors` to search errors by type");
@@ -209,10 +209,10 @@ const sepRow = (widths) => widths.map((w) => "-".repeat(w + 2)).join("|");
209
209
  *
210
210
  * @internal
211
211
  */
212
- const renderBelowTargetTable = (files) => {
212
+ const renderBelowTargetTable = (files, cwd) => {
213
213
  if (files.length === 0) return [];
214
214
  const rows = files.map((f) => ({
215
- file: relativePath(f.file),
215
+ file: relativePath(f.file, cwd),
216
216
  stmts: String(Math.round(f.summary.statements)),
217
217
  branches: String(Math.round(f.summary.branches)),
218
218
  funcs: String(Math.round(f.summary.functions)),
@@ -331,7 +331,7 @@ const renderCoverageSection = (reports, options, ao) => {
331
331
  lines.push("Files below aspirational target:");
332
332
  const limit = options.coverageConsoleLimit;
333
333
  const shown = agg.belowTargetFiles.slice(0, limit);
334
- lines.push(...renderBelowTargetTable(shown));
334
+ lines.push(...renderBelowTargetTable(shown, options.cwd));
335
335
  if (agg.belowTargetFiles.length > limit) lines.push(`… ${agg.belowTargetFiles.length - limit} more (use the test_coverage MCP tool for the full list)`);
336
336
  }
337
337
  return lines;
@@ -342,7 +342,7 @@ const renderCoverageSection = (reports, options, ao) => {
342
342
  *
343
343
  * @internal
344
344
  */
345
- const renderFailuresSection = (reports, ao) => {
345
+ const renderFailuresSection = (reports, cwd, ao) => {
346
346
  const lines = [];
347
347
  const reportsWithFailures = reports.filter((r) => r.summary.failed > 0 || r.unhandledErrors.length > 0);
348
348
  if (reportsWithFailures.length === 0) return lines;
@@ -351,7 +351,7 @@ const renderFailuresSection = (reports, ao) => {
351
351
  const projectName = report.project ?? "default";
352
352
  lines.push(` ${projectName}`);
353
353
  for (const mod of report.failed) {
354
- const file = relativePath(mod.file);
354
+ const file = relativePath(mod.file, cwd);
355
355
  const failedTests = mod.tests.filter((t) => t.state === "failed");
356
356
  for (const test of failedTests) lines.push(...renderFailedTest(file, test, ao));
357
357
  if (mod.errors && mod.errors.length > 0) for (const err of mod.errors) {
@@ -403,7 +403,7 @@ const renderTotal = (reports, ao) => {
403
403
  const renderNextSteps = (reports, options) => {
404
404
  const lines = [];
405
405
  const allFailedFiles = /* @__PURE__ */ new Set();
406
- for (const r of reports) for (const f of r.failedFiles) allFailedFiles.add(relativePath(f));
406
+ for (const r of reports) for (const f of r.failedFiles) allFailedFiles.add(relativePath(f, options.cwd));
407
407
  if (allFailedFiles.size === 0) return lines;
408
408
  lines.push("Next steps:");
409
409
  for (const file of allFailedFiles) lines.push(` Re-run: pnpm vitest run ${file}`);
@@ -470,7 +470,7 @@ const formatTerminal = (reports, options) => {
470
470
  out.push("");
471
471
  }
472
472
  }
473
- const failures = renderFailuresSection(reports, ao);
473
+ const failures = renderFailuresSection(reports, options.cwd, ao);
474
474
  if (failures.length > 0) {
475
475
  out.push(...failures);
476
476
  out.push("");
@@ -0,0 +1,52 @@
1
+ //#region src/utils/posix-path.ts
2
+ /**
3
+ * Pure POSIX path helpers for the platform-free core.
4
+ *
5
+ * `@vitest-agent/sdk` must not import `node:path`, so the handful of path
6
+ * operations the core needs are implemented here over normalized
7
+ * forward-slash strings. Every helper accepts Windows-style input (a
8
+ * backslash separator) and normalizes it via {@link toPosix} at entry, so a
9
+ * caller never has to know which separator the platform used.
10
+ */
11
+ /**
12
+ * Normalizes `p` to forward slashes: every backslash becomes `/`.
13
+ * @public
14
+ */
15
+ const toPosix = (p) => p.replaceAll("\\", "/");
16
+ /**
17
+ * Last segment of `p` after normalization — `"b.ts"` for `"/a/b.ts"`.
18
+ * Trailing separators are ignored, so `"/a/b/"` yields `"b"`.
19
+ * @public
20
+ */
21
+ const basenamePosix = (p) => {
22
+ const normalized = toPosix(p).replace(/\/+$/, "");
23
+ const index = normalized.lastIndexOf("/");
24
+ return index === -1 ? normalized : normalized.slice(index + 1);
25
+ };
26
+ /**
27
+ * Joins `parts` with a single `/` between each, normalizing every part and
28
+ * collapsing duplicate separators at the seams. Empty parts are skipped.
29
+ * @public
30
+ */
31
+ const joinPosix = (...parts) => {
32
+ const normalized = parts.map(toPosix).filter((part) => part.length > 0);
33
+ if (normalized.length === 0) return "";
34
+ const [first, ...rest] = normalized;
35
+ return rest.reduce((acc, part) => `${acc.replace(/\/+$/, "")}/${part.replace(/^\/+/, "")}`, first);
36
+ };
37
+ /**
38
+ * Path of `to` relative to `from`, both normalized. Returns `""` when they
39
+ * are the same path and `to` unchanged when it is not under `from` (no
40
+ * `../` climbing — the core has no notion of a filesystem to climb through).
41
+ * @public
42
+ */
43
+ const relativePosix = (from, to) => {
44
+ const base = toPosix(from).replace(/\/+$/, "");
45
+ const target = toPosix(to);
46
+ if (target === base) return "";
47
+ const prefix = `${base}/`;
48
+ return target.startsWith(prefix) ? target.slice(prefix.length) : target;
49
+ };
50
+
51
+ //#endregion
52
+ export { basenamePosix, joinPosix, relativePosix, toPosix };
@@ -1,4 +1,4 @@
1
- import { basename, join, relative, sep } from "node:path";
1
+ import { basenamePosix, joinPosix, relativePosix, toPosix } from "./posix-path.js";
2
2
 
3
3
  //#region src/utils/test-location.ts
4
4
  /** Directory holding source files whose tests may be co-located. @public */
@@ -58,9 +58,15 @@ const TEST_FILE_SUFFIX_RE = /\.(?:test|spec)\.(?:ts|tsx|js|jsx)$/;
58
58
  function isTestFileName(filePathOrName) {
59
59
  return TEST_FILE_SUFFIX_RE.test(filePathOrName);
60
60
  }
61
- /** True when `child` is `parent` or sits beneath it on a segment boundary. */
61
+ /**
62
+ * True when `child` is `parent` or sits beneath it on a segment boundary.
63
+ * Both sides are normalized to forward slashes first, so a Windows path
64
+ * compares the same way a POSIX one does.
65
+ */
62
66
  function contains(parent, child) {
63
- return child === parent || child.startsWith(parent.endsWith(sep) ? parent : `${parent}${sep}`);
67
+ const parentPosix = toPosix(parent);
68
+ const childPosix = toPosix(child);
69
+ return childPosix === parentPosix || childPosix.startsWith(parentPosix.endsWith("/") ? parentPosix : `${parentPosix}/`);
64
70
  }
65
71
  /**
66
72
  * Returns the deepest supplied workspace containing `filePath`, or `null` when
@@ -100,7 +106,7 @@ function findOwningWorkspace(workspaces, filePath) {
100
106
  function classifyTestPath(workspaces, filePath) {
101
107
  const owner = findOwningWorkspace(workspaces, filePath);
102
108
  if (owner === null) return null;
103
- const segments = relative(owner.path, filePath).split(sep);
109
+ const segments = relativePosix(owner.path, filePath).split("/");
104
110
  if (segments.some((s) => NON_DISCOVERABLE_DIRS.has(s))) return null;
105
111
  const [head] = segments;
106
112
  if (head === "src") return {
@@ -119,7 +125,7 @@ function classifyTestPath(workspaces, filePath) {
119
125
  return {
120
126
  verdict: "invalid",
121
127
  workspace: owner.name,
122
- suggestedPath: join(owner.path, TEST_DIR, basename(filePath))
128
+ suggestedPath: joinPosix(owner.path, TEST_DIR, basenamePosix(filePath))
123
129
  };
124
130
  }
125
131
 
package/version.js ADDED
@@ -0,0 +1,17 @@
1
+ //#region src/version.ts
2
+ /**
3
+ * The version of this package. Inlined at build time from
4
+ * package.json#version via rslib-builder's __PACKAGE_VERSION__ substitution.
5
+ * Source-level reads (workspace `exports: "./src/index.ts"` during dev)
6
+ * see the `"0.0.0"` fallback — a clear signal the build pipeline has not
7
+ * substituted yet. Exported for version introspection by downstream tooling.
8
+ *
9
+ * This is the one sanctioned `process` read in the platform-free core: the
10
+ * bundler replaces the token at build time, so no runtime `process` access
11
+ * survives into the published artifact.
12
+ * @public
13
+ */
14
+ const CURRENT_SDK_VERSION = "5.0.0";
15
+
16
+ //#endregion
17
+ export { CURRENT_SDK_VERSION };
@@ -1,46 +0,0 @@
1
- import { VitestAgentConfig } from "../schemas/Config.js";
2
- import { VitestAgentConfigFile } from "../services/Config.js";
3
- import { ConfigFile, ConfigResolver, MergeStrategy, TomlCodec } from "@effected/config-file";
4
-
5
- //#region src/layers/ConfigLive.ts
6
- const CONFIG_FILENAME = "vitest-agent.config.toml";
7
- /**
8
- * Build the live `ConfigFile` layer for a given project directory.
9
- *
10
- * Resolves the optional `vitest-agent.config.toml` from (in order):
11
- *
12
- * 1. The workspace root (when `projectDir` is inside a pnpm/npm/yarn workspace).
13
- * 2. The git repository root (when `projectDir` is inside a git repo).
14
- * 3. Walking upward from `projectDir`.
15
- *
16
- * The first found file wins (`MergeStrategy.firstMatch()`). If no file is
17
- * present, downstream callers use `loadOrDefault(new VitestAgentConfig({}))`
18
- * to get an empty config.
19
- *
20
- * @param projectDir - Absolute path inside the user's workspace. Resolvers
21
- * anchor here rather than `process.cwd()` so the plugin-spawned MCP server
22
- * sees the right config even when invoked from elsewhere.
23
- * @public
24
- */
25
- const ConfigLive = (projectDir) => ConfigFile.layer(VitestAgentConfigFile, {
26
- schema: VitestAgentConfig,
27
- codec: TomlCodec,
28
- strategy: MergeStrategy.firstMatch(),
29
- resolvers: [
30
- ConfigResolver.workspaceRoot({
31
- filename: CONFIG_FILENAME,
32
- cwd: projectDir
33
- }),
34
- ConfigResolver.gitRoot({
35
- filename: CONFIG_FILENAME,
36
- cwd: projectDir
37
- }),
38
- ConfigResolver.upwardWalk({
39
- filename: CONFIG_FILENAME,
40
- cwd: projectDir
41
- })
42
- ]
43
- });
44
-
45
- //#endregion
46
- export { ConfigLive };