@vitest-agent/mcp 1.3.6 → 2.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.
@@ -1,6 +1,6 @@
1
1
  import { publicProcedure } from "../context.js";
2
2
  import { CoverageTotals, DataReader, FileCoverageReport } from "@vitest-agent/sdk";
3
- import { Effect, Option, ParseResult, Schema } from "effect";
3
+ import { Effect, Option, Schema, SchemaGetter } from "effect";
4
4
 
5
5
  //#region src/tools/file-coverage.ts
6
6
  /**
@@ -21,19 +21,23 @@ const FileCoverageMatched = Schema.Struct({
21
21
  report: FileCoverageReport,
22
22
  globalThresholds: CoverageGlobalThresholds,
23
23
  relatedTestFiles: Schema.Array(Schema.String)
24
- }).annotations({ identifier: "FileCoverageMatched" });
24
+ }).annotate({ identifier: "FileCoverageMatched" });
25
25
  const FileCoverageNoMatch = Schema.Struct({
26
26
  dataAvailable: Schema.Literal(true),
27
27
  matched: Schema.Literal(false),
28
28
  filePath: Schema.String,
29
29
  totals: CoverageTotals,
30
30
  relatedTestFiles: Schema.Array(Schema.String)
31
- }).annotations({ identifier: "FileCoverageNoMatch" });
31
+ }).annotate({ identifier: "FileCoverageNoMatch" });
32
32
  const FileCoverageAbsent = Schema.Struct({
33
33
  dataAvailable: Schema.Literal(false),
34
34
  filePath: Schema.String
35
- }).annotations({ identifier: "FileCoverageAbsent" });
36
- const FileCoverageResult = Schema.Union(FileCoverageMatched, FileCoverageNoMatch, FileCoverageAbsent).annotations({
35
+ }).annotate({ identifier: "FileCoverageAbsent" });
36
+ const FileCoverageResult = Schema.Union([
37
+ FileCoverageMatched,
38
+ FileCoverageNoMatch,
39
+ FileCoverageAbsent
40
+ ]).annotate({
37
41
  identifier: "FileCoverageResult",
38
42
  title: "file_coverage result",
39
43
  description: "Per-file coverage with related tests. Discriminate on `dataAvailable` then on `matched`."
@@ -65,12 +69,11 @@ const formatFileCoverageMarkdown = (data) => {
65
69
  }
66
70
  return lines.join("\n");
67
71
  };
68
- const FileCoverageAsMarkdown = Schema.transformOrFail(FileCoverageResult, Schema.String, {
69
- strict: true,
70
- decode: (data) => ParseResult.succeed(formatFileCoverageMarkdown(data)),
71
- encode: (text, _options, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, text, "FileCoverageAsMarkdown is one-way."))
72
- });
73
- const fileCoverage = publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({
72
+ const FileCoverageAsMarkdown = FileCoverageResult.pipe(Schema.decodeTo(Schema.String, {
73
+ decode: SchemaGetter.transform((data) => formatFileCoverageMarkdown(data)),
74
+ encode: SchemaGetter.forbidden(() => "FileCoverageAsMarkdown is one-way.")
75
+ }));
76
+ const fileCoverage = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({
74
77
  filePath: Schema.String,
75
78
  project: Schema.optional(Schema.String)
76
79
  }))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
package/tools/help.js CHANGED
@@ -2,7 +2,7 @@ import { publicProcedure } from "../context.js";
2
2
  import { Schema } from "effect";
3
3
 
4
4
  //#region src/tools/help.ts
5
- const HelpResult = Schema.Struct({ helpText: Schema.String.annotations({ description: "Markdown table of every MCP tool with parameters and a one-line description." }) }).annotations({
5
+ const HelpResult = Schema.Struct({ helpText: Schema.String.annotate({ description: "Markdown table of every MCP tool with parameters and a one-line description." }) }).annotate({
6
6
  identifier: "HelpResult",
7
7
  title: "help result",
8
8
  description: "Static help reference. Read structuredContent.helpText programmatically; the same string lives in content[].text for transcripts."
package/tools/history.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { publicProcedure } from "../context.js";
2
2
  import { DataReader, HistoryRecord } from "@vitest-agent/sdk";
3
- import { Effect, ParseResult, Schema } from "effect";
3
+ import { Effect, Schema, SchemaGetter } from "effect";
4
4
 
5
5
  //#region src/tools/history.ts
6
6
  /**
@@ -13,45 +13,45 @@ import { Effect, ParseResult, Schema } from "effect";
13
13
  * @packageDocumentation
14
14
  */
