@vitest-agent/mcp 1.3.6 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vitest-agent-mcp.js +2 -2
- package/index.d.ts +999 -1001
- package/index.js +1 -1
- package/layers/McpLive.js +4 -5
- package/package.json +7 -12
- package/server.js +5 -5
- package/tools/_tdd-error-envelope.js +1 -1
- package/tools/acceptance-metrics.js +23 -24
- package/tools/cache-health.js +12 -13
- package/tools/commit-changes.js +27 -22
- package/tools/configure.js +27 -31
- package/tools/coverage.js +9 -10
- package/tools/errors.js +31 -27
- package/tools/failure-signature-get.js +10 -11
- package/tools/file-coverage.js +14 -11
- package/tools/help.js +1 -1
- package/tools/history.js +29 -30
- package/tools/hypothesis.js +33 -12
- package/tools/inventory.js +45 -27
- package/tools/note.js +32 -10
- package/tools/overview.js +16 -13
- package/tools/ping.js +1 -1
- package/tools/register-agent.js +18 -13
- package/tools/run-tests.js +22 -21
- package/tools/settings-list.js +11 -12
- package/tools/status.js +14 -15
- package/tools/tdd-artifact.js +28 -13
- package/tools/tdd-behavior.js +33 -15
- package/tools/tdd-goal.js +39 -23
- package/tools/tdd-phase-transition-request.js +59 -25
- package/tools/tdd-task.js +40 -21
- package/tools/test.js +28 -18
- package/tools/trends.js +12 -13
- package/tools/triage-brief.js +4 -4
- package/tools/turn-search.js +25 -18
- package/tools/wrapup-prompt.js +18 -6
- package/tsdoc-metadata.json +1 -1
- package/utils/effect-to-zod.js +18 -5
package/tools/tdd-task.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { idempotentProcedure } from "../middleware/idempotency.js";
|
|
2
2
|
import { DataReader, DataStore, GoalDetail } from "@vitest-agent/sdk";
|
|
3
|
-
import { Effect, Match, Option,
|
|
3
|
+
import { Effect, Match, Option, Schema, SchemaGetter } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/tools/tdd-task.ts
|
|
6
6
|
/**
|
|
@@ -22,7 +22,7 @@ const TddPhaseRow = Schema.Struct({
|
|
|
22
22
|
startedAt: Schema.String,
|
|
23
23
|
endedAt: Schema.NullOr(Schema.String),
|
|
24
24
|
transitionReason: Schema.NullOr(Schema.String)
|
|
25
|
-
}).
|
|
25
|
+
}).annotate({ identifier: "TddTaskPhaseRow" });
|
|
26
26
|
const TddArtifactDetailRow = Schema.Struct({
|
|
27
27
|
id: Schema.Number,
|
|
28
28
|
phaseId: Schema.Number,
|
|
@@ -30,7 +30,7 @@ const TddArtifactDetailRow = Schema.Struct({
|
|
|
30
30
|
testCaseId: Schema.NullOr(Schema.Number),
|
|
31
31
|
testRunId: Schema.NullOr(Schema.Number),
|
|
32
32
|
recordedAt: Schema.String
|
|
33
|
-
}).
|
|
33
|
+
}).annotate({ identifier: "TddTaskArtifactRow" });
|
|
34
34
|
const TddTaskDetailSchema = Schema.Struct({
|
|
35
35
|
tddTaskId: Schema.Number,
|
|
36
36
|
sessionId: Schema.Number,
|
|
@@ -42,35 +42,39 @@ const TddTaskDetailSchema = Schema.Struct({
|
|
|
42
42
|
goals: Schema.Array(GoalDetail),
|
|
43
43
|
phases: Schema.Array(TddPhaseRow),
|
|
44
44
|
artifacts: Schema.Array(TddArtifactDetailRow)
|
|
45
|
-
}).
|
|
45
|
+
}).annotate({ identifier: "TddTaskDetailSchema" });
|
|
46
46
|
const CurrentPhaseLookup = Schema.Struct({
|
|
47
47
|
id: Schema.Number,
|
|
48
48
|
phase: Schema.String,
|
|
49
49
|
startedAt: Schema.String,
|
|
50
50
|
behaviorId: Schema.NullOr(Schema.Number)
|
|
51
|
-
}).
|
|
51
|
+
}).annotate({ identifier: "TddTaskCurrentPhaseLookup" });
|
|
52
52
|
const TddTaskStartOk = Schema.Struct({
|
|
53
53
|
action: Schema.Literal("start"),
|
|
54
54
|
tddTaskId: Schema.Number,
|
|
55
55
|
goal: Schema.String,
|
|
56
56
|
runId: Schema.optional(Schema.String)
|
|
57
|
-
}).
|
|
57
|
+
}).annotate({ identifier: "TddTaskStartOk" });
|
|
58
58
|
const TddTaskEndOk = Schema.Struct({
|
|
59
59
|
action: Schema.Literal("end"),
|
|
60
60
|
tddTaskId: Schema.Number,
|
|
61
|
-
outcome: Schema.
|
|
62
|
-
|
|
61
|
+
outcome: Schema.Literals([
|
|
62
|
+
"succeeded",
|
|
63
|
+
"blocked",
|
|
64
|
+
"abandoned"
|
|
65
|
+
])
|
|
66
|
+
}).annotate({ identifier: "TddTaskEndOk" });
|
|
63
67
|
const TddTaskGetFound = Schema.Struct({
|
|
64
68
|
action: Schema.Literal("get"),
|
|
65
69
|
found: Schema.Literal(true),
|
|
66
70
|
task: TddTaskDetailSchema,
|
|
67
71
|
currentPhase: Schema.NullOr(CurrentPhaseLookup)
|
|
68
|
-
}).
|
|
72
|
+
}).annotate({ identifier: "TddTaskGetFound" });
|
|
69
73
|
const TddTaskGetMissing = Schema.Struct({
|
|
70
74
|
action: Schema.Literal("get"),
|
|
71
75
|
found: Schema.Literal(false),
|
|
72
76
|
tddTaskId: Schema.Number
|
|
73
|
-
}).
|
|
77
|
+
}).annotate({ identifier: "TddTaskGetMissing" });
|
|
74
78
|
const TddTaskResumeFound = Schema.Struct({
|
|
75
79
|
action: Schema.Literal("resume"),
|
|
76
80
|
found: Schema.Literal(true),
|
|
@@ -80,13 +84,20 @@ const TddTaskResumeFound = Schema.Struct({
|
|
|
80
84
|
currentPhase: Schema.NullOr(CurrentPhaseLookup),
|
|
81
85
|
phasesRecorded: Schema.Number,
|
|
82
86
|
artifactsRecorded: Schema.Number
|
|
83
|
-
}).
|
|
87
|
+
}).annotate({ identifier: "TddTaskResumeFound" });
|
|
84
88
|
const TddTaskResumeMissing = Schema.Struct({
|
|
85
89
|
action: Schema.Literal("resume"),
|
|
86
90
|
found: Schema.Literal(false),
|
|
87
91
|
tddTaskId: Schema.Number
|
|
88
|
-
}).
|
|
89
|
-
const TddTaskResult = Schema.Union(
|
|
92
|
+
}).annotate({ identifier: "TddTaskResumeMissing" });
|
|
93
|
+
const TddTaskResult = Schema.Union([
|
|
94
|
+
TddTaskStartOk,
|
|
95
|
+
TddTaskEndOk,
|
|
96
|
+
TddTaskGetFound,
|
|
97
|
+
TddTaskGetMissing,
|
|
98
|
+
TddTaskResumeFound,
|
|
99
|
+
TddTaskResumeMissing
|
|
100
|
+
]).annotate({
|
|
90
101
|
identifier: "TddTaskResult",
|
|
91
102
|
title: "tdd_task result",
|
|
92
103
|
description: "Discriminate on `action`. `get` and `resume` further discriminate on `found`. `get` carries the full nested task tree."
|
|
@@ -146,11 +157,10 @@ const formatTddTaskMarkdown = (data) => {
|
|
|
146
157
|
lines.push("", `Use \`tdd_task({ action: "get", tddTaskId: ${data.tddTaskId} })\` for the full detail tree, or call \`tdd_phase_transition_request\` to advance.`);
|
|
147
158
|
return lines.join("\n");
|
|
148
159
|
};
|
|
149
|
-
const TddTaskAsMarkdown = Schema.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
160
|
+
const TddTaskAsMarkdown = TddTaskResult.pipe(Schema.decodeTo(Schema.String, {
|
|
161
|
+
decode: SchemaGetter.transform((data) => formatTddTaskMarkdown(data)),
|
|
162
|
+
encode: SchemaGetter.forbidden(() => "TddTaskAsMarkdown is one-way.")
|
|
163
|
+
}));
|
|
154
164
|
const StartVariant = Schema.Struct({
|
|
155
165
|
action: Schema.Literal("start"),
|
|
156
166
|
goal: Schema.String,
|
|
@@ -163,7 +173,11 @@ const StartVariant = Schema.Struct({
|
|
|
163
173
|
const EndVariant = Schema.Struct({
|
|
164
174
|
action: Schema.Literal("end"),
|
|
165
175
|
tddTaskId: Schema.Number,
|
|
166
|
-
outcome: Schema.
|
|
176
|
+
outcome: Schema.Literals([
|
|
177
|
+
"succeeded",
|
|
178
|
+
"blocked",
|
|
179
|
+
"abandoned"
|
|
180
|
+
]),
|
|
167
181
|
summaryNoteId: Schema.optional(Schema.Number)
|
|
168
182
|
});
|
|
169
183
|
const GetVariant = Schema.Struct({
|
|
@@ -174,8 +188,13 @@ const ResumeVariant = Schema.Struct({
|
|
|
174
188
|
action: Schema.Literal("resume"),
|
|
175
189
|
tddTaskId: Schema.Number
|
|
176
190
|
});
|
|
177
|
-
const TddTaskInput = Schema.Union(
|
|
178
|
-
|
|
191
|
+
const TddTaskInput = Schema.Union([
|
|
192
|
+
StartVariant,
|
|
193
|
+
EndVariant,
|
|
194
|
+
GetVariant,
|
|
195
|
+
ResumeVariant
|
|
196
|
+
]);
|
|
197
|
+
const tddTask = idempotentProcedure.input(Schema.toStandardSchemaV1(TddTaskInput)).mutation(async ({ ctx, input }) => {
|
|
179
198
|
return ctx.runtime.runPromise(Match.value(input).pipe(Match.discriminatorsExhaustive("action")({
|
|
180
199
|
start: (variant) => Effect.gen(function* () {
|
|
181
200
|
const reader = yield* DataReader;
|
package/tools/test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { publicProcedure } from "../context.js";
|
|
2
2
|
import { DataReader } from "@vitest-agent/sdk";
|
|
3
|
-
import { Effect, Match, Option,
|
|
3
|
+
import { Effect, Match, Option, Schema, SchemaGetter } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/tools/test.ts
|
|
6
6
|
/**
|
|
@@ -20,17 +20,17 @@ const TestRowSchema = Schema.Struct({
|
|
|
20
20
|
duration: Schema.NullOr(Schema.Number),
|
|
21
21
|
module: Schema.String,
|
|
22
22
|
classification: Schema.NullOr(Schema.String)
|
|
23
|
-
}).
|
|
23
|
+
}).annotate({ identifier: "TestListRow" });
|
|
24
24
|
const TestErrorRowMini = Schema.Struct({
|
|
25
25
|
name: Schema.NullOr(Schema.String),
|
|
26
26
|
message: Schema.String,
|
|
27
27
|
diff: Schema.NullOr(Schema.String),
|
|
28
28
|
stack: Schema.NullOr(Schema.String)
|
|
29
|
-
}).
|
|
29
|
+
}).annotate({ identifier: "TestGetErrorRow" });
|
|
30
30
|
const TestRunRow = Schema.Struct({
|
|
31
|
-
state: Schema.
|
|
31
|
+
state: Schema.Literals(["passed", "failed"]),
|
|
32
32
|
timestamp: Schema.String
|
|
33
|
-
}).
|
|
33
|
+
}).annotate({ identifier: "TestGetRunRow" });
|
|
34
34
|
const TestListGroup = Schema.Struct({
|
|
35
35
|
project: Schema.String,
|
|
36
36
|
tests: Schema.Array(TestRowSchema)
|
|
@@ -39,7 +39,7 @@ const TestListResult = Schema.Struct({
|
|
|
39
39
|
action: Schema.Literal("list"),
|
|
40
40
|
count: Schema.Number,
|
|
41
41
|
groups: Schema.Array(TestListGroup)
|
|
42
|
-
}).
|
|
42
|
+
}).annotate({ identifier: "TestList" });
|
|
43
43
|
const TestGetFound = Schema.Struct({
|
|
44
44
|
action: Schema.Literal("get"),
|
|
45
45
|
found: Schema.Literal(true),
|
|
@@ -47,26 +47,32 @@ const TestGetFound = Schema.Struct({
|
|
|
47
47
|
test: TestRowSchema,
|
|
48
48
|
errors: Schema.Array(TestErrorRowMini),
|
|
49
49
|
runs: Schema.Array(TestRunRow)
|
|
50
|
-
}).
|
|
50
|
+
}).annotate({ identifier: "TestGetFound" });
|
|
51
51
|
const TestGetMissing = Schema.Struct({
|
|
52
52
|
action: Schema.Literal("get"),
|
|
53
53
|
found: Schema.Literal(false),
|
|
54
54
|
project: Schema.String,
|
|
55
55
|
fullName: Schema.String
|
|
56
|
-
}).
|
|
56
|
+
}).annotate({ identifier: "TestGetMissing" });
|
|
57
57
|
const TestForFileResult = Schema.Struct({
|
|
58
58
|
action: Schema.Literal("for_file"),
|
|
59
59
|
filePath: Schema.String,
|
|
60
60
|
count: Schema.Number,
|
|
61
61
|
testFiles: Schema.Array(Schema.String)
|
|
62
|
-
}).
|
|
62
|
+
}).annotate({ identifier: "TestForFile" });
|
|
63
63
|
const TestForTagResult = Schema.Struct({
|
|
64
64
|
action: Schema.Literal("for_tag"),
|
|
65
65
|
tag: Schema.String,
|
|
66
66
|
count: Schema.Number,
|
|
67
67
|
groups: Schema.Array(TestListGroup)
|
|
68
|
-
}).
|
|
69
|
-
const TestResult = Schema.Union(
|
|
68
|
+
}).annotate({ identifier: "TestForTag" });
|
|
69
|
+
const TestResult = Schema.Union([
|
|
70
|
+
TestListResult,
|
|
71
|
+
TestGetFound,
|
|
72
|
+
TestGetMissing,
|
|
73
|
+
TestForFileResult,
|
|
74
|
+
TestForTagResult
|
|
75
|
+
]).annotate({
|
|
70
76
|
identifier: "TestResult",
|
|
71
77
|
title: "test result",
|
|
72
78
|
description: "Discriminate on `action`. `get` further discriminates on `found`. `list`, `for_file`, and `for_tag` all carry counted arrays — `list` and `for_tag` group by project."
|
|
@@ -160,11 +166,10 @@ const formatTestMarkdown = (data) => {
|
|
|
160
166
|
for (const f of data.testFiles) lines.push(`- \`${f}\``);
|
|
161
167
|
return lines.join("\n");
|
|
162
168
|
};
|
|
163
|
-
const TestAsMarkdown = Schema.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
});
|
|
169
|
+
const TestAsMarkdown = TestResult.pipe(Schema.decodeTo(Schema.String, {
|
|
170
|
+
decode: SchemaGetter.transform((data) => formatTestMarkdown(data)),
|
|
171
|
+
encode: SchemaGetter.forbidden(() => "TestAsMarkdown is one-way.")
|
|
172
|
+
}));
|
|
168
173
|
const ListVariant = Schema.Struct({
|
|
169
174
|
action: Schema.Literal("list"),
|
|
170
175
|
project: Schema.optional(Schema.String),
|
|
@@ -186,8 +191,13 @@ const ForTagVariant = Schema.Struct({
|
|
|
186
191
|
tag: Schema.String,
|
|
187
192
|
project: Schema.optional(Schema.String)
|
|
188
193
|
});
|
|
189
|
-
const TestInput = Schema.Union(
|
|
190
|
-
|
|
194
|
+
const TestInput = Schema.Union([
|
|
195
|
+
ListVariant,
|
|
196
|
+
GetVariant,
|
|
197
|
+
ForFileVariant,
|
|
198
|
+
ForTagVariant
|
|
199
|
+
]);
|
|
200
|
+
const test = publicProcedure.input(Schema.toStandardSchemaV1(TestInput)).query(async ({ ctx, input }) => {
|
|
191
201
|
return ctx.runtime.runPromise(Match.value(input).pipe(Match.discriminatorsExhaustive("action")({
|
|
192
202
|
list: (variant) => Effect.gen(function* () {
|
|
193
203
|
const reader = yield* DataReader;
|
package/tools/trends.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { publicProcedure } from "../context.js";
|
|
2
2
|
import { DataReader, TrendRecord } from "@vitest-agent/sdk";
|
|
3
|
-
import { Effect, Option,
|
|
3
|
+
import { Effect, Option, Schema, SchemaGetter } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/tools/trends.ts
|
|
6
6
|
/**
|
|
@@ -14,15 +14,15 @@ import { Effect, Option, ParseResult, Schema } from "effect";
|
|
|
14
14
|
* @packageDocumentation
|
|
15
15
|
*/
|
|
16
16
|
const TrendsAvailable = Schema.Struct({
|
|
17
|
-
dataAvailable: Schema.Literal(true).
|
|
17
|
+
dataAvailable: Schema.Literal(true).annotate({ description: "Discriminant — `true` when at least one trend entry exists for the project." }),
|
|
18
18
|
project: Schema.String,
|
|
19
|
-
trends: TrendRecord.
|
|
20
|
-
}).
|
|
19
|
+
trends: TrendRecord.annotate({ description: "Trend entries oldest-first; the latest entry drives `direction` and the headline metrics." })
|
|
20
|
+
}).annotate({ identifier: "TestTrendsAvailable" });
|
|
21
21
|
const TrendsAbsent = Schema.Struct({
|
|
22
|
-
dataAvailable: Schema.Literal(false).
|
|
22
|
+
dataAvailable: Schema.Literal(false).annotate({ description: "Discriminant — `false` when fewer than two runs have been recorded for the project." }),
|
|
23
23
|
project: Schema.String
|
|
24
|
-
}).
|
|
25
|
-
const TestTrendsResult = Schema.Union(TrendsAvailable, TrendsAbsent).
|
|
24
|
+
}).annotate({ identifier: "TestTrendsAbsent" });
|
|
25
|
+
const TestTrendsResult = Schema.Union([TrendsAvailable, TrendsAbsent]).annotate({
|
|
26
26
|
identifier: "TestTrendsResult",
|
|
27
27
|
title: "test_trends result",
|
|
28
28
|
description: "Coverage trend record per project. Discriminate on `dataAvailable` to handle the cold-start case."
|
|
@@ -88,12 +88,11 @@ const formatTestTrendsMarkdown = (data) => {
|
|
|
88
88
|
}
|
|
89
89
|
return lines.join("\n");
|
|
90
90
|
};
|
|
91
|
-
const TestTrendsAsMarkdown = Schema.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const testTrends = publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({
|
|
91
|
+
const TestTrendsAsMarkdown = TestTrendsResult.pipe(Schema.decodeTo(Schema.String, {
|
|
92
|
+
decode: SchemaGetter.transform((data) => formatTestTrendsMarkdown(data)),
|
|
93
|
+
encode: SchemaGetter.forbidden(() => "TestTrendsAsMarkdown is one-way: markdown cannot be parsed back to TestTrendsResult.")
|
|
94
|
+
}));
|
|
95
|
+
const testTrends = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({
|
|
97
96
|
project: Schema.String,
|
|
98
97
|
limit: Schema.optional(Schema.Number)
|
|
99
98
|
}))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
|
package/tools/triage-brief.js
CHANGED
|
@@ -14,14 +14,14 @@ import { Effect, Schema } from "effect";
|
|
|
14
14
|
* @packageDocumentation
|
|
15
15
|
*/
|
|
16
16
|
const TriageBriefResult = Schema.Struct({
|
|
17
|
-
hasContent: Schema.Boolean.
|
|
18
|
-
markdown: Schema.String.
|
|
19
|
-
}).
|
|
17
|
+
hasContent: Schema.Boolean.annotate({ description: "`false` when no orientation signal is available yet (run tests to populate)." }),
|
|
18
|
+
markdown: Schema.String.annotate({ description: "Pre-rendered markdown brief or the empty-state message." })
|
|
19
|
+
}).annotate({
|
|
20
20
|
identifier: "TriageBriefResult",
|
|
21
21
|
title: "triage_brief result",
|
|
22
22
|
description: "Orientation triage envelope. Branch on `hasContent` for cold-start; consume `markdown` for rendering."
|
|
23
23
|
});
|
|
24
|
-
const triageBrief = publicProcedure.input(Schema.
|
|
24
|
+
const triageBrief = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({
|
|
25
25
|
project: Schema.optional(Schema.String),
|
|
26
26
|
maxLines: Schema.optional(Schema.Number)
|
|
27
27
|
}))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
|
package/tools/turn-search.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { publicProcedure } from "../context.js";
|
|
2
2
|
import { DataReader } from "@vitest-agent/sdk";
|
|
3
|
-
import { Effect,
|
|
3
|
+
import { Effect, Schema, SchemaGetter } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/tools/turn-search.ts
|
|
6
6
|
/**
|
|
@@ -9,20 +9,20 @@ import { Effect, ParseResult, Schema } from "effect";
|
|
|
9
9
|
* @packageDocumentation
|
|
10
10
|
*/
|
|
11
11
|
const TurnRow = Schema.Struct({
|
|
12
|
-
id: Schema.Number.
|
|
13
|
-
sessionId: Schema.Number.
|
|
14
|
-
turnNo: Schema.Number.
|
|
15
|
-
type: Schema.String.
|
|
16
|
-
payload: Schema.String.
|
|
17
|
-
occurredAt: Schema.String.
|
|
18
|
-
}).
|
|
12
|
+
id: Schema.Number.annotate({ description: "Numeric primary key of this turn row." }),
|
|
13
|
+
sessionId: Schema.Number.annotate({ description: "Owning `sessions.id` (integer FK)." }),
|
|
14
|
+
turnNo: Schema.Number.annotate({ description: "Turn ordinal within the session (1-based)." }),
|
|
15
|
+
type: Schema.String.annotate({ description: "Turn category (`user_prompt`, `tool_call`, `tool_result`, `file_edit`, `hook_fire`, `note`, `hypothesis`)." }),
|
|
16
|
+
payload: Schema.String.annotate({ description: "Type-specific payload as a JSON-encoded string. Decode shape depends on `type`." }),
|
|
17
|
+
occurredAt: Schema.String.annotate({ description: "ISO-8601 timestamp the turn was recorded at." })
|
|
18
|
+
}).annotate({
|
|
19
19
|
identifier: "TurnRow",
|
|
20
20
|
description: "One row from the turns log."
|
|
21
21
|
});
|
|
22
22
|
const TurnSearchResult = Schema.Struct({
|
|
23
|
-
count: Schema.Number.
|
|
24
|
-
turns: Schema.Array(TurnRow).
|
|
25
|
-
}).
|
|
23
|
+
count: Schema.Number.annotate({ description: "Number of matching turn rows returned." }),
|
|
24
|
+
turns: Schema.Array(TurnRow).annotate({ description: "Matching turns ordered by `occurredAt` ascending." })
|
|
25
|
+
}).annotate({
|
|
26
26
|
identifier: "TurnSearchResult",
|
|
27
27
|
title: "turn_search result",
|
|
28
28
|
description: "Turn-log search results across all sessions, optionally filtered by session, time, type."
|
|
@@ -33,15 +33,22 @@ const formatTurnSearchMarkdown = (data) => {
|
|
|
33
33
|
for (const t of data.turns) lines.push(`- session=${t.sessionId} turn=${t.turnNo} type=${t.type} at=${t.occurredAt}`);
|
|
34
34
|
return lines.join("\n");
|
|
35
35
|
};
|
|
36
|
-
const TurnSearchAsMarkdown = Schema.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const turnSearch = publicProcedure.input(Schema.standardSchemaV1(Schema.Struct({
|
|
36
|
+
const TurnSearchAsMarkdown = TurnSearchResult.pipe(Schema.decodeTo(Schema.String, {
|
|
37
|
+
decode: SchemaGetter.transform((data) => formatTurnSearchMarkdown(data)),
|
|
38
|
+
encode: SchemaGetter.forbidden(() => "TurnSearchAsMarkdown is one-way: markdown cannot be parsed back to TurnSearchResult.")
|
|
39
|
+
}));
|
|
40
|
+
const turnSearch = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({
|
|
42
41
|
sessionId: Schema.optional(Schema.Number),
|
|
43
42
|
since: Schema.optional(Schema.String),
|
|
44
|
-
type: Schema.optional(Schema.
|
|
43
|
+
type: Schema.optional(Schema.Literals([
|
|
44
|
+
"user_prompt",
|
|
45
|
+
"tool_call",
|
|
46
|
+
"tool_result",
|
|
47
|
+
"file_edit",
|
|
48
|
+
"hook_fire",
|
|
49
|
+
"note",
|
|
50
|
+
"hypothesis"
|
|
51
|
+
])),
|
|
45
52
|
limit: Schema.optional(Schema.Number)
|
|
46
53
|
}))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
|
|
47
54
|
const rows = yield* (yield* DataReader).searchTurns({
|
package/tools/wrapup-prompt.js
CHANGED
|
@@ -13,18 +13,30 @@ import { Effect, Schema } from "effect";
|
|
|
13
13
|
* @packageDocumentation
|
|
14
14
|
*/
|
|
15
15
|
const WrapupPromptResult = Schema.Struct({
|
|
16
|
-
hasContent: Schema.Boolean.
|
|
17
|
-
kind: Schema.
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
hasContent: Schema.Boolean.annotate({ description: "`false` when there is nothing to wrap up for the named session/kind." }),
|
|
17
|
+
kind: Schema.Literals([
|
|
18
|
+
"stop",
|
|
19
|
+
"session_end",
|
|
20
|
+
"pre_compact",
|
|
21
|
+
"tdd_handoff",
|
|
22
|
+
"user_prompt_nudge"
|
|
23
|
+
]).annotate({ description: "Echo of the wrap-up kind that was rendered (defaulted to `session_end` when omitted)." }),
|
|
24
|
+
markdown: Schema.String.annotate({ description: "Pre-rendered wrap-up markdown or the empty-state message." })
|
|
25
|
+
}).annotate({
|
|
20
26
|
identifier: "WrapupPromptResult",
|
|
21
27
|
title: "wrapup_prompt result",
|
|
22
28
|
description: "Wrap-up envelope. Branch on `hasContent` for the empty case; consume `markdown` for rendering."
|
|
23
29
|
});
|
|
24
|
-
const wrapupPrompt = publicProcedure.input(Schema.
|
|
30
|
+
const wrapupPrompt = publicProcedure.input(Schema.toStandardSchemaV1(Schema.Struct({
|
|
25
31
|
sessionId: Schema.optional(Schema.Number),
|
|
26
32
|
chatId: Schema.optional(Schema.String),
|
|
27
|
-
kind: Schema.optional(Schema.
|
|
33
|
+
kind: Schema.optional(Schema.Literals([
|
|
34
|
+
"stop",
|
|
35
|
+
"session_end",
|
|
36
|
+
"pre_compact",
|
|
37
|
+
"tdd_handoff",
|
|
38
|
+
"user_prompt_nudge"
|
|
39
|
+
])),
|
|
28
40
|
userPromptHint: Schema.optional(Schema.String)
|
|
29
41
|
}))).query(async ({ ctx, input }) => ctx.runtime.runPromise(Effect.gen(function* () {
|
|
30
42
|
const kind = input.kind ?? "session_end";
|
package/tsdoc-metadata.json
CHANGED
package/utils/effect-to-zod.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Schema } from "effect";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/effect-to-zod.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Bridge an Effect Schema to a zod schema by routing through JSON
|
|
7
|
+
* Schema. Used at the MCP `registerTool` boundary so a tool can keep
|
|
8
|
+
* Effect Schema as the canonical source of truth for its output shape
|
|
9
|
+
* while the SDK still receives the zod instance it expects in the
|
|
10
|
+
* `outputSchema` field.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Convert an Effect `Schema.Codec<A, I>` to a zod schema by
|
|
16
|
+
* serializing it to JSON Schema (`Schema.toJsonSchemaDocument`) and ingesting the
|
|
8
17
|
* result via zod 4's `z.fromJSONSchema`.
|
|
9
18
|
*
|
|
10
19
|
* Trade-offs:
|
|
@@ -22,7 +31,7 @@ import { z } from "zod";
|
|
|
22
31
|
*
|
|
23
32
|
* Implementation note: zod 4's `z.fromJSONSchema` does not resolve
|
|
24
33
|
* `$ref` lookups into `$defs` — every `{ $ref: "#/$defs/X" }` it
|
|
25
|
-
* encounters throws "Reference not found". Effect's `
|
|
34
|
+
* encounters throws "Reference not found". Effect's `Schema.toJsonSchemaDocument`
|
|
26
35
|
* emits a `$ref`-and-`$defs` representation whenever a Schema carries
|
|
27
36
|
* an `identifier` annotation. The bridge therefore inlines every
|
|
28
37
|
* `$ref` in the document before handing it to zod (recursive
|
|
@@ -42,7 +51,11 @@ import { z } from "zod";
|
|
|
42
51
|
* if the rich listing matters.
|
|
43
52
|
*/
|
|
44
53
|
const effectToZodSchema = (schema) => {
|
|
45
|
-
const
|
|
54
|
+
const document = Schema.toJsonSchemaDocument(schema);
|
|
55
|
+
const jsonSchema = {
|
|
56
|
+
...document.schema,
|
|
57
|
+
$defs: document.definitions
|
|
58
|
+
};
|
|
46
59
|
const inlined = inlineAllRefs(jsonSchema);
|
|
47
60
|
const zodSchema = z.fromJSONSchema(inlined);
|
|
48
61
|
if (isObjectLike(zodSchema)) return zodSchema;
|