@vitest-agent/cli 1.0.7 → 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.
- package/bin/vitest-agent.js +6 -8
- package/commands/agent.js +29 -28
- package/commands/db.js +21 -20
- package/commands/doctor.js +5 -5
- package/commands/record.js +39 -48
- package/commands/triage.js +4 -4
- package/commands/wrapup.js +6 -6
- package/index.d.ts +5 -5
- package/index.js +1 -1
- package/layers/CliLive.js +5 -6
- package/layers/SidecarLive.js +3 -4
- package/lib/record-turn.js +4 -4
- package/lib/record-workspace-changes.js +1 -15
- package/package.json +8 -17
- package/tsdoc-metadata.json +1 -1
package/bin/vitest-agent.js
CHANGED
|
@@ -3,10 +3,11 @@ import { CliLive } from "../layers/CliLive.js";
|
|
|
3
3
|
import { agentCommand } from "../commands/agent.js";
|
|
4
4
|
import { dbCommand } from "../commands/db.js";
|
|
5
5
|
import { doctorCommand } from "../commands/doctor.js";
|
|
6
|
-
import
|
|
6
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
7
7
|
import { PathResolutionLive, formatFatalError, resolveDataPath, resolveLogFile, resolveLogLevel } from "@vitest-agent/sdk";
|
|
8
8
|
import { Cause, Console, Effect } from "effect";
|
|
9
|
-
import
|
|
9
|
+
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
|
|
10
|
+
import { Command } from "effect/unstable/cli";
|
|
10
11
|
|
|
11
12
|
//#region src/bin.ts
|
|
12
13
|
/**
|
|
@@ -19,15 +20,12 @@ const rootCommand = Command.make("vitest-agent").pipe(Command.withSubcommands([
|
|
|
19
20
|
doctorCommand,
|
|
20
21
|
agentCommand
|
|
21
22
|
]));
|
|
22
|
-
const cli = Command.run(rootCommand, {
|
|
23
|
-
name: "vitest-agent",
|
|
24
|
-
version: "0.0.0"
|
|
25
|
-
});
|
|
23
|
+
const cli = Command.run(rootCommand, { version: "0.0.0" });
|
|
26
24
|
const logLevel = resolveLogLevel();
|
|
27
25
|
const logFile = resolveLogFile();
|
|
28
26
|
const projectDir = process.env.VITEST_AGENT_PROJECT_DIR ?? process.cwd();
|
|
29
|
-
const main = resolveDataPath(projectDir).pipe(Effect.flatMap((dbPath) =>
|
|
30
|
-
if (Cause.
|
|
27
|
+
const main = resolveDataPath(projectDir).pipe(Effect.flatMap((dbPath) => cli.pipe(Effect.provide(CliLive(dbPath, logLevel, logFile)))), Effect.provide(PathResolutionLive(projectDir)), Effect.provide(NodeServices.layer), Effect.catchCause((cause) => {
|
|
28
|
+
if (cause.reasons.filter(Cause.isDieReason).length > 0) return Console.error(`vitest-agent: ${formatFatalError(cause)}`).pipe(Effect.andThen(Effect.failCause(cause)));
|
|
31
29
|
return Effect.failCause(cause);
|
|
32
30
|
}));
|
|
33
31
|
NodeRuntime.runMain(main);
|
package/commands/agent.js
CHANGED
|
@@ -5,11 +5,11 @@ import { endAgentEffect } from "../lib/internal-end-agent.js";
|
|
|
5
5
|
import { recordCommand } from "./record.js";
|
|
6
6
|
import { triageCommand } from "./triage.js";
|
|
7
7
|
import { wrapupCommand } from "./wrapup.js";
|
|
8
|
-
import
|
|
8
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
9
9
|
import { resolveProjectKeyFromCwd } from "@vitest-agent/sdk";
|
|
10
|
-
import { Cause,
|
|
10
|
+
import { Cause, Effect, Option } from "effect";
|
|
11
11
|
import { join } from "node:path";
|
|
12
|
-
import { Command,
|
|
12
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
13
13
|
import { exitCodeForTag, injectEnv } from "@vitest-agent/sdk/dispatch";
|
|
14
14
|
import { resolveSidecarBinaryPath } from "@vitest-agent/sidecar";
|
|
15
15
|
|
|
@@ -46,26 +46,27 @@ const writeStderrAndExit = (exitCode, tag, message) => Effect.sync(() => {
|
|
|
46
46
|
process.exit(exitCode);
|
|
47
47
|
});
|
|
48
48
|
const mapDefectToExit = (cause) => {
|
|
49
|
-
const failures =
|
|
49
|
+
const failures = cause.reasons.filter(Cause.isFailReason);
|
|
50
50
|
if (failures.length > 0) {
|
|
51
|
-
const
|
|
51
|
+
const error = failures[0].error;
|
|
52
|
+
const tagged = error;
|
|
52
53
|
const tag = tagged._tag ?? "UnknownError";
|
|
53
|
-
const message = tagged.reason ?? tagged.message ?? String(
|
|
54
|
+
const message = tagged.reason ?? tagged.message ?? String(error);
|
|
54
55
|
const exitCode = exitCodeForTag(tag);
|
|
55
56
|
return writeStderrAndExit(exitCode, tag, message);
|
|
56
57
|
}
|
|
57
|
-
const defect =
|
|
58
|
+
const defect = cause.reasons.filter(Cause.isDieReason)[0]?.defect;
|
|
58
59
|
const message = defect instanceof Error ? defect.message : String(defect ?? "unknown defect");
|
|
59
60
|
return writeStderrAndExit(5, "Defect", message);
|
|
60
61
|
};
|
|
61
|
-
const hostKindOpt =
|
|
62
|
-
const agentTypeOpt =
|
|
63
|
-
const hostSessionIdOpt =
|
|
64
|
-
const transcriptPathOpt =
|
|
65
|
-
const cwdOpt =
|
|
66
|
-
const parentAgentIdOpt =
|
|
67
|
-
const clientNonceOpt =
|
|
68
|
-
const projectKeyOverrideOpt =
|
|
62
|
+
const hostKindOpt = Flag.string("host-kind").pipe(Flag.withDescription("Host vendor identifier; e.g. 'claude-code', 'cursor', 'goose'"));
|
|
63
|
+
const agentTypeOpt = Flag.string("agent-type").pipe(Flag.withDescription("Agent type, must begin with the host-kind prefix"));
|
|
64
|
+
const hostSessionIdOpt = Flag.string("host-session-id").pipe(Flag.withDescription("Host's native session id (host chat UUID; `session_id` in the CC hook payload)"));
|
|
65
|
+
const transcriptPathOpt = Flag.string("transcript-path").pipe(Flag.withDescription("Path to the host's transcript file (basename UUID is the conversation key)"));
|
|
66
|
+
const cwdOpt = Flag.string("cwd").pipe(Flag.withDescription("Workspace root directory the agent is running in"));
|
|
67
|
+
const parentAgentIdOpt = Flag.optional(Flag.string("parent-agent-id"));
|
|
68
|
+
const clientNonceOpt = Flag.optional(Flag.string("client-nonce"));
|
|
69
|
+
const projectKeyOverrideOpt = Flag.optional(Flag.string("project-key"));
|
|
69
70
|
const registerAgentSubcommand = Command.make("register-agent", {
|
|
70
71
|
hostKind: hostKindOpt,
|
|
71
72
|
agentType: agentTypeOpt,
|
|
@@ -81,7 +82,7 @@ const registerAgentSubcommand = Command.make("register-agent", {
|
|
|
81
82
|
const registryDbPath = join(resolveRegistryDir(), REGISTRY_DB_FILENAME);
|
|
82
83
|
const sidecar = SidecarLive({
|
|
83
84
|
perProjectDbPath,
|
|
84
|
-
sessionMapDbPath: yield* resolveSessionMapPath().pipe(Effect.
|
|
85
|
+
sessionMapDbPath: yield* resolveSessionMapPath().pipe(Effect.catchCause(mapDefectToExit)),
|
|
85
86
|
registryDbPath
|
|
86
87
|
});
|
|
87
88
|
const result = yield* registerAgentEffect({
|
|
@@ -93,7 +94,7 @@ const registerAgentSubcommand = Command.make("register-agent", {
|
|
|
93
94
|
projectKey,
|
|
94
95
|
...Option.isSome(opts.parentAgentId) && { parentAgentId: opts.parentAgentId.value },
|
|
95
96
|
...Option.isSome(opts.clientNonce) && { clientNonce: opts.clientNonce.value }
|
|
96
|
-
}).pipe(Effect.provide(sidecar), Effect.
|
|
97
|
+
}).pipe(Effect.provide(sidecar), Effect.catchCause(mapDefectToExit));
|
|
97
98
|
yield* writeStdout(JSON.stringify({
|
|
98
99
|
agentId: result.agentId,
|
|
99
100
|
conversationId: result.conversationId,
|
|
@@ -101,12 +102,12 @@ const registerAgentSubcommand = Command.make("register-agent", {
|
|
|
101
102
|
idempotencyKey: result.idempotencyKey,
|
|
102
103
|
idempotencyHit: result.idempotencyHit
|
|
103
104
|
}));
|
|
104
|
-
}).pipe(Effect.provide(
|
|
105
|
-
const agentIdOpt =
|
|
106
|
-
const endedAtOpt =
|
|
107
|
-
const endHostSessionIdOpt =
|
|
108
|
-
const endCwdOpt =
|
|
109
|
-
const endProjectKeyOverrideOpt =
|
|
105
|
+
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Register an agent invocation in the per-project store and the per-client session map"));
|
|
106
|
+
const agentIdOpt = Flag.string("agent-id").pipe(Flag.withDescription("The agent_id (UUID) returned by an earlier register-agent call"));
|
|
107
|
+
const endedAtOpt = Flag.optional(Flag.integer("ended-at"));
|
|
108
|
+
const endHostSessionIdOpt = Flag.optional(Flag.string("host-session-id"));
|
|
109
|
+
const endCwdOpt = Flag.string("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Workspace root, used to locate the per-project data.db"));
|
|
110
|
+
const endProjectKeyOverrideOpt = Flag.optional(Flag.string("project-key"));
|
|
110
111
|
const endAgentSubcommand = Command.make("end-agent", {
|
|
111
112
|
agentId: agentIdOpt,
|
|
112
113
|
endedAt: endedAtOpt,
|
|
@@ -118,7 +119,7 @@ const endAgentSubcommand = Command.make("end-agent", {
|
|
|
118
119
|
const registryDbPath = join(resolveRegistryDir(), REGISTRY_DB_FILENAME);
|
|
119
120
|
const sidecar = SidecarLive({
|
|
120
121
|
perProjectDbPath,
|
|
121
|
-
sessionMapDbPath: yield* resolveSessionMapPath().pipe(Effect.
|
|
122
|
+
sessionMapDbPath: yield* resolveSessionMapPath().pipe(Effect.catchCause(mapDefectToExit)),
|
|
122
123
|
registryDbPath
|
|
123
124
|
});
|
|
124
125
|
const endedAt = Option.isSome(opts.endedAt) ? opts.endedAt.value : Math.floor(Date.now() / 1e3);
|
|
@@ -126,10 +127,10 @@ const endAgentSubcommand = Command.make("end-agent", {
|
|
|
126
127
|
agentId: opts.agentId,
|
|
127
128
|
endedAt,
|
|
128
129
|
...Option.isSome(opts.hostSessionId) && { hostSessionId: opts.hostSessionId.value }
|
|
129
|
-
}).pipe(Effect.provide(sidecar), Effect.
|
|
130
|
-
}).pipe(Effect.provide(
|
|
131
|
-
const commandOpt =
|
|
132
|
-
const cwdInjectOpt =
|
|
130
|
+
}).pipe(Effect.provide(sidecar), Effect.catchCause(mapDefectToExit));
|
|
131
|
+
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Mark an agent (and optionally its session) as ended"));
|
|
132
|
+
const commandOpt = Flag.string("command").pipe(Flag.withDescription("The Bash command to (possibly) rewrite with VITEST_AGENT_* env-prefix"));
|
|
133
|
+
const cwdInjectOpt = Flag.string("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Working directory; used to find package.json scripts"));
|
|
133
134
|
const injectEnvSubcommand = Command.make("inject-env", {
|
|
134
135
|
command: commandOpt,
|
|
135
136
|
cwd: cwdInjectOpt
|
package/commands/db.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { formatDbQuery } from "../lib/format-db-query.js";
|
|
2
|
-
import
|
|
2
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
3
3
|
import { layer } from "@effect/sql-sqlite-node/SqliteClient";
|
|
4
4
|
import { DataStore, resolveDataPath } from "@vitest-agent/sdk";
|
|
5
|
-
import { Effect } from "effect";
|
|
6
|
-
import {
|
|
7
|
-
import { SqlClient } from "
|
|
5
|
+
import { Effect, FileSystem } from "effect";
|
|
6
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
7
|
+
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
8
8
|
import * as readline from "node:readline";
|
|
9
|
-
import { FileSystem } from "@effect/platform";
|
|
10
9
|
|
|
11
10
|
//#region src/commands/db.ts
|
|
12
11
|
/**
|
|
@@ -18,12 +17,12 @@ const pathCommand = Command.make("path", {}, () => Effect.gen(function* () {
|
|
|
18
17
|
const dbPath = yield* resolveDataPath(process.env.VITEST_AGENT_PROJECT_DIR ?? process.cwd());
|
|
19
18
|
yield* Effect.sync(() => process.stdout.write(`${dbPath}\n`));
|
|
20
19
|
})).pipe(Command.withDescription("Print the resolved database path"));
|
|
21
|
-
const keepRecentOption =
|
|
20
|
+
const keepRecentOption = Flag.withDefault(Flag.integer("keep-recent"), 30).pipe(Flag.withDescription("Number of most-recent sessions to keep in full"));
|
|
22
21
|
const pruneCommand = Command.make("prune", { keepRecent: keepRecentOption }, ({ keepRecent }) => Effect.gen(function* () {
|
|
23
22
|
const result = yield* (yield* DataStore).pruneSessions(keepRecent);
|
|
24
23
|
yield* Effect.sync(() => process.stdout.write(`Pruned ${result.prunedTurns} turn row(s) across ${result.affectedSessions} session(s); session rows retained.\n`));
|
|
25
24
|
})).pipe(Command.withDescription("Drop old sessions' turn history (W1 retention; keeps the last N in full)"));
|
|
26
|
-
const yesOption =
|
|
25
|
+
const yesOption = Flag.boolean("yes").pipe(Flag.withDefault(false), Flag.withDescription("Skip the interactive confirmation prompt"));
|
|
27
26
|
const resetCommand = Command.make("reset", { yes: yesOption }, ({ yes }) => Effect.gen(function* () {
|
|
28
27
|
const agentId = process.env.VITEST_AGENT_AGENT_ID;
|
|
29
28
|
if (agentId !== void 0 && agentId.length > 0) {
|
|
@@ -62,15 +61,15 @@ const resetCommand = Command.make("reset", { yes: yesOption }, ({ yes }) => Effe
|
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
const fs = yield* FileSystem.FileSystem;
|
|
65
|
-
yield* fs.remove(dbPath).pipe(Effect.
|
|
66
|
-
yield* fs.remove(`${dbPath}-shm`).pipe(Effect.
|
|
67
|
-
yield* fs.remove(`${dbPath}-wal`).pipe(Effect.
|
|
64
|
+
yield* fs.remove(dbPath).pipe(Effect.catch(() => Effect.void));
|
|
65
|
+
yield* fs.remove(`${dbPath}-shm`).pipe(Effect.catch(() => Effect.void));
|
|
66
|
+
yield* fs.remove(`${dbPath}-wal`).pipe(Effect.catch(() => Effect.void));
|
|
68
67
|
yield* Effect.sync(() => {
|
|
69
68
|
process.stdout.write(`Deleted database at ${dbPath}\n`);
|
|
70
69
|
});
|
|
71
|
-
}).pipe(Effect.provide(
|
|
72
|
-
const queryFormatOption =
|
|
73
|
-
const sqlArg =
|
|
70
|
+
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Wipe the database (human-only; blocked in agent contexts)"));
|
|
71
|
+
const queryFormatOption = Flag.choice("format", ["table", "json"]).pipe(Flag.withDefault("table"), Flag.withDescription("Output format for query results"));
|
|
72
|
+
const sqlArg = Argument.string("sql").pipe(Argument.withDescription("Read-only SQL statement to execute against data.db"));
|
|
74
73
|
/**
|
|
75
74
|
* Flatten an error and its cause chain into a single message so the
|
|
76
75
|
* driver's `attempt to write a readonly database` text surfaces to
|
|
@@ -78,13 +77,15 @@ const sqlArg = Args.text({ name: "sql" }).pipe(Args.withDescription("Read-only S
|
|
|
78
77
|
*/
|
|
79
78
|
const describeError = (error) => {
|
|
80
79
|
if (!(error instanceof Error)) return String(error);
|
|
81
|
-
const parts = [
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const parts = [];
|
|
81
|
+
const seen = /* @__PURE__ */ new Set();
|
|
82
|
+
let current = error;
|
|
83
|
+
while (current instanceof Error && !seen.has(current)) {
|
|
84
|
+
seen.add(current);
|
|
85
|
+
if (current.message.length > 0 && !parts.includes(current.message)) parts.push(current.message);
|
|
86
|
+
current = current.cause;
|
|
86
87
|
}
|
|
87
|
-
return parts.
|
|
88
|
+
return parts.join(": ");
|
|
88
89
|
};
|
|
89
90
|
const queryCommand = Command.make("query", {
|
|
90
91
|
sql: sqlArg,
|
|
@@ -106,7 +107,7 @@ const queryCommand = Command.make("query", {
|
|
|
106
107
|
}).pipe(Effect.provide(layer({
|
|
107
108
|
filename: dbPath,
|
|
108
109
|
readonly: true
|
|
109
|
-
})), Effect.
|
|
110
|
+
})), Effect.catch((error) => Effect.sync(() => {
|
|
110
111
|
process.stderr.write(`db query: ${describeError(error)}\n`);
|
|
111
112
|
process.exit(3);
|
|
112
113
|
})));
|
package/commands/doctor.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatDoctor } from "../lib/format-doctor.js";
|
|
2
2
|
import { DataReader, resolveDataPath } from "@vitest-agent/sdk";
|
|
3
3
|
import { Effect, Option } from "effect";
|
|
4
|
-
import { Command,
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
|
|
6
6
|
//#region src/commands/doctor.ts
|
|
7
7
|
/**
|
|
@@ -9,7 +9,7 @@ import { Command, Options } from "@effect/cli";
|
|
|
9
9
|
*
|
|
10
10
|
* @packageDocumentation
|
|
11
11
|
*/
|
|
12
|
-
const formatOption =
|
|
12
|
+
const formatOption = Flag.withDefault(Flag.choice("format", ["markdown", "json"]), "markdown");
|
|
13
13
|
const writeOutput = (results, format) => Effect.sync(() => {
|
|
14
14
|
if (format === "json") process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
|
|
15
15
|
else process.stdout.write(`${formatDoctor(results)}\n`);
|
|
@@ -23,7 +23,7 @@ const doctorCommand = Command.make("doctor", { format: formatOption }, ({ format
|
|
|
23
23
|
passed: true,
|
|
24
24
|
detail: `\`${dbPath}\``
|
|
25
25
|
});
|
|
26
|
-
const manifestOpt = yield* reader.getManifest().pipe(Effect.
|
|
26
|
+
const manifestOpt = yield* reader.getManifest().pipe(Effect.catch(() => Effect.succeed(Option.none())));
|
|
27
27
|
if (Option.isNone(manifestOpt)) {
|
|
28
28
|
results.push({
|
|
29
29
|
name: "Manifest",
|
|
@@ -44,7 +44,7 @@ const doctorCommand = Command.make("doctor", { format: formatOption }, ({ format
|
|
|
44
44
|
const reportIssues = [];
|
|
45
45
|
for (const entry of manifest.projects) {
|
|
46
46
|
const project = entry.project;
|
|
47
|
-
const reportOpt = yield* reader.getLatestRun(project).pipe(Effect.
|
|
47
|
+
const reportOpt = yield* reader.getLatestRun(project).pipe(Effect.catch(() => Effect.succeed(Option.none())));
|
|
48
48
|
if (Option.isNone(reportOpt)) reportIssues.push(`\`${entry.project}\` no report data`);
|
|
49
49
|
else validReports++;
|
|
50
50
|
}
|
|
@@ -68,7 +68,7 @@ const doctorCommand = Command.make("doctor", { format: formatOption }, ({ format
|
|
|
68
68
|
if (!(yield* reader.getHistory(project).pipe(Effect.map((h) => ({
|
|
69
69
|
ok: true,
|
|
70
70
|
record: h
|
|
71
|
-
})), Effect.
|
|
71
|
+
})), Effect.catch(() => Effect.succeed({
|
|
72
72
|
ok: false,
|
|
73
73
|
record: null
|
|
74
74
|
})))).ok) historyIssues.push(`\`${entry.project}\` history read error`);
|
package/commands/record.js
CHANGED
|
@@ -4,25 +4,16 @@ import { recordTurnEffect } from "../lib/record-turn.js";
|
|
|
4
4
|
import { recordRunWorkspaceChangesEffect } from "../lib/record-workspace-changes.js";
|
|
5
5
|
import { DataReader, DataStore } from "@vitest-agent/sdk";
|
|
6
6
|
import { Effect, Option } from "effect";
|
|
7
|
-
import {
|
|
7
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
8
8
|
|
|
9
9
|
//#region src/commands/record.ts
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @packageDocumentation
|
|
18
|
-
*/
|
|
19
|
-
const chatId = Options.text("chat-id").pipe(Options.withDescription("Host chat id (`session_id` in the Claude Code hook envelope; equivalent in other clients)"));
|
|
20
|
-
const occurredAt = Options.text("occurred-at").pipe(Options.withDefault((/* @__PURE__ */ new Date()).toISOString()), Options.withDescription("ISO 8601 timestamp; defaults to now"));
|
|
21
|
-
const payloadArg = Args.text({ name: "payload-json" }).pipe(Args.withDescription("Stringified JSON payload (validated against TurnPayload)"));
|
|
22
|
-
const project = Options.text("project");
|
|
23
|
-
const cwd = Options.text("cwd");
|
|
24
|
-
const projectOptional = Options.optional(Options.text("project"));
|
|
25
|
-
const cwdOptional = Options.optional(Options.text("cwd"));
|
|
10
|
+
const chatId = Flag.string("chat-id").pipe(Flag.withDescription("Host chat id (`session_id` in the Claude Code hook envelope; equivalent in other clients)"));
|
|
11
|
+
const occurredAt = Flag.string("occurred-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()), Flag.withDescription("ISO 8601 timestamp; defaults to now"));
|
|
12
|
+
const payloadArg = Argument.string("payload-json").pipe(Argument.withDescription("Stringified JSON payload (validated against TurnPayload)"));
|
|
13
|
+
const project = Flag.string("project");
|
|
14
|
+
const cwd = Flag.string("cwd");
|
|
15
|
+
const projectOptional = Flag.optional(Flag.string("project"));
|
|
16
|
+
const cwdOptional = Flag.optional(Flag.string("cwd"));
|
|
26
17
|
const turnSubcommand = Command.make("turn", {
|
|
27
18
|
chatId,
|
|
28
19
|
occurredAt,
|
|
@@ -35,15 +26,15 @@ const turnSubcommand = Command.make("turn", {
|
|
|
35
26
|
occurredAt,
|
|
36
27
|
...project._tag === "Some" && { project: project.value },
|
|
37
28
|
...cwd._tag === "Some" && { cwd: cwd.value }
|
|
38
|
-
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.
|
|
29
|
+
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
39
30
|
process.stderr.write(`record turn: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
40
31
|
process.exit(1);
|
|
41
32
|
})))).pipe(Command.withDescription("Validate a TurnPayload JSON and write a turn row"));
|
|
42
|
-
const agentKind =
|
|
43
|
-
const agentType =
|
|
44
|
-
const parentChatId =
|
|
45
|
-
const triageWasNonEmpty =
|
|
46
|
-
const startedAt =
|
|
33
|
+
const agentKind = Flag.choice("agent-kind", ["main", "subagent"]).pipe(Flag.withDefault("main"));
|
|
34
|
+
const agentType = Flag.optional(Flag.string("agent-type"));
|
|
35
|
+
const parentChatId = Flag.optional(Flag.string("parent-chat-id"));
|
|
36
|
+
const triageWasNonEmpty = Flag.boolean("triage-was-non-empty").pipe(Flag.withDefault(false));
|
|
37
|
+
const startedAt = Flag.string("started-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
47
38
|
const sessionStartSubcommand = Command.make("session-start", {
|
|
48
39
|
chatId,
|
|
49
40
|
project,
|
|
@@ -62,12 +53,12 @@ const sessionStartSubcommand = Command.make("session-start", {
|
|
|
62
53
|
...opts.parentChatId._tag === "Some" && { parentChatId: opts.parentChatId.value },
|
|
63
54
|
triageWasNonEmpty: opts.triageWasNonEmpty,
|
|
64
55
|
startedAt: opts.startedAt
|
|
65
|
-
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.
|
|
56
|
+
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
66
57
|
process.stderr.write(`record session-start: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
67
58
|
process.exit(1);
|
|
68
59
|
})))).pipe(Command.withDescription("Insert a new sessions row"));
|
|
69
|
-
const endedAt =
|
|
70
|
-
const endReason =
|
|
60
|
+
const endedAt = Flag.string("ended-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
61
|
+
const endReason = Flag.optional(Flag.string("end-reason"));
|
|
71
62
|
const sessionEndSubcommand = Command.make("session-end", {
|
|
72
63
|
chatId,
|
|
73
64
|
endedAt,
|
|
@@ -76,11 +67,11 @@ const sessionEndSubcommand = Command.make("session-end", {
|
|
|
76
67
|
chatId: opts.chatId,
|
|
77
68
|
endedAt: opts.endedAt,
|
|
78
69
|
endReason: opts.endReason._tag === "Some" ? opts.endReason.value : null
|
|
79
|
-
}).pipe(Effect.flatMap(() => Effect.sync(() => process.stdout.write(`{"ok":true}\n`))), Effect.
|
|
70
|
+
}).pipe(Effect.flatMap(() => Effect.sync(() => process.stdout.write(`{"ok":true}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
80
71
|
process.stderr.write(`record session-end: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
81
72
|
process.exit(1);
|
|
82
73
|
})))).pipe(Command.withDescription("Update sessions.ended_at + end_reason"));
|
|
83
|
-
const artifactKindOpt =
|
|
74
|
+
const artifactKindOpt = Flag.choice("artifact-kind", [
|
|
84
75
|
"test_written",
|
|
85
76
|
"test_failed_run",
|
|
86
77
|
"code_written",
|
|
@@ -88,12 +79,12 @@ const artifactKindOpt = Options.choice("artifact-kind", [
|
|
|
88
79
|
"refactor",
|
|
89
80
|
"test_weakened"
|
|
90
81
|
]);
|
|
91
|
-
const filePathOpt =
|
|
92
|
-
const testCaseIdOpt =
|
|
93
|
-
const testRunIdOpt =
|
|
94
|
-
const testFirstFailureRunIdOpt =
|
|
95
|
-
const diffExcerptOpt =
|
|
96
|
-
const recordedAtOpt =
|
|
82
|
+
const filePathOpt = Flag.optional(Flag.string("file-path"));
|
|
83
|
+
const testCaseIdOpt = Flag.optional(Flag.integer("test-case-id"));
|
|
84
|
+
const testRunIdOpt = Flag.optional(Flag.integer("test-run-id"));
|
|
85
|
+
const testFirstFailureRunIdOpt = Flag.optional(Flag.integer("test-first-failure-run-id"));
|
|
86
|
+
const diffExcerptOpt = Flag.optional(Flag.string("diff-excerpt"));
|
|
87
|
+
const recordedAtOpt = Flag.string("recorded-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
97
88
|
const tddArtifactSubcommand = Command.make("tdd-artifact", {
|
|
98
89
|
chatId,
|
|
99
90
|
project: projectOptional,
|
|
@@ -120,7 +111,7 @@ const tddArtifactSubcommand = Command.make("tdd-artifact", {
|
|
|
120
111
|
...opts.diffExcerpt._tag === "Some" && { diffExcerpt: opts.diffExcerpt.value },
|
|
121
112
|
recordedAt: opts.recordedAt
|
|
122
113
|
});
|
|
123
|
-
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.
|
|
114
|
+
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
124
115
|
process.stderr.write(`record tdd-artifact: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
125
116
|
process.exit(1);
|
|
126
117
|
})))).pipe(Command.withDescription("Record a TDD artifact (D7: CLI-only)"));
|
|
@@ -133,15 +124,15 @@ const testCaseTurnsSubcommand = Command.make("test-case-turns", { chatId }, ({ c
|
|
|
133
124
|
updated,
|
|
134
125
|
latestTestCaseId: Option.getOrNull(latestId)
|
|
135
126
|
};
|
|
136
|
-
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.
|
|
127
|
+
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
137
128
|
process.stderr.write(`record test-case-turns: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
138
129
|
process.exit(1);
|
|
139
130
|
})))).pipe(Command.withDescription("Backfill test_cases.created_turn_id from file_edits in the current session (BUG-2 fix)"));
|
|
140
|
-
const invocationMethodOpt =
|
|
131
|
+
const invocationMethodOpt = Flag.choice("invocation-method", [
|
|
141
132
|
"bash",
|
|
142
133
|
"mcp",
|
|
143
134
|
"cli"
|
|
144
|
-
]).pipe(
|
|
135
|
+
]).pipe(Flag.withDescription("How tests were invoked: \"bash\", \"mcp\", or \"cli\""), Flag.withDefault("bash"));
|
|
145
136
|
const runTriggerSubcommand = Command.make("run-trigger", {
|
|
146
137
|
chatId,
|
|
147
138
|
invocationMethod: invocationMethodOpt
|
|
@@ -150,18 +141,18 @@ const runTriggerSubcommand = Command.make("run-trigger", {
|
|
|
150
141
|
chatId,
|
|
151
142
|
invocationMethod
|
|
152
143
|
});
|
|
153
|
-
}).pipe(Effect.
|
|
144
|
+
}).pipe(Effect.catch((err) => Effect.sync(() => {
|
|
154
145
|
process.stderr.write(`record run-trigger: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
155
146
|
process.exit(1);
|
|
156
147
|
})))).pipe(Command.withDescription("Associate the latest test run with the current Claude Code session"));
|
|
157
|
-
const shaOpt =
|
|
158
|
-
const parentShaOpt =
|
|
159
|
-
const messageOpt =
|
|
160
|
-
const authorOpt =
|
|
161
|
-
const committedAtOpt =
|
|
162
|
-
const branchOpt =
|
|
163
|
-
const projectOpt =
|
|
164
|
-
const filesArg =
|
|
148
|
+
const shaOpt = Flag.string("sha");
|
|
149
|
+
const parentShaOpt = Flag.optional(Flag.string("parent-sha"));
|
|
150
|
+
const messageOpt = Flag.optional(Flag.string("message"));
|
|
151
|
+
const authorOpt = Flag.optional(Flag.string("author"));
|
|
152
|
+
const committedAtOpt = Flag.optional(Flag.string("committed-at"));
|
|
153
|
+
const branchOpt = Flag.optional(Flag.string("branch"));
|
|
154
|
+
const projectOpt = Flag.optional(Flag.string("project"));
|
|
155
|
+
const filesArg = Argument.string("files-json").pipe(Argument.withDescription("JSON array of {\"filePath\",\"changeKind\"} objects"));
|
|
165
156
|
const runWorkspaceChangesSubcommand = Command.make("run-workspace-changes", {
|
|
166
157
|
sha: shaOpt,
|
|
167
158
|
parentSha: parentShaOpt,
|
|
@@ -186,7 +177,7 @@ const runWorkspaceChangesSubcommand = Command.make("run-workspace-changes", {
|
|
|
186
177
|
...opts.project._tag === "Some" && { project: opts.project.value },
|
|
187
178
|
files: parsed
|
|
188
179
|
});
|
|
189
|
-
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.
|
|
180
|
+
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
190
181
|
process.stderr.write(`record run-workspace-changes: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
191
182
|
process.exit(1);
|
|
192
183
|
})))).pipe(Command.withDescription("Record a commit + its changed files (driven by post-commit hook)"));
|
package/commands/triage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatTriageEffect } from "@vitest-agent/sdk";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import { Command,
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
5
5
|
//#region src/commands/triage.ts
|
|
6
6
|
/**
|
|
@@ -12,13 +12,13 @@ import { Command, Options } from "@effect/cli";
|
|
|
12
12
|
*
|
|
13
13
|
* @packageDocumentation
|
|
14
14
|
*/
|
|
15
|
-
const formatOption =
|
|
15
|
+
const formatOption = Flag.withDefault(Flag.choice("format", [
|
|
16
16
|
"markdown",
|
|
17
17
|
"json",
|
|
18
18
|
"silent"
|
|
19
19
|
]), "markdown");
|
|
20
|
-
const projectOption =
|
|
21
|
-
const maxLinesOption =
|
|
20
|
+
const projectOption = Flag.optional(Flag.string("project"));
|
|
21
|
+
const maxLinesOption = Flag.optional(Flag.integer("max-lines"));
|
|
22
22
|
const triageCommand = Command.make("triage", {
|
|
23
23
|
format: formatOption,
|
|
24
24
|
project: projectOption,
|
package/commands/wrapup.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatWrapupEffect } from "@vitest-agent/sdk";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import { Command,
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
5
5
|
//#region src/commands/wrapup.ts
|
|
6
6
|
/**
|
|
@@ -13,17 +13,17 @@ import { Command, Options } from "@effect/cli";
|
|
|
13
13
|
*
|
|
14
14
|
* @packageDocumentation
|
|
15
15
|
*/
|
|
16
|
-
const rowIdOption =
|
|
17
|
-
const chatIdOption =
|
|
18
|
-
const kindOption =
|
|
16
|
+
const rowIdOption = Flag.optional(Flag.integer("row-id"));
|
|
17
|
+
const chatIdOption = Flag.optional(Flag.string("chat-id"));
|
|
18
|
+
const kindOption = Flag.withDefault(Flag.choice("kind", [
|
|
19
19
|
"stop",
|
|
20
20
|
"session_end",
|
|
21
21
|
"pre_compact",
|
|
22
22
|
"tdd_handoff",
|
|
23
23
|
"user_prompt_nudge"
|
|
24
24
|
]), "session_end");
|
|
25
|
-
const userPromptHintOption =
|
|
26
|
-
const formatOption =
|
|
25
|
+
const userPromptHintOption = Flag.optional(Flag.string("user-prompt-hint"));
|
|
26
|
+
const formatOption = Flag.withDefault(Flag.choice("format", ["markdown", "json"]), "markdown");
|
|
27
27
|
const wrapupCommand = Command.make("wrapup", {
|
|
28
28
|
rowId: rowIdOption,
|
|
29
29
|
chatId: chatIdOption,
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
2
2
|
import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
|
|
3
3
|
import { Effect, Layer, LogLevel } from "effect";
|
|
4
4
|
import { DataReader, DataStore, PerClientSessionMapWriter, ProjectIdentityNotResolvableError } from "@vitest-agent/sdk";
|
|
@@ -7,8 +7,8 @@ import { DataReader, DataStore, PerClientSessionMapWriter, ProjectIdentityNotRes
|
|
|
7
7
|
* Composition layer for the CLI runtime.
|
|
8
8
|
*
|
|
9
9
|
* Wires `DataReader`, `ProjectDiscovery`, `HistoryTracker`,
|
|
10
|
-
* `OutputPipeline`, `SqliteClient`, the DB migrator,
|
|
11
|
-
*
|
|
10
|
+
* `OutputPipeline`, `SqliteClient`, the DB migrator, the Node platform
|
|
11
|
+
* services, and `Logger` into a single layer the `vitest-agent`
|
|
12
12
|
* bin provides to `Command.run`.
|
|
13
13
|
*
|
|
14
14
|
* @param dbPath - absolute path to the per-project `data.db`
|
|
@@ -16,7 +16,7 @@ import { DataReader, DataStore, PerClientSessionMapWriter, ProjectIdentityNotRes
|
|
|
16
16
|
* @param logFile - optional path for structured log output
|
|
17
17
|
* @public
|
|
18
18
|
*/
|
|
19
|
-
declare const CliLive: (dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").DetailResolver | import("@vitest-agent/sdk").EnvironmentDetector | import("@vitest-agent/sdk").ExecutorResolver | import("@vitest-agent/sdk").FormatSelector | import("@vitest-agent/sdk").HistoryTracker | import("@vitest-agent/sdk").OutputRenderer | import("@vitest-agent/sdk").ProjectDiscovery | import("
|
|
19
|
+
declare const CliLive: (dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").DetailResolver | import("@vitest-agent/sdk").EnvironmentDetector | import("@vitest-agent/sdk").ExecutorResolver | import("@vitest-agent/sdk").FormatSelector | import("@vitest-agent/sdk").HistoryTracker | import("@vitest-agent/sdk").OutputRenderer | import("@vitest-agent/sdk").ProjectDiscovery | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | NodeServices.NodeServices, SqliteMigrator.MigrationError | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/layers/SidecarLive.d.ts
|
|
22
22
|
/**
|
|
@@ -42,7 +42,7 @@ interface SidecarPaths {
|
|
|
42
42
|
* @param paths - the three SQLite database paths to open
|
|
43
43
|
* @public
|
|
44
44
|
*/
|
|
45
|
-
declare const SidecarLive: (paths: SidecarPaths) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").DiscoveryRegistry | import("@vitest-agent/sdk").PerClientSessionMapReader | import("@vitest-agent/sdk").PerClientSessionMapWriter | import("@vitest-agent/sdk").RunContext$ |
|
|
45
|
+
declare const SidecarLive: (paths: SidecarPaths) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").DiscoveryRegistry | import("@vitest-agent/sdk").PerClientSessionMapReader | import("@vitest-agent/sdk").PerClientSessionMapWriter | import("@vitest-agent/sdk").RunContext$ | NodeServices.NodeServices, SqliteMigrator.MigrationError | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/lib/internal-register-agent.d.ts
|
|
48
48
|
/**
|
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { DATA_DB_FILENAME, REGISTRY_DB_FILENAME, SESSIONS_DB_FILENAME, resolvePr
|
|
|
11
11
|
*
|
|
12
12
|
* @public
|
|
13
13
|
*/
|
|
14
|
-
const CURRENT_CLI_VERSION = "
|
|
14
|
+
const CURRENT_CLI_VERSION = "2.0.0";
|
|
15
15
|
|
|
16
16
|
//#endregion
|
|
17
17
|
export { CURRENT_CLI_VERSION, CliLive, DATA_DB_FILENAME, REGISTRY_DB_FILENAME, SESSIONS_DB_FILENAME, SidecarLive, registerAgentEffect, resolveProjectDataDir, resolveRegistryDir, resolveSessionMapPath };
|
package/layers/CliLive.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as NodeContext$1 from "@effect/platform-node/NodeContext";
|
|
1
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
3
2
|
import { layer } from "@effect/sql-sqlite-node/SqliteClient";
|
|
4
3
|
import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
|
|
5
4
|
import { DataReaderLive, DataStoreLive, HistoryTrackerLive, LoggerLive, OutputPipelineLive, ProjectDiscoveryLive, migration0001 } from "@vitest-agent/sdk";
|
|
@@ -10,8 +9,8 @@ import { Layer } from "effect";
|
|
|
10
9
|
* Composition layer for the CLI runtime.
|
|
11
10
|
*
|
|
12
11
|
* Wires `DataReader`, `ProjectDiscovery`, `HistoryTracker`,
|
|
13
|
-
* `OutputPipeline`, `SqliteClient`, the DB migrator,
|
|
14
|
-
*
|
|
12
|
+
* `OutputPipeline`, `SqliteClient`, the DB migrator, the Node platform
|
|
13
|
+
* services, and `Logger` into a single layer the `vitest-agent`
|
|
15
14
|
* bin provides to `Command.run`.
|
|
16
15
|
*
|
|
17
16
|
* @param dbPath - absolute path to the per-project `data.db`
|
|
@@ -21,9 +20,9 @@ import { Layer } from "effect";
|
|
|
21
20
|
*/
|
|
22
21
|
const CliLive = (dbPath, logLevel, logFile) => {
|
|
23
22
|
const SqliteLayer = layer({ filename: dbPath });
|
|
24
|
-
const PlatformLayer =
|
|
23
|
+
const PlatformLayer = NodeServices.layer;
|
|
25
24
|
const MigratorLayer = SqliteMigrator.layer({ loader: SqliteMigrator.fromRecord({ "0001_initial": migration0001 }) }).pipe(Layer.provide(Layer.merge(SqliteLayer, PlatformLayer)));
|
|
26
|
-
return Layer.mergeAll(ProjectDiscoveryLive, HistoryTrackerLive, OutputPipelineLive).pipe(Layer.provideMerge(DataReaderLive), Layer.provideMerge(DataStoreLive), Layer.provideMerge(MigratorLayer), Layer.provideMerge(SqliteLayer), Layer.provideMerge(PlatformLayer), Layer.provideMerge(
|
|
25
|
+
return Layer.mergeAll(ProjectDiscoveryLive, HistoryTrackerLive, OutputPipelineLive).pipe(Layer.provideMerge(DataReaderLive), Layer.provideMerge(DataStoreLive), Layer.provideMerge(MigratorLayer), Layer.provideMerge(SqliteLayer), Layer.provideMerge(PlatformLayer), Layer.provideMerge(LoggerLive(logLevel, logFile)));
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
//#endregion
|
package/layers/SidecarLive.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as NodeContext$1 from "@effect/platform-node/NodeContext";
|
|
1
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
3
2
|
import { layer } from "@effect/sql-sqlite-node/SqliteClient";
|
|
4
3
|
import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
|
|
5
4
|
import { DataReaderLive, DataStoreLive, DiscoveryRegistryLive, LoggerLive, PerClientSessionMapWriterLive, RunContextLive, migration0001, registryMigration0001, sessionMapMigration0001 } from "@vitest-agent/sdk";
|
|
@@ -17,7 +16,7 @@ import { Layer } from "effect";
|
|
|
17
16
|
* @public
|
|
18
17
|
*/
|
|
19
18
|
const SidecarLive = (paths) => {
|
|
20
|
-
const PlatformLayer =
|
|
19
|
+
const PlatformLayer = NodeServices.layer;
|
|
21
20
|
const ProjectSqliteLayer = layer({ filename: paths.perProjectDbPath });
|
|
22
21
|
const ProjectMigratorLayer = SqliteMigrator.layer({ loader: SqliteMigrator.fromRecord({ "0001_initial": migration0001 }) }).pipe(Layer.provide(Layer.merge(ProjectSqliteLayer, PlatformLayer)));
|
|
23
22
|
const ProjectStoreLayer = Layer.mergeAll(DataStoreLive.pipe(Layer.provide(ProjectSqliteLayer)), DataReaderLive.pipe(Layer.provide(ProjectSqliteLayer)), ProjectMigratorLayer);
|
|
@@ -27,7 +26,7 @@ const SidecarLive = (paths) => {
|
|
|
27
26
|
const RegistrySqliteLayer = layer({ filename: paths.registryDbPath });
|
|
28
27
|
const RegistryMigratorLayer = SqliteMigrator.layer({ loader: SqliteMigrator.fromRecord({ "0001_initial": registryMigration0001 }) }).pipe(Layer.provide(Layer.merge(RegistrySqliteLayer, PlatformLayer)));
|
|
29
28
|
const RegistryLayer = Layer.mergeAll(DiscoveryRegistryLive.pipe(Layer.provide(RegistrySqliteLayer)), RegistryMigratorLayer);
|
|
30
|
-
return Layer.mergeAll(ProjectStoreLayer, SessionMapLayer, RegistryLayer, RunContextLive).pipe(Layer.provideMerge(PlatformLayer), Layer.provideMerge(
|
|
29
|
+
return Layer.mergeAll(ProjectStoreLayer, SessionMapLayer, RegistryLayer, RunContextLive).pipe(Layer.provideMerge(PlatformLayer), Layer.provideMerge(LoggerLive()));
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
//#endregion
|
package/lib/record-turn.js
CHANGED
|
@@ -13,14 +13,14 @@ const parseAndValidateTurnPayload = (raw) => {
|
|
|
13
13
|
error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
const decoded = Schema.
|
|
17
|
-
if (decoded._tag === "
|
|
16
|
+
const decoded = Schema.decodeUnknownResult(TurnPayload)(parsed);
|
|
17
|
+
if (decoded._tag === "Failure") return {
|
|
18
18
|
ok: false,
|
|
19
|
-
error: `Invalid TurnPayload: ${decoded.
|
|
19
|
+
error: `Invalid TurnPayload: ${decoded.failure.message}`
|
|
20
20
|
};
|
|
21
21
|
return {
|
|
22
22
|
ok: true,
|
|
23
|
-
payload: decoded.
|
|
23
|
+
payload: decoded.success
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
const recordTurnEffect = (input) => Effect.gen(function* () {
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import { DataStore } from "@vitest-agent/sdk";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import { SqlClient } from "
|
|
3
|
+
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
4
4
|
|
|
5
5
|
//#region src/lib/record-workspace-changes.ts
|
|
6
6
|
/**
|
|
7
|
-
* Lib function for the `record run-workspace-changes` CLI subcommand.
|
|
8
|
-
*
|
|
9
|
-
* Driven by the PostToolUse hook on `git commit` / `git push`. Writes a
|
|
10
|
-
* commits row (idempotent on sha via ON CONFLICT DO NOTHING in
|
|
11
|
-
* DataStore.writeCommit) and zero or more run_changed_files rows.
|
|
12
|
-
*
|
|
13
|
-
* Optionally associates the changed files with a test_run_id; for the
|
|
14
|
-
* commit-side write path we don't have a run, so we associate the files
|
|
15
|
-
* with the most-recent test_run for the project (best-effort) or skip
|
|
16
|
-
* the file rows entirely if no test_run exists yet.
|
|
17
|
-
*
|
|
18
|
-
* @packageDocumentation
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
7
|
* `ProjectRunSummary` doesn't expose `lastRunId`, so we query it directly
|
|
22
8
|
* via SqlClient. This is the run id of the most-recent test run for a
|
|
23
9
|
* given project (or any project, when `project` is unspecified). Used
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "On-demand CLI for vitest-agent. Reads cached test data and reports status, overview, coverage, history, trends, and cache health.",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
31
31
|
"types": "./index.d.ts",
|
|
32
|
-
"import": "./index.js"
|
|
32
|
+
"import": "./index.js",
|
|
33
|
+
"default": "./index.js"
|
|
33
34
|
},
|
|
34
35
|
"./package.json": "./package.json"
|
|
35
36
|
},
|
|
@@ -37,21 +38,11 @@
|
|
|
37
38
|
"vitest-agent": "bin/vitest-agent.js"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@effect/
|
|
41
|
-
"@effect/
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"@effect/printer": "^0.49.0",
|
|
46
|
-
"@effect/printer-ansi": "^0.49.0",
|
|
47
|
-
"@effect/rpc": "^0.75.1",
|
|
48
|
-
"@effect/sql": "^0.51.1",
|
|
49
|
-
"@effect/sql-sqlite-node": "^0.52.0",
|
|
50
|
-
"@effect/typeclass": "^0.40.0",
|
|
51
|
-
"@effect/workflow": "^0.18.2",
|
|
52
|
-
"@vitest-agent/sdk": "1.3.3",
|
|
53
|
-
"@vitest-agent/sidecar": "1.0.2",
|
|
54
|
-
"effect": "^3.21.4"
|
|
41
|
+
"@effect/platform-node": "4.0.0-beta.98",
|
|
42
|
+
"@effect/sql-sqlite-node": "4.0.0-beta.98",
|
|
43
|
+
"@vitest-agent/sdk": "2.0.0",
|
|
44
|
+
"@vitest-agent/sidecar": "2.0.0",
|
|
45
|
+
"effect": "4.0.0-beta.98"
|
|
55
46
|
},
|
|
56
47
|
"engines": {
|
|
57
48
|
"node": ">=24.11.0"
|