15
15
  const FlakyTestRow = Schema.Struct({
16
- fullName: Schema.String.annotations({ description: "Full hierarchical test name (`describe > it`)." }),
17
- modulePath: Schema.String.annotations({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
16
+ fullName: Schema.String.annotate({ description: "Full hierarchical test name (`describe > it`)." }),
17
+ modulePath: Schema.String.annotate({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
18
18
  project: Schema.String,
19
- passCount: Schema.Number.annotations({ description: "Number of passing runs in the recent window." }),
20
- failCount: Schema.Number.annotations({ description: "Number of failing runs in the recent window." }),
21
- lastState: Schema.Literal("passed", "failed").annotations({ description: "State of the most recent run." }),
22
- lastTimestamp: Schema.String.annotations({ description: "ISO-8601 timestamp of the most recent run." })
23
- }).annotations({
19
+ passCount: Schema.Number.annotate({ description: "Number of passing runs in the recent window." }),
20
+ failCount: Schema.Number.annotate({ description: "Number of failing runs in the recent window." }),
21
+ lastState: Schema.Literals(["passed", "failed"]).annotate({ description: "State of the most recent run." }),
22
+ lastTimestamp: Schema.String.annotate({ description: "ISO-8601 timestamp of the most recent run." })
23
+ }).annotate({
24
24
  identifier: "FlakyTestRow",
25
25
  description: "A test that produced both passes and failures within the recent run window."
26
26
  });
27
27
  const PersistentFailureRow = Schema.Struct({
28
28
  fullName: Schema.String,
29
- modulePath: Schema.String.annotations({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
29
+ modulePath: Schema.String.annotate({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
30
30
  project: Schema.String,
31
- consecutiveFailures: Schema.Number.annotations({ description: "Length of the current uninterrupted failure streak." }),
32
- firstFailedAt: Schema.String.annotations({ description: "ISO-8601 timestamp of the first failure in this streak." }),
33
- lastFailedAt: Schema.String.annotations({ description: "ISO-8601 timestamp of the most recent failure." }),
34
- lastErrorMessage: Schema.NullOr(Schema.String).annotations({ description: "Last error message reported by the failing test, when captured." })
35
- }).annotations({
31
+ consecutiveFailures: Schema.Number.annotate({ description: "Length of the current uninterrupted failure streak." }),
32
+ firstFailedAt: Schema.String.annotate({ description: "ISO-8601 timestamp of the first failure in this streak." }),
33
+ lastFailedAt: Schema.String.annotate({ description: "ISO-8601 timestamp of the most recent failure." }),
34
+ lastErrorMessage: Schema.NullOr(Schema.String).annotate({ description: "Last error message reported by the failing test, when captured." })
35
+ }).annotate({
36
36
  identifier: "PersistentFailureRow",
37
37
  description: "A test that has failed in every recent run since `firstFailedAt`."
38
38
  });
39
39
  const RecoveredTestRow = Schema.Struct({
40
- modulePath: Schema.String.annotations({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
40
+ modulePath: Schema.String.annotate({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
41
41
  fullName: Schema.String,
42
- recentRuns: Schema.Array(Schema.Literal("passed", "failed")).annotations({ description: "Last 10 run states for this test, oldest first." })
43
- }).annotations({
42
+ recentRuns: Schema.Array(Schema.Literals(["passed", "failed"])).annotate({ description: "Last 10 run states for this test, oldest first." })
43
+ }).annotate({
44
44
  identifier: "RecoveredTestRow",
45
45
  description: "A test whose latest run passed after the previous one failed."
46
46
  });
47
47
  const TestHistoryResult = Schema.Struct({
48
- project: Schema.String.annotations({ description: "Workspace project key the history was computed for." }),
49
- hasData: Schema.Boolean.annotations({ description: "`false` when no history rows exist for the project — agent should suggest running tests first." }),
50
- history: HistoryRecord.annotations({ description: "Raw per-test history record (stored in `test_runs` joins)." }),
51
- flaky: Schema.Array(FlakyTestRow).annotations({ description: "Tests with mixed pass/fail outcomes recently." }),
52
- persistent: Schema.Array(PersistentFailureRow).annotations({ description: "Tests failing across consecutive runs." }),
53
- recovered: Schema.Array(RecoveredTestRow).annotations({ description: "Tests that just transitioned from failing to passing in the last run." })
54
- }).annotations({
48
+ project: Schema.String.annotate({ description: "Workspace project key the history was computed for." }),
49
+ hasData: Schema.Boolean.annotate({ description: "`false` when no history rows exist for the project — agent should suggest running tests first." }),
50
+ history: HistoryRecord.annotate({ description: "Raw per-test history record (stored in `test_runs` joins)." }),
51
+ flaky: Schema.Array(FlakyTestRow).annotate({ description: "Tests with mixed pass/fail outcomes recently." }),
52
+ persistent: Schema.Array(PersistentFailureRow).annotate({ description: "Tests failing across consecutive runs." }),
53
+ recovered: Schema.Array(RecoveredTestRow).annotate({ description: "Tests that just transitioned from failing to passing in the last run." })
54
+ }).annotate({
55
55
  identifier: "TestHistoryResult",
56
56
  title: "test_history result",
57
57
  description: "Per-project flaky/persistent/recovered test classifications computed from `test_runs` history."
@@ -87,12 +87,11 @@ const formatTestHistoryMarkdown = (data) => {
87
87
  lines.push(`_History updated: ${data.history.updatedAt}_`);
88
88
  return lines.join("\n");
89
89
  };
90
- const TestHistoryAsMarkdown = Schema.transformOrFail(TestHistoryResult, Schema.String, {
91
- strict: true,
92
- decode: (data) => ParseResult.succeed(formatTestHistoryMarkdown(data)),
93
- encode: (text, _options, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, text, "TestHistoryAsMarkdown is one-way: markdown cannot be parsed back to TestHistoryResult."))
94
- });
95
- const testHistory = publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({ project: Schema.String }))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
90
+ const TestHistoryAsMarkdown = TestHistoryResult.pipe(Schema.decodeTo(Schema.String, {
91
+ decode: SchemaGetter.transform((data) => formatTestHistoryMarkdown(data)),
92
+ encode: SchemaGetter.forbidden(() => "TestHistoryAsMarkdown is one-way: markdown cannot be parsed back to TestHistoryResult.")
93
+ }));
94
+ const testHistory = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({ project: Schema.String }))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
96
95
  const reader = yield* DataReader;
97
96
  const [history, flaky, persistent] = yield* Effect.all([
98
97
  reader.getHistory(input.project),
@@ -16,15 +16,19 @@ import { Effect, Match, Option, Schema } from "effect";
16
16
  const HypothesisRowSchema = Schema.Struct({
17
17
  id: Schema.Number,
18
18
  sessionId: Schema.Number,
19
- content: Schema.String.annotations({ description: "Free-text hypothesis the agent recorded before attempting a fix." }),
20
- citedTestErrorId: Schema.NullOr(Schema.Number).annotations({ description: "Optional `test_errors.id` the hypothesis cites as the failing observation." }),
21
- citedStackFrameId: Schema.NullOr(Schema.Number).annotations({ description: "Optional `stack_frames.id` for the specific frame the hypothesis blames." }),
22
- validationOutcome: Schema.NullOr(Schema.Literal("confirmed", "refuted", "abandoned")).annotations({ description: "Outcome recorded by `hypothesis (action: validate)`; `null` while still open." }),
23
- validatedAt: Schema.NullOr(Schema.String).annotations({ description: "ISO-8601 validation timestamp; `null` while open." })
24
- }).annotations({ identifier: "HypothesisRow" });
19
+ content: Schema.String.annotate({ description: "Free-text hypothesis the agent recorded before attempting a fix." }),
20
+ citedTestErrorId: Schema.NullOr(Schema.Number).annotate({ description: "Optional `test_errors.id` the hypothesis cites as the failing observation." }),
21
+ citedStackFrameId: Schema.NullOr(Schema.Number).annotate({ description: "Optional `stack_frames.id` for the specific frame the hypothesis blames." }),
22
+ validationOutcome: Schema.NullOr(Schema.Literals([
23
+ "confirmed",
24
+ "refuted",
25
+ "abandoned"
26
+ ])).annotate({ description: "Outcome recorded by `hypothesis (action: validate)`; `null` while still open." }),
27
+ validatedAt: Schema.NullOr(Schema.String).annotate({ description: "ISO-8601 validation timestamp; `null` while open." })
28
+ }).annotate({ identifier: "HypothesisRow" });
25
29
  const HypothesisRecordOk = Schema.Struct({
26
30
  action: Schema.Literal("record"),
27
- id: Schema.Number.annotations({ description: "Newly inserted hypothesis row primary key." })
31
+ id: Schema.Number.annotate({ description: "Newly inserted hypothesis row primary key." })
28
32
  });
29
33
  const HypothesisValidateOk = Schema.Struct({ action: Schema.Literal("validate") });
30
34
  const HypothesisListOk = Schema.Struct({
@@ -32,7 +36,11 @@ const HypothesisListOk = Schema.Struct({
32
36
  count: Schema.Number,
33
37
  hypotheses: Schema.Array(HypothesisRowSchema)
34
38
  });
35
- const HypothesisResult = Schema.Union(HypothesisRecordOk, HypothesisValidateOk, HypothesisListOk).annotations({
39
+ const HypothesisResult = Schema.Union([
40
+ HypothesisRecordOk,
41
+ HypothesisValidateOk,
42
+ HypothesisListOk
43
+ ]).annotate({
36
44
  identifier: "HypothesisResult",
37
45
  title: "hypothesis result",
38
46
  description: "Discriminate on `action`. record returns the new id; list returns the matching rows; validate returns an empty acknowledgement."
@@ -58,18 +66,31 @@ const RecordVariant = Schema.Struct({
58
66
  const ValidateVariant = Schema.Struct({
59
67
  action: Schema.Literal("validate"),
60
68
  id: Schema.Number,
61
- outcome: Schema.Literal("confirmed", "refuted", "abandoned"),
69
+ outcome: Schema.Literals([
70
+ "confirmed",
71
+ "refuted",
72
+ "abandoned"
73
+ ]),
62
74
  validatedTurnId: Schema.optional(Schema.Number),
63
75
  validatedAt: Schema.String
64
76
  });
65
77
  const ListVariant = Schema.Struct({
66
78
  action: Schema.Literal("list"),
67
79
  sessionId: Schema.optional(Schema.Number),
68
- outcome: Schema.optional(Schema.Literal("confirmed", "refuted", "abandoned", "open")),
80
+ outcome: Schema.optional(Schema.Literals([
81
+ "confirmed",
82
+ "refuted",
83
+ "abandoned",
84
+ "open"
85
+ ])),
69
86
  limit: Schema.optional(Schema.Number)
70
87
  });
71
- const HypothesisInput = Schema.Union(RecordVariant, ValidateVariant, ListVariant);
72
- const hypothesis = idempotentProcedure.input(Schema.standardSchemaV1(HypothesisInput)).mutation(async ({ ctx, input }) => {
88
+ const HypothesisInput = Schema.Union([
89
+ RecordVariant,
90
+ ValidateVariant,
91
+ ListVariant
92
+ ]);
93
+ const hypothesis = idempotentProcedure.input(Schema.toStandardSchemaV1(HypothesisInput)).mutation(async ({ ctx, input }) => {
73
94
  return ctx.runtime.runPromise(Match.value(input).pipe(Match.discriminatorsExhaustive("action")({
74
95
  record: (variant) => Effect.gen(function* () {
75
96
  const store = yield* DataStore;
@@ -1,6 +1,6 @@
1
1
  import { publicProcedure } from "../context.js";
2
2
  import { DataReader } from "@vitest-agent/sdk";
3
- import { Effect, Match, Option, ParseResult, Schema } from "effect";
3
+ import { Effect, Match, Option, Schema, SchemaGetter } from "effect";
4
4
 
5
5
  //#region src/tools/inventory.ts
6
6
  /**
@@ -18,44 +18,48 @@ import { Effect, Match, Option, ParseResult, Schema } from "effect";
18
18
  const ProjectRunSummary = Schema.Struct({
19
19
  project: Schema.String,
20
20
  lastRun: Schema.NullOr(Schema.String),
21
- lastResult: Schema.NullOr(Schema.Literal("passed", "failed", "interrupted")),
21
+ lastResult: Schema.NullOr(Schema.Literals([
22
+ "passed",
23
+ "failed",
24
+ "interrupted"
25
+ ])),
22
26
  total: Schema.Number,
23
27
  passed: Schema.Number,
24
28
  failed: Schema.Number,
25
29
  skipped: Schema.Number
26
- }).annotations({ identifier: "InventoryProjectRow" });
30
+ }).annotate({ identifier: "InventoryProjectRow" });
27
31
  const ModuleRow = Schema.Struct({
28
32
  id: Schema.Number,
29
33
  file: Schema.String,
30
34
  state: Schema.String,
31
35
  testCount: Schema.Number,
32
36
  duration: Schema.NullOr(Schema.Number)
33
- }).annotations({ identifier: "InventoryModuleRow" });
37
+ }).annotate({ identifier: "InventoryModuleRow" });
34
38
  const SuiteRow = Schema.Struct({
35
39
  id: Schema.Number,
36
40
  name: Schema.String,
37
41
  module: Schema.String,
38
42
  state: Schema.String,
39
43
  testCount: Schema.Number
40
- }).annotations({ identifier: "InventorySuiteRow" });
44
+ }).annotate({ identifier: "InventorySuiteRow" });
41
45
  const SessionRow = Schema.Struct({
42
46
  id: Schema.Number,
43
47
  chatId: Schema.String,
44
48
  project: Schema.String,
45
49
  cwd: Schema.String,
46
- agentKind: Schema.Literal("main", "subagent"),
50
+ agentKind: Schema.Literals(["main", "subagent"]),
47
51
  agentType: Schema.NullOr(Schema.String),
48
52
  parentSessionId: Schema.NullOr(Schema.Number),
49
53
  triageWasNonEmpty: Schema.Boolean,
50
54
  startedAt: Schema.String,
51
55
  endedAt: Schema.NullOr(Schema.String),
52
56
  endReason: Schema.NullOr(Schema.String)
53
- }).annotations({ identifier: "InventorySessionRow" });
57
+ }).annotate({ identifier: "InventorySessionRow" });
54
58
  const ProjectInventory = Schema.Struct({
55
59
  inventoryKind: Schema.Literal("project"),
56
60
  count: Schema.Number,
57
61
  projects: Schema.Array(ProjectRunSummary)
58
- }).annotations({ identifier: "ProjectInventory" });
62
+ }).annotate({ identifier: "ProjectInventory" });
59
63
  const ModuleGroup = Schema.Struct({
60
64
  project: Schema.String,
61
65
  modules: Schema.Array(ModuleRow)
@@ -64,7 +68,7 @@ const ModuleInventory = Schema.Struct({
64
68
  inventoryKind: Schema.Literal("module"),
65
69
  count: Schema.Number,
66
70
  groups: Schema.Array(ModuleGroup)
67
- }).annotations({ identifier: "ModuleInventory" });
71
+ }).annotate({ identifier: "ModuleInventory" });
68
72
  const SuiteGroup = Schema.Struct({
69
73
  project: Schema.String,
70
74
  suites: Schema.Array(SuiteRow)
@@ -73,50 +77,59 @@ const SuiteInventory = Schema.Struct({
73
77
  inventoryKind: Schema.Literal("suite"),
74
78
  count: Schema.Number,
75
79
  groups: Schema.Array(SuiteGroup)
76
- }).annotations({ identifier: "SuiteInventory" });
80
+ }).annotate({ identifier: "SuiteInventory" });
77
81
  const SessionDetailFound = Schema.Struct({
78
82
  inventoryKind: Schema.Literal("session_detail"),
79
83
  found: Schema.Literal(true),
80
84
  session: SessionRow
81
- }).annotations({ identifier: "SessionDetailFound" });
85
+ }).annotate({ identifier: "SessionDetailFound" });
82
86
  const SessionDetailMissing = Schema.Struct({
83
87
  inventoryKind: Schema.Literal("session_detail"),
84
88
  found: Schema.Literal(false),
85
89
  id: Schema.Number
86
- }).annotations({ identifier: "SessionDetailMissing" });
90
+ }).annotate({ identifier: "SessionDetailMissing" });
87
91
  const SessionListInventory = Schema.Struct({
88
92
  inventoryKind: Schema.Literal("session_list"),
89
93
  count: Schema.Number,
90
94
  sessions: Schema.Array(SessionRow)
91
- }).annotations({ identifier: "SessionListInventory" });
95
+ }).annotate({ identifier: "SessionListInventory" });
92
96
  const TagProjectBreakdown = Schema.Struct({
93
97
  project: Schema.String,
94
98
  moduleCount: Schema.Number,
95
99
  testCount: Schema.Number
96
- }).annotations({ identifier: "TagProjectBreakdown" });
100
+ }).annotate({ identifier: "TagProjectBreakdown" });
97
101
  const TagRowScoped = Schema.Struct({
98
102
  tag: Schema.String,
99
103
  moduleCount: Schema.Number,
100
104
  testCount: Schema.Number
101
- }).annotations({ identifier: "TagRowScoped" });
105
+ }).annotate({ identifier: "TagRowScoped" });
102
106
  const TagRowUnscoped = Schema.Struct({
103
107
  tag: Schema.String,
104
108
  moduleCount: Schema.Number,
105
109
  testCount: Schema.Number,
106
110
  byProject: Schema.Array(TagProjectBreakdown)
107
- }).annotations({ identifier: "TagRowUnscoped" });
111
+ }).annotate({ identifier: "TagRowUnscoped" });
108
112
  const TagInventoryScoped = Schema.Struct({
109
113
  inventoryKind: Schema.Literal("tag_scoped"),
110
114
  project: Schema.String,
111
115
  count: Schema.Number,
112
116
  tags: Schema.Array(TagRowScoped)
113
- }).annotations({ identifier: "TagInventoryScoped" });
117
+ }).annotate({ identifier: "TagInventoryScoped" });
114
118
  const TagInventoryUnscoped = Schema.Struct({
115
119
  inventoryKind: Schema.Literal("tag_unscoped"),
116
120
  count: Schema.Number,
117
121
  tags: Schema.Array(TagRowUnscoped)
118
- }).annotations({ identifier: "TagInventoryUnscoped" });
119
- const InventoryResult = Schema.Union(ProjectInventory, ModuleInventory, SuiteInventory, SessionDetailFound, SessionDetailMissing, SessionListInventory, TagInventoryScoped, TagInventoryUnscoped).annotations({
122
+ }).annotate({ identifier: "TagInventoryUnscoped" });
123
+ const InventoryResult = Schema.Union([
124
+ ProjectInventory,
125
+ ModuleInventory,
126
+ SuiteInventory,
127
+ SessionDetailFound,
128
+ SessionDetailMissing,
129
+ SessionListInventory,
130
+ TagInventoryScoped,
131
+ TagInventoryUnscoped
132
+ ]).annotate({
120
133
  identifier: "InventoryResult",
121
134
  title: "inventory result",
122
135
  description: "Discriminate on `inventoryKind`. project/module/suite carry counted lists; session_detail discriminates further on `found`; session_list returns the matching sessions; tag_scoped and tag_unscoped carry per-tag counts (the unscoped form also carries a `byProject` breakdown per tag)."
@@ -209,11 +222,10 @@ const formatInventoryMarkdown = (data) => {
209
222
  }
210
223
  return lines.join("\n");
211
224
  };
212
- const InventoryAsMarkdown = Schema.transformOrFail(InventoryResult, Schema.String, {
213
- strict: true,
214
- decode: (data) => ParseResult.succeed(formatInventoryMarkdown(data)),
215
- encode: (text, _options, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, text, "InventoryAsMarkdown is one-way."))
216
- });
225
+ const InventoryAsMarkdown = InventoryResult.pipe(Schema.decodeTo(Schema.String, {
226
+ decode: SchemaGetter.transform((data) => formatInventoryMarkdown(data)),
227
+ encode: SchemaGetter.forbidden(() => "InventoryAsMarkdown is one-way.")
228
+ }));
217
229
  const ProjectVariant = Schema.Struct({ kind: Schema.Literal("project") });
218
230
  const ModuleVariant = Schema.Struct({
219
231
  kind: Schema.Literal("module"),
@@ -228,15 +240,21 @@ const SessionVariant = Schema.Struct({
228
240
  kind: Schema.Literal("session"),
229
241
  id: Schema.optional(Schema.Number),
230
242
  project: Schema.optional(Schema.String),
231
- agentKind: Schema.optional(Schema.Literal("main", "subagent")),
243
+ agentKind: Schema.optional(Schema.Literals(["main", "subagent"])),
232
244
  limit: Schema.optional(Schema.Number)
233
245
  });
234
246
  const TagVariant = Schema.Struct({
235
247
  kind: Schema.Literal("tag"),
236
248
  project: Schema.optional(Schema.String)
237
249
  });
238
- const InventoryInput = Schema.Union(ProjectVariant, ModuleVariant, SuiteVariant, SessionVariant, TagVariant);
239
- const inventory = publicProcedure.input(Schema.standardSchemaV1(InventoryInput)).query(async ({ ctx, input }) => {
250
+ const InventoryInput = Schema.Union([
251
+ ProjectVariant,
252
+ ModuleVariant,
253
+ SuiteVariant,
254
+ SessionVariant,
255
+ TagVariant
256
+ ]);
257
+ const inventory = publicProcedure.input(Schema.toStandardSchemaV1(InventoryInput)).query(async ({ ctx, input }) => {
240
258
  return ctx.runtime.runPromise(Match.value(input).pipe(Match.discriminatorsExhaustive("kind")({
241
259
  project: () => Effect.gen(function* () {
242
260
  const projects = yield* (yield* DataReader).getRunsByProject();
package/tools/note.js CHANGED
@@ -3,12 +3,19 @@ import { DataReader, DataStore } from "@vitest-agent/sdk";
3
3
  import { Effect, Match, Option, Schema } from "effect";
4
4
 
5
5
  //#region src/tools/note.ts
6
- const NoteScope = Schema.Literal("global", "project", "module", "suite", "test", "note");
6
+ const NoteScope = Schema.Literals([
7
+ "global",
8
+ "project",
9
+ "module",
10
+ "suite",
11
+ "test",
12
+ "note"
13
+ ]);
7
14
  const NoteRowSchema = Schema.Struct({
8
- id: Schema.Number.annotations({ description: "Note primary key." }),
15
+ id: Schema.Number.annotate({ description: "Note primary key." }),
9
16
  title: Schema.String,
10
17
  content: Schema.String,
11
- scope: NoteScope.annotations({ description: "`global` (project-agnostic), `project`, `module`, `suite`, `test` (scoped), or `note` (child note attached via `parentNoteId`)." }),
18
+ scope: NoteScope.annotate({ description: "`global` (project-agnostic), `project`, `module`, `suite`, `test` (scoped), or `note` (child note attached via `parentNoteId`)." }),
12
19
  project: Schema.NullOr(Schema.String),
13
20
  testFullName: Schema.NullOr(Schema.String),
14
21
  modulePath: Schema.NullOr(Schema.String),
@@ -18,15 +25,15 @@ const NoteRowSchema = Schema.Struct({
18
25
  pinned: Schema.Boolean,
19
26
  createdAt: Schema.String,
20
27
  updatedAt: Schema.String
21
- }).annotations({ identifier: "NoteRow" });
28
+ }).annotate({ identifier: "NoteRow" });
22
29
  const NoteCreateOk = Schema.Struct({
23
30
  action: Schema.Literal("create"),
24
- id: Schema.Number.annotations({ description: "Primary key of the newly inserted note." })
31
+ id: Schema.Number.annotate({ description: "Primary key of the newly inserted note." })
25
32
  });
26
33
  const NoteListOk = Schema.Struct({
27
34
  action: Schema.Literal("list"),
28
35
  count: Schema.Number,
29
- notes: Schema.Array(NoteRowSchema).annotations({ description: "Notes matching the optional scope/project/test filters." })
36
+ notes: Schema.Array(NoteRowSchema).annotate({ description: "Notes matching the optional scope/project/test filters." })
30
37
  });
31
38
  const NoteGetFound = Schema.Struct({
32
39
  action: Schema.Literal("get"),
@@ -50,9 +57,17 @@ const NoteSearchOk = Schema.Struct({
50
57
  action: Schema.Literal("search"),
51
58
  query: Schema.String,
52
59
  count: Schema.Number,
53
- notes: Schema.Array(NoteRowSchema).annotations({ description: "Notes whose title or content match the FTS5 query." })
60
+ notes: Schema.Array(NoteRowSchema).annotate({ description: "Notes whose title or content match the FTS5 query." })
54
61
  });
55
- const NoteResult = Schema.Union(NoteCreateOk, NoteListOk, NoteGetFound, NoteGetMissing, NoteUpdateOk, NoteDeleteOk, NoteSearchOk).annotations({
62
+ const NoteResult = Schema.Union([
63
+ NoteCreateOk,
64
+ NoteListOk,
65
+ NoteGetFound,
66
+ NoteGetMissing,
67
+ NoteUpdateOk,
68
+ NoteDeleteOk,
69
+ NoteSearchOk
70
+ ]).annotate({
56
71
  identifier: "NoteResult",
57
72
  title: "note result",
58
73
  description: "Discriminate on `action`. `get` further discriminates on `found`."
@@ -129,8 +144,15 @@ const SearchVariant = Schema.Struct({
129
144
  action: Schema.Literal("search"),
130
145
  query: Schema.String
131
146
  });
132
- const NoteInputUnion = Schema.Union(CreateVariant, ListVariant, GetVariant, UpdateVariant, DeleteVariant, SearchVariant);
133
- const note = publicProcedure.input(Schema.standardSchemaV1(NoteInputUnion)).mutation(async ({ ctx, input }) => {
147
+ const NoteInputUnion = Schema.Union([
148
+ CreateVariant,
149
+ ListVariant,
150
+ GetVariant,
151
+ UpdateVariant,
152
+ DeleteVariant,
153
+ SearchVariant
154
+ ]);
155
+ const note = publicProcedure.input(Schema.toStandardSchemaV1(NoteInputUnion)).mutation(async ({ ctx, input }) => {
134
156
  return ctx.runtime.runPromise(Match.value(input).pipe(Match.discriminatorsExhaustive("action")({
135
157
  create: (variant) => Effect.gen(function* () {
136
158
  const store = yield* DataStore;
package/tools/overview.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { publicProcedure } from "../context.js";
2
2
  import { DataReader } from "@vitest-agent/sdk";
3
- import { Effect, Option, ParseResult, Schema } from "effect";
3
+ import { Effect, Option, Schema, SchemaGetter } from "effect";
4
4
 
5
5
  //#region src/tools/overview.ts
6
6
  /**
@@ -11,12 +11,16 @@ import { Effect, Option, ParseResult, Schema } from "effect";
11
11
  const ProjectRunSummary = Schema.Struct({
12
12
  project: Schema.String,
13
13
  lastRun: Schema.NullOr(Schema.String),
14
- lastResult: Schema.NullOr(Schema.Literal("passed", "failed", "interrupted")),
14
+ lastResult: Schema.NullOr(Schema.Literals([
15
+ "passed",
16
+ "failed",
17
+ "interrupted"
18
+ ])),
15
19
  total: Schema.Number,
16
20
  passed: Schema.Number,
17
21
  failed: Schema.Number,
18
22
  skipped: Schema.Number
19
- }).annotations({
23
+ }).annotate({
20
24
  identifier: "ProjectRunSummary",
21
25
  description: "One row per project's most recent run summary."
22
26
  });
@@ -24,13 +28,13 @@ const OverviewAvailable = Schema.Struct({
24
28
  dataAvailable: Schema.Literal(true),
25
29
  projectFilter: Schema.optional(Schema.String),
26
30
  runs: Schema.Array(ProjectRunSummary)
27
- }).annotations({ identifier: "TestOverviewAvailable" });
31
+ }).annotate({ identifier: "TestOverviewAvailable" });
28
32
  const OverviewAbsent = Schema.Struct({
29
33
  dataAvailable: Schema.Literal(false),
30
34
  projectFilter: Schema.optional(Schema.String),
31
- reason: Schema.Literal("no_runs", "project_filter_empty")
32
- }).annotations({ identifier: "TestOverviewAbsent" });
33
- const TestOverviewResult = Schema.Union(OverviewAvailable, OverviewAbsent).annotations({
35
+ reason: Schema.Literals(["no_runs", "project_filter_empty"])
36
+ }).annotate({ identifier: "TestOverviewAbsent" });
37
+ const TestOverviewResult = Schema.Union([OverviewAvailable, OverviewAbsent]).annotate({
34
38
  identifier: "TestOverviewResult",
35
39
  title: "test_overview result",
36
40
  description: "Per-project run metrics. Discriminate on `dataAvailable` for cold-start handling."
@@ -62,12 +66,11 @@ const formatTestOverviewMarkdown = (data) => {
62
66
  }
63
67
  return lines.join("\n");
64
68
  };
65
- const TestOverviewAsMarkdown = Schema.transformOrFail(TestOverviewResult, Schema.String, {
66
- strict: true,
67
- decode: (data) => ParseResult.succeed(formatTestOverviewMarkdown(data)),
68
- encode: (text, _options, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, text, "TestOverviewAsMarkdown is one-way."))
69
- });
70
- const testOverview = publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({ project: Schema.optional(Schema.String) }))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
69
+ const TestOverviewAsMarkdown = TestOverviewResult.pipe(Schema.decodeTo(Schema.String, {
70
+ decode: SchemaGetter.transform((data) => formatTestOverviewMarkdown(data)),
71
+ encode: SchemaGetter.forbidden(() => "TestOverviewAsMarkdown is one-way.")
72
+ }));
73
+ const testOverview = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({ project: Schema.optional(Schema.String) }))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
71
74
  const reader = yield* DataReader;
72
75
  const [manifestOpt, runs] = yield* Effect.all([reader.getManifest(), reader.getRunsByProject()]);
73
76
  if (Option.isNone(manifestOpt) || runs.length === 0) return {
package/tools/ping.js CHANGED
@@ -11,7 +11,7 @@ import { Schema } from "effect";
11
11
  *
12
12
  * @packageDocumentation
13
13
  */
14
- const PingResult = Schema.Struct({ message: Schema.Literal("pong").annotations({ description: "Constant `pong`. Presence confirms the MCP server responded." }) }).annotations({
14
+ const PingResult = Schema.Struct({ message: Schema.Literal("pong").annotate({ description: "Constant `pong`. Presence confirms the MCP server responded." }) }).annotate({
15
15
  identifier: "PingResult",
16
16
  title: "ping result",
17
17
  description: "Liveness probe. Carries no data beyond the constant `pong` discriminant."
@@ -43,29 +43,34 @@ const RegisterAgentInput = Schema.Struct({
43
43
  startWorktreeDir: Schema.optional(Schema.String)
44
44
  });
45
45
  const RegisterAgentSuccess = Schema.Struct({
46
- ok: Schema.Literal(true).annotations({ description: "Discriminant — `true` when the agent row was inserted (or an existing one recovered)." }),
47
- agentId: Schema.String.annotations({
46
+ ok: Schema.Literal(true).annotate({ description: "Discriminant — `true` when the agent row was inserted (or an existing one recovered)." }),
47
+ agentId: Schema.String.annotate({
48
48
  title: "agents.agent_id",
49
49
  description: "Canonical UUID for the registered agent — pass to subsequent attribution-bearing calls."
50
50
  }),
51
- conversationId: Schema.NullOr(Schema.String).annotations({ description: "Conversation UUID from the host's transcript when one was supplied; `null` otherwise." }),
52
- idempotencyKey: Schema.String.annotations({ description: "26-char base32 SHA-256 of (agentType, parentAgentId|sentinel, clientNonce). Stable across retries with identical input." })
53
- }).annotations({ identifier: "RegisterAgentSuccess" });
51
+ conversationId: Schema.NullOr(Schema.String).annotate({ description: "Conversation UUID from the host's transcript when one was supplied; `null` otherwise." }),
52
+ idempotencyKey: Schema.String.annotate({ description: "26-char base32 SHA-256 of (agentType, parentAgentId|sentinel, clientNonce). Stable across retries with identical input." })
53
+ }).annotate({ identifier: "RegisterAgentSuccess" });
54
54
  const RegisterAgentFailure = Schema.Struct({
55
- ok: Schema.Literal(false).annotations({ description: "Discriminant — `false` when registration was refused." }),
55
+ ok: Schema.Literal(false).annotate({ description: "Discriminant — `false` when registration was refused." }),
56
56
  error: Schema.Struct({
57
- code: Schema.Literal("AGENT_ALREADY_REGISTERED", "PARENT_AGENT_NOT_FOUND", "SESSION_NOT_FOUND", "INVALID_AGENT_TYPE_PREFIX").annotations({ description: "Refusal reason. AGENT_ALREADY_REGISTERED carries `existingAgentId` so the caller can recover. INVALID_AGENT_TYPE_PREFIX carries `expectedPrefix`." }),
58
- message: Schema.String.annotations({ description: "Human-readable refusal explanation." }),
59
- existingAgentId: Schema.optional(Schema.String).annotations({ description: "Present only when `code = AGENT_ALREADY_REGISTERED`. Use this id instead of registering a new one." }),
60
- expectedPrefix: Schema.optional(Schema.String).annotations({ description: "Present only when `code = INVALID_AGENT_TYPE_PREFIX`. The required `<hostKind>-` prefix." })
57
+ code: Schema.Literals([
58
+ "AGENT_ALREADY_REGISTERED",
59
+ "PARENT_AGENT_NOT_FOUND",
60
+ "SESSION_NOT_FOUND",
61
+ "INVALID_AGENT_TYPE_PREFIX"
62
+ ]).annotate({ description: "Refusal reason. AGENT_ALREADY_REGISTERED carries `existingAgentId` so the caller can recover. INVALID_AGENT_TYPE_PREFIX carries `expectedPrefix`." }),
63
+ message: Schema.String.annotate({ description: "Human-readable refusal explanation." }),
64
+ existingAgentId: Schema.optional(Schema.String).annotate({ description: "Present only when `code = AGENT_ALREADY_REGISTERED`. Use this id instead of registering a new one." }),
65
+ expectedPrefix: Schema.optional(Schema.String).annotate({ description: "Present only when `code = INVALID_AGENT_TYPE_PREFIX`. The required `<hostKind>-` prefix." })
61
66
  })
62
- }).annotations({ identifier: "RegisterAgentFailure" });
63
- const RegisterAgentResult = Schema.Union(RegisterAgentSuccess, RegisterAgentFailure).annotations({
67
+ }).annotate({ identifier: "RegisterAgentFailure" });
68
+ const RegisterAgentResult = Schema.Union([RegisterAgentSuccess, RegisterAgentFailure]).annotate({
64
69
  identifier: "RegisterAgentResult",
65
70
  title: "register_agent result",
66
71
  description: "Discriminate on `ok`. The four failure codes are documented per their `code` literal."
67
72
  });
68
- const registerAgent = publicProcedure.input(Schema.standardSchemaV1(RegisterAgentInput)).mutation(async ({ ctx, input }) => {
73
+ const registerAgent = publicProcedure.input(Schema.toStandardSchemaV1(RegisterAgentInput)).mutation(async ({ ctx, input }) => {
69
74
  const expectedPrefix = `${input.hostKind ?? "claude-code"}-`;
70
75
  if (!input.agentType.startsWith(expectedPrefix)) return {
71
76
  ok: false,