@vitest-agent/cli 2.2.18 → 3.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 +4 -24
- package/commands/agent.js +31 -39
- package/commands/db.js +7 -7
- package/commands/doctor.js +2 -2
- package/commands/record.js +36 -40
- package/commands/triage.js +4 -4
- package/commands/wrapup.js +6 -6
- package/index.d.ts +3 -158
- package/index.js +2 -16
- package/main.d.ts +22 -0
- package/main.js +58 -0
- package/package.json +13 -7
- package/version.js +13 -0
- package/layers/CliLive.js +0 -29
- package/layers/SidecarLive.js +0 -33
- package/lib/internal-end-agent.js +0 -27
- package/lib/internal-register-agent.js +0 -81
- package/lib/record-session.js +0 -33
- package/lib/record-tdd-artifact.js +0 -104
- package/lib/record-turn.js +0 -44
- package/lib/record-workspace-changes.js +0 -50
- package/lib/resolve-session-for-recording.js +0 -71
- package/lib/sidecar-paths.js +0 -101
package/bin/vitest-agent.js
CHANGED
|
@@ -1,34 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { agentCommand } from "../commands/agent.js";
|
|
4
|
-
import { dbCommand } from "../commands/db.js";
|
|
5
|
-
import { doctorCommand } from "../commands/doctor.js";
|
|
6
|
-
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
7
|
-
import { PathResolutionLive, formatFatalError, resolveDataPath, resolveLogFile, resolveLogLevel } from "@vitest-agent/sdk";
|
|
8
|
-
import { Cause, Console, Effect } from "effect";
|
|
9
|
-
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
|
|
10
|
-
import { Command } from "effect/unstable/cli";
|
|
2
|
+
import { main } from "../main.js";
|
|
11
3
|
|
|
12
4
|
//#region src/bin.ts
|
|
13
5
|
/**
|
|
14
|
-
* CLI entry point for vitest-agent.
|
|
6
|
+
* CLI entry point for vitest-agent. Thin shim over `main.ts` — the
|
|
7
|
+
* assembled program that owns the process.
|
|
15
8
|
*
|
|
16
9
|
* @packageDocumentation
|
|
17
10
|
*/
|
|
18
|
-
|
|
19
|
-
dbCommand,
|
|
20
|
-
doctorCommand,
|
|
21
|
-
agentCommand
|
|
22
|
-
]));
|
|
23
|
-
const cli = Command.run(rootCommand, { version: "0.0.0" });
|
|
24
|
-
const logLevel = resolveLogLevel();
|
|
25
|
-
const logFile = resolveLogFile();
|
|
26
|
-
const projectDir = process.env.VITEST_AGENT_PROJECT_DIR ?? process.cwd();
|
|
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)));
|
|
29
|
-
return Effect.failCause(cause);
|
|
30
|
-
}));
|
|
31
|
-
NodeRuntime.runMain(main);
|
|
11
|
+
main();
|
|
32
12
|
|
|
33
13
|
//#endregion
|
|
34
14
|
export { };
|
package/commands/agent.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { SidecarLive } from "../layers/SidecarLive.js";
|
|
2
|
-
import { registerAgentEffect } from "../lib/internal-register-agent.js";
|
|
3
|
-
import { DATA_DB_FILENAME, REGISTRY_DB_FILENAME, resolveProjectDataDir, resolveRegistryDir, resolveSessionMapPath } from "../lib/sidecar-paths.js";
|
|
4
|
-
import { endAgentEffect } from "../lib/internal-end-agent.js";
|
|
5
1
|
import { recordCommand } from "./record.js";
|
|
6
2
|
import { triageCommand } from "./triage.js";
|
|
7
3
|
import { wrapupCommand } from "./wrapup.js";
|
|
8
4
|
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
9
|
-
import {
|
|
5
|
+
import { SidecarPlatformLive, endAgentEffect, registerAgentEffect, resolveHookPaths, resolveProjectKeyFromCwd } from "@vitest-agent/engine";
|
|
6
|
+
import { classifyTestPath, detectNonDefaultDiscoverStrategy, findOwningWorkspace } from "@vitest-agent/sdk";
|
|
10
7
|
import { Cause, Effect, Option } from "effect";
|
|
8
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
11
9
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
10
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
|
-
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
14
11
|
import { findWorkspaceRootSync, getWorkspacePackagesSync } from "@effected/workspaces";
|
|
15
12
|
import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
16
13
|
import { exitCodeForTag, injectEnv } from "@vitest-agent/sdk/dispatch";
|
|
@@ -66,14 +63,14 @@ const mapDefectToExit = (cause) => {
|
|
|
66
63
|
const message = defect instanceof Error ? defect.message : String(defect ?? "unknown defect");
|
|
67
64
|
return writeStderrAndExit(5, "Defect", message);
|
|
68
65
|
};
|
|
69
|
-
const hostKindOpt = Flag.
|
|
70
|
-
const agentTypeOpt = Flag.
|
|
71
|
-
const hostSessionIdOpt = Flag.
|
|
72
|
-
const transcriptPathOpt = Flag.
|
|
73
|
-
const cwdOpt = Flag.
|
|
74
|
-
const parentAgentIdOpt = Flag.optional(Flag.
|
|
75
|
-
const clientNonceOpt = Flag.optional(Flag.
|
|
76
|
-
const projectKeyOverrideOpt = Flag.optional(Flag.
|
|
66
|
+
const hostKindOpt = Flag.String("host-kind").pipe(Flag.withDescription("Host vendor identifier; e.g. 'claude-code', 'cursor', 'goose'"));
|
|
67
|
+
const agentTypeOpt = Flag.String("agent-type").pipe(Flag.withDescription("Agent type, must begin with the host-kind prefix"));
|
|
68
|
+
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)"));
|
|
69
|
+
const transcriptPathOpt = Flag.String("transcript-path").pipe(Flag.withDescription("Path to the host's transcript file (basename UUID is the conversation key)"));
|
|
70
|
+
const cwdOpt = Flag.String("cwd").pipe(Flag.withDescription("Workspace root directory the agent is running in"));
|
|
71
|
+
const parentAgentIdOpt = Flag.optional(Flag.String("parent-agent-id"));
|
|
72
|
+
const clientNonceOpt = Flag.optional(Flag.String("client-nonce"));
|
|
73
|
+
const projectKeyOverrideOpt = Flag.optional(Flag.String("project-key"));
|
|
77
74
|
const registerAgentSubcommand = Command.make("register-agent", {
|
|
78
75
|
hostKind: hostKindOpt,
|
|
79
76
|
agentType: agentTypeOpt,
|
|
@@ -85,14 +82,11 @@ const registerAgentSubcommand = Command.make("register-agent", {
|
|
|
85
82
|
projectKeyOverride: projectKeyOverrideOpt
|
|
86
83
|
}, (opts) => Effect.gen(function* () {
|
|
87
84
|
const projectKey = Option.isSome(opts.projectKeyOverride) ? opts.projectKeyOverride.value : resolveProjectKeyFromCwd(opts.cwd);
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
sessionMapDbPath,
|
|
94
|
-
registryDbPath
|
|
95
|
-
});
|
|
85
|
+
const paths = yield* resolveHookPaths({
|
|
86
|
+
env: process.env,
|
|
87
|
+
projectKey
|
|
88
|
+
}).pipe(Effect.catchCause(mapDefectToExit));
|
|
89
|
+
const sidecar = SidecarPlatformLive(paths, process.env);
|
|
96
90
|
const result = yield* registerAgentEffect({
|
|
97
91
|
hostSessionId: opts.hostSessionId,
|
|
98
92
|
transcriptPath: opts.transcriptPath,
|
|
@@ -111,11 +105,11 @@ const registerAgentSubcommand = Command.make("register-agent", {
|
|
|
111
105
|
idempotencyHit: result.idempotencyHit
|
|
112
106
|
}));
|
|
113
107
|
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Register an agent invocation in the per-project store and the per-client session map"));
|
|
114
|
-
const agentIdOpt = Flag.
|
|
115
|
-
const endedAtOpt = Flag.optional(Flag.
|
|
116
|
-
const endHostSessionIdOpt = Flag.optional(Flag.
|
|
117
|
-
const endCwdOpt = Flag.
|
|
118
|
-
const endProjectKeyOverrideOpt = Flag.optional(Flag.
|
|
108
|
+
const agentIdOpt = Flag.String("agent-id").pipe(Flag.withDescription("The agent_id (UUID) returned by an earlier register-agent call"));
|
|
109
|
+
const endedAtOpt = Flag.optional(Flag.Int("ended-at"));
|
|
110
|
+
const endHostSessionIdOpt = Flag.optional(Flag.String("host-session-id"));
|
|
111
|
+
const endCwdOpt = Flag.String("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Workspace root, used to locate the per-project data.db"));
|
|
112
|
+
const endProjectKeyOverrideOpt = Flag.optional(Flag.String("project-key"));
|
|
119
113
|
const endAgentSubcommand = Command.make("end-agent", {
|
|
120
114
|
agentId: agentIdOpt,
|
|
121
115
|
endedAt: endedAtOpt,
|
|
@@ -124,14 +118,11 @@ const endAgentSubcommand = Command.make("end-agent", {
|
|
|
124
118
|
projectKeyOverride: endProjectKeyOverrideOpt
|
|
125
119
|
}, (opts) => Effect.gen(function* () {
|
|
126
120
|
const projectKey = Option.isSome(opts.projectKeyOverride) ? opts.projectKeyOverride.value : resolveProjectKeyFromCwd(opts.cwd);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
sessionMapDbPath,
|
|
133
|
-
registryDbPath
|
|
134
|
-
});
|
|
121
|
+
const paths = yield* resolveHookPaths({
|
|
122
|
+
env: process.env,
|
|
123
|
+
projectKey
|
|
124
|
+
}).pipe(Effect.catchCause(mapDefectToExit));
|
|
125
|
+
const sidecar = SidecarPlatformLive(paths, process.env);
|
|
135
126
|
const endedAt = Option.isSome(opts.endedAt) ? opts.endedAt.value : Math.floor(Date.now() / 1e3);
|
|
136
127
|
yield* endAgentEffect({
|
|
137
128
|
agentId: opts.agentId,
|
|
@@ -139,8 +130,8 @@ const endAgentSubcommand = Command.make("end-agent", {
|
|
|
139
130
|
...Option.isSome(opts.hostSessionId) && { hostSessionId: opts.hostSessionId.value }
|
|
140
131
|
}).pipe(Effect.provide(sidecar), Effect.catchCause(mapDefectToExit));
|
|
141
132
|
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Mark an agent (and optionally its session) as ended"));
|
|
142
|
-
const commandOpt = Flag.
|
|
143
|
-
const cwdInjectOpt = Flag.
|
|
133
|
+
const commandOpt = Flag.String("command").pipe(Flag.withDescription("The Bash command to (possibly) rewrite with VITEST_AGENT_* env-prefix"));
|
|
134
|
+
const cwdInjectOpt = Flag.String("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Working directory; used to find package.json scripts"));
|
|
144
135
|
const injectEnvSubcommand = Command.make("inject-env", {
|
|
145
136
|
command: commandOpt,
|
|
146
137
|
cwd: cwdInjectOpt
|
|
@@ -148,7 +139,8 @@ const injectEnvSubcommand = Command.make("inject-env", {
|
|
|
148
139
|
const out = injectEnv({
|
|
149
140
|
command: opts.command,
|
|
150
141
|
cwd: opts.cwd,
|
|
151
|
-
env: process.env
|
|
142
|
+
env: process.env,
|
|
143
|
+
readFile: (path) => readFileSync(path, "utf-8")
|
|
152
144
|
});
|
|
153
145
|
process.stdout.write(`${out}\n`);
|
|
154
146
|
})).pipe(Command.withDescription("Rewrite a Bash command to prepend VITEST_AGENT_* env vars when it invokes Vitest"));
|
|
@@ -157,7 +149,7 @@ const sidecarPathSubcommand = Command.make("sidecar-path", {}, () => Effect.sync
|
|
|
157
149
|
if (path === null) process.exit(1);
|
|
158
150
|
process.stdout.write(`${path}\n`);
|
|
159
151
|
})).pipe(Command.withDescription("Print the absolute path of the platform sidecar binary resolved via require.resolve"));
|
|
160
|
-
const testPathArg = Argument.
|
|
152
|
+
const testPathArg = Argument.String("path").pipe(Argument.withDescription("Path to classify against the workspace test layout; relative paths resolve against VITEST_AGENT_PROJECT_DIR, or cwd when that is unset"));
|
|
161
153
|
/**
|
|
162
154
|
* True when any directory between `ownerPath` and `filePath`'s own directory
|
|
163
155
|
* declares its own `package.json`.
|
package/commands/db.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { formatDbQuery } from "../lib/format-db-query.js";
|
|
2
2
|
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
3
|
-
import {
|
|
4
|
-
import { DataStore, resolveDataPath } from "@vitest-agent/sdk";
|
|
3
|
+
import { DataStore, resolveDataPath } from "@vitest-agent/engine";
|
|
5
4
|
import { Effect, FileSystem } from "effect";
|
|
6
5
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
7
|
-
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
8
6
|
import * as readline from "node:readline";
|
|
7
|
+
import { layer } from "@effect/sql-sqlite-node/SqliteClient";
|
|
8
|
+
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
9
9
|
|
|
10
10
|
//#region src/commands/db.ts
|
|
11
11
|
/**
|
|
@@ -17,12 +17,12 @@ const pathCommand = Command.make("path", {}, () => Effect.gen(function* () {
|
|
|
17
17
|
const dbPath = yield* resolveDataPath(process.env.VITEST_AGENT_PROJECT_DIR ?? process.cwd());
|
|
18
18
|
yield* Effect.sync(() => process.stdout.write(`${dbPath}\n`));
|
|
19
19
|
})).pipe(Command.withDescription("Print the resolved database path"));
|
|
20
|
-
const keepRecentOption = Flag.withDefault(Flag.
|
|
20
|
+
const keepRecentOption = Flag.withDefault(Flag.Int("keep-recent"), 30).pipe(Flag.withDescription("Number of most-recent sessions to keep in full"));
|
|
21
21
|
const pruneCommand = Command.make("prune", { keepRecent: keepRecentOption }, ({ keepRecent }) => Effect.gen(function* () {
|
|
22
22
|
const result = yield* (yield* DataStore).pruneSessions(keepRecent);
|
|
23
23
|
yield* Effect.sync(() => process.stdout.write(`Pruned ${result.prunedTurns} turn row(s) across ${result.affectedSessions} session(s); session rows retained.\n`));
|
|
24
24
|
})).pipe(Command.withDescription("Drop old sessions' turn history (W1 retention; keeps the last N in full)"));
|
|
25
|
-
const yesOption = Flag.
|
|
25
|
+
const yesOption = Flag.Boolean("yes").pipe(Flag.withDefault(false), Flag.withDescription("Skip the interactive confirmation prompt"));
|
|
26
26
|
const resetCommand = Command.make("reset", { yes: yesOption }, ({ yes }) => Effect.gen(function* () {
|
|
27
27
|
const agentId = process.env.VITEST_AGENT_AGENT_ID;
|
|
28
28
|
if (agentId !== void 0 && agentId.length > 0) {
|
|
@@ -68,8 +68,8 @@ const resetCommand = Command.make("reset", { yes: yesOption }, ({ yes }) => Effe
|
|
|
68
68
|
process.stdout.write(`Deleted database at ${dbPath}\n`);
|
|
69
69
|
});
|
|
70
70
|
}).pipe(Effect.provide(NodeServices.layer))).pipe(Command.withDescription("Wipe the database (human-only; blocked in agent contexts)"));
|
|
71
|
-
const queryFormatOption = Flag.
|
|
72
|
-
const sqlArg = Argument.
|
|
71
|
+
const queryFormatOption = Flag.Literals("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"));
|
|
73
73
|
/**
|
|
74
74
|
* Flatten an error and its cause chain into a single message so the
|
|
75
75
|
* driver's `attempt to write a readonly database` text surfaces to
|
package/commands/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatDoctor } from "../lib/format-doctor.js";
|
|
2
|
-
import { DataReader, resolveDataPath } from "@vitest-agent/
|
|
2
|
+
import { DataReader, resolveDataPath } from "@vitest-agent/engine";
|
|
3
3
|
import { Effect, Option } from "effect";
|
|
4
4
|
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ import { Command, Flag } from "effect/unstable/cli";
|
|
|
9
9
|
*
|
|
10
10
|
* @packageDocumentation
|
|
11
11
|
*/
|
|
12
|
-
const formatOption = Flag.withDefault(Flag.
|
|
12
|
+
const formatOption = Flag.withDefault(Flag.Literals("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`);
|
package/commands/record.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import { recordSessionEnd, recordSessionStart } from "
|
|
2
|
-
import { dispatchRecordTddArtifactEffect } from "../lib/record-tdd-artifact.js";
|
|
3
|
-
import { recordTurnEffect } from "../lib/record-turn.js";
|
|
4
|
-
import { recordRunWorkspaceChangesEffect } from "../lib/record-workspace-changes.js";
|
|
5
|
-
import { DataReader, DataStore } from "@vitest-agent/sdk";
|
|
1
|
+
import { DataReader, DataStore, dispatchRecordTddArtifactEffect, recordRunWorkspaceChangesEffect, recordSessionEnd, recordSessionStart, recordTurnEffect } from "@vitest-agent/engine";
|
|
6
2
|
import { Effect, Option } from "effect";
|
|
7
3
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
8
4
|
|
|
9
5
|
//#region src/commands/record.ts
|
|
10
|
-
const chatId = Flag.
|
|
11
|
-
const occurredAt = Flag.
|
|
12
|
-
const payloadArg = Argument.
|
|
13
|
-
const project = Flag.
|
|
14
|
-
const cwd = Flag.
|
|
15
|
-
const projectOptional = Flag.optional(Flag.
|
|
16
|
-
const cwdOptional = Flag.optional(Flag.
|
|
6
|
+
const chatId = Flag.String("chat-id").pipe(Flag.withDescription("Host chat id (`session_id` in the Claude Code hook envelope; equivalent in other clients)"));
|
|
7
|
+
const occurredAt = Flag.String("occurred-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()), Flag.withDescription("ISO 8601 timestamp; defaults to now"));
|
|
8
|
+
const payloadArg = Argument.String("payload-json").pipe(Argument.withDescription("Stringified JSON payload (validated against TurnPayload)"));
|
|
9
|
+
const project = Flag.String("project");
|
|
10
|
+
const cwd = Flag.String("cwd");
|
|
11
|
+
const projectOptional = Flag.optional(Flag.String("project"));
|
|
12
|
+
const cwdOptional = Flag.optional(Flag.String("cwd"));
|
|
17
13
|
const turnSubcommand = Command.make("turn", {
|
|
18
14
|
chatId,
|
|
19
15
|
occurredAt,
|
|
@@ -25,16 +21,16 @@ const turnSubcommand = Command.make("turn", {
|
|
|
25
21
|
payloadJson: payload,
|
|
26
22
|
occurredAt,
|
|
27
23
|
...project._tag === "Some" && { project: project.value },
|
|
28
|
-
|
|
24
|
+
cwd: cwd._tag === "Some" ? cwd.value : process.cwd()
|
|
29
25
|
}).pipe(Effect.flatMap((result) => Effect.sync(() => process.stdout.write(`${JSON.stringify(result)}\n`))), Effect.catch((err) => Effect.sync(() => {
|
|
30
26
|
process.stderr.write(`record turn: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
31
27
|
process.exit(1);
|
|
32
28
|
})))).pipe(Command.withDescription("Validate a TurnPayload JSON and write a turn row"));
|
|
33
|
-
const agentKind = Flag.
|
|
34
|
-
const agentType = Flag.optional(Flag.
|
|
35
|
-
const parentChatId = Flag.optional(Flag.
|
|
36
|
-
const triageWasNonEmpty = Flag.
|
|
37
|
-
const startedAt = Flag.
|
|
29
|
+
const agentKind = Flag.Literals("agent-kind", ["main", "subagent"]).pipe(Flag.withDefault("main"));
|
|
30
|
+
const agentType = Flag.optional(Flag.String("agent-type"));
|
|
31
|
+
const parentChatId = Flag.optional(Flag.String("parent-chat-id"));
|
|
32
|
+
const triageWasNonEmpty = Flag.Boolean("triage-was-non-empty").pipe(Flag.withDefault(false));
|
|
33
|
+
const startedAt = Flag.String("started-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
38
34
|
const sessionStartSubcommand = Command.make("session-start", {
|
|
39
35
|
chatId,
|
|
40
36
|
project,
|
|
@@ -57,8 +53,8 @@ const sessionStartSubcommand = Command.make("session-start", {
|
|
|
57
53
|
process.stderr.write(`record session-start: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
58
54
|
process.exit(1);
|
|
59
55
|
})))).pipe(Command.withDescription("Insert a new sessions row"));
|
|
60
|
-
const endedAt = Flag.
|
|
61
|
-
const endReason = Flag.optional(Flag.
|
|
56
|
+
const endedAt = Flag.String("ended-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
57
|
+
const endReason = Flag.optional(Flag.String("end-reason"));
|
|
62
58
|
const sessionEndSubcommand = Command.make("session-end", {
|
|
63
59
|
chatId,
|
|
64
60
|
endedAt,
|
|
@@ -71,7 +67,7 @@ const sessionEndSubcommand = Command.make("session-end", {
|
|
|
71
67
|
process.stderr.write(`record session-end: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
72
68
|
process.exit(1);
|
|
73
69
|
})))).pipe(Command.withDescription("Update sessions.ended_at + end_reason"));
|
|
74
|
-
const artifactKindOpt = Flag.
|
|
70
|
+
const artifactKindOpt = Flag.Literals("artifact-kind", [
|
|
75
71
|
"test_written",
|
|
76
72
|
"test_failed_run",
|
|
77
73
|
"code_written",
|
|
@@ -79,15 +75,15 @@ const artifactKindOpt = Flag.choice("artifact-kind", [
|
|
|
79
75
|
"refactor",
|
|
80
76
|
"test_weakened"
|
|
81
77
|
]);
|
|
82
|
-
const filePathOpt = Flag.optional(Flag.
|
|
83
|
-
const testCaseIdOpt = Flag.optional(Flag.
|
|
84
|
-
const testRunIdOpt = Flag.optional(Flag.
|
|
85
|
-
const testFirstFailureRunIdOpt = Flag.optional(Flag.
|
|
86
|
-
const diffExcerptOpt = Flag.optional(Flag.
|
|
87
|
-
const recordedAtOpt = Flag.
|
|
88
|
-
const chatIdOptional = Flag.optional(Flag.
|
|
89
|
-
const tddTaskIdOpt = Flag.optional(Flag.
|
|
90
|
-
const suiteOpt = Flag.
|
|
78
|
+
const filePathOpt = Flag.optional(Flag.String("file-path"));
|
|
79
|
+
const testCaseIdOpt = Flag.optional(Flag.Int("test-case-id"));
|
|
80
|
+
const testRunIdOpt = Flag.optional(Flag.Int("test-run-id"));
|
|
81
|
+
const testFirstFailureRunIdOpt = Flag.optional(Flag.Int("test-first-failure-run-id"));
|
|
82
|
+
const diffExcerptOpt = Flag.optional(Flag.String("diff-excerpt"));
|
|
83
|
+
const recordedAtOpt = Flag.String("recorded-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
|
|
84
|
+
const chatIdOptional = Flag.optional(Flag.String("chat-id")).pipe(Flag.withDescription("Host chat id; omit when --tdd-task-id is supplied instead"));
|
|
85
|
+
const tddTaskIdOpt = Flag.optional(Flag.Int("tdd-task-id")).pipe(Flag.withDescription("Explicit TDD task id escape hatch (issue #144): bypasses chat-id -> session -> task resolution entirely"));
|
|
86
|
+
const suiteOpt = Flag.Literals("suite", ["vitest", "bats"]).pipe(Flag.withDefault("vitest"), Flag.withDescription("Explicit test-runner marker (issue #363): 'vitest' (default) or 'bats'. Distinguishes a bats run-level artifact (no test_case_id) from a vitest one for the D2 phase-transition validator."));
|
|
91
87
|
const tddArtifactSubcommand = Command.make("tdd-artifact", {
|
|
92
88
|
chatId: chatIdOptional,
|
|
93
89
|
tddTaskId: tddTaskIdOpt,
|
|
@@ -108,7 +104,7 @@ const tddArtifactSubcommand = Command.make("tdd-artifact", {
|
|
|
108
104
|
...opts.chatId._tag === "Some" && { chatId: opts.chatId.value },
|
|
109
105
|
...opts.tddTaskId._tag === "Some" && { tddTaskId: opts.tddTaskId.value },
|
|
110
106
|
...opts.project._tag === "Some" && { project: opts.project.value },
|
|
111
|
-
|
|
107
|
+
cwd: opts.cwd._tag === "Some" ? opts.cwd.value : process.cwd(),
|
|
112
108
|
artifactKind: opts.artifactKind,
|
|
113
109
|
...fileId !== void 0 && { fileId },
|
|
114
110
|
...opts.testCaseId._tag === "Some" && { testCaseId: opts.testCaseId.value },
|
|
@@ -135,7 +131,7 @@ const testCaseTurnsSubcommand = Command.make("test-case-turns", { chatId }, ({ c
|
|
|
135
131
|
process.stderr.write(`record test-case-turns: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
136
132
|
process.exit(1);
|
|
137
133
|
})))).pipe(Command.withDescription("Backfill test_cases.created_turn_id from file_edits in the current session (BUG-2 fix)"));
|
|
138
|
-
const invocationMethodOpt = Flag.
|
|
134
|
+
const invocationMethodOpt = Flag.Literals("invocation-method", [
|
|
139
135
|
"bash",
|
|
140
136
|
"mcp",
|
|
141
137
|
"cli"
|
|
@@ -152,14 +148,14 @@ const runTriggerSubcommand = Command.make("run-trigger", {
|
|
|
152
148
|
process.stderr.write(`record run-trigger: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
153
149
|
process.exit(1);
|
|
154
150
|
})))).pipe(Command.withDescription("Associate the latest test run with the current Claude Code session"));
|
|
155
|
-
const shaOpt = Flag.
|
|
156
|
-
const parentShaOpt = Flag.optional(Flag.
|
|
157
|
-
const messageOpt = Flag.optional(Flag.
|
|
158
|
-
const authorOpt = Flag.optional(Flag.
|
|
159
|
-
const committedAtOpt = Flag.optional(Flag.
|
|
160
|
-
const branchOpt = Flag.optional(Flag.
|
|
161
|
-
const projectOpt = Flag.optional(Flag.
|
|
162
|
-
const filesArg = Argument.
|
|
151
|
+
const shaOpt = Flag.String("sha");
|
|
152
|
+
const parentShaOpt = Flag.optional(Flag.String("parent-sha"));
|
|
153
|
+
const messageOpt = Flag.optional(Flag.String("message"));
|
|
154
|
+
const authorOpt = Flag.optional(Flag.String("author"));
|
|
155
|
+
const committedAtOpt = Flag.optional(Flag.String("committed-at"));
|
|
156
|
+
const branchOpt = Flag.optional(Flag.String("branch"));
|
|
157
|
+
const projectOpt = Flag.optional(Flag.String("project"));
|
|
158
|
+
const filesArg = Argument.String("files-json").pipe(Argument.withDescription("JSON array of {\"filePath\",\"changeKind\"} objects"));
|
|
163
159
|
const runWorkspaceChangesSubcommand = Command.make("run-workspace-changes", {
|
|
164
160
|
sha: shaOpt,
|
|
165
161
|
parentSha: parentShaOpt,
|
package/commands/triage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatTriageEffect } from "@vitest-agent/
|
|
1
|
+
import { formatTriageEffect } from "@vitest-agent/engine";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
@@ -12,13 +12,13 @@ import { Command, Flag } from "effect/unstable/cli";
|
|
|
12
12
|
*
|
|
13
13
|
* @packageDocumentation
|
|
14
14
|
*/
|
|
15
|
-
const formatOption = Flag.withDefault(Flag.
|
|
15
|
+
const formatOption = Flag.withDefault(Flag.Literals("format", [
|
|
16
16
|
"markdown",
|
|
17
17
|
"json",
|
|
18
18
|
"silent"
|
|
19
19
|
]), "markdown");
|
|
20
|
-
const projectOption = Flag.optional(Flag.
|
|
21
|
-
const maxLinesOption = Flag.optional(Flag.
|
|
20
|
+
const projectOption = Flag.optional(Flag.String("project"));
|
|
21
|
+
const maxLinesOption = Flag.optional(Flag.Int("max-lines"));
|
|
22
22
|
const triageCommand = Command.make("triage", {
|
|
23
23
|
format: formatOption,
|
|
24
24
|
project: projectOption,
|
package/commands/wrapup.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatWrapupEffect } from "@vitest-agent/
|
|
1
|
+
import { formatWrapupEffect } from "@vitest-agent/engine";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
@@ -13,17 +13,17 @@ import { Command, Flag } from "effect/unstable/cli";
|
|
|
13
13
|
*
|
|
14
14
|
* @packageDocumentation
|
|
15
15
|
*/
|
|
16
|
-
const rowIdOption = Flag.optional(Flag.
|
|
17
|
-
const chatIdOption = Flag.optional(Flag.
|
|
18
|
-
const kindOption = Flag.withDefault(Flag.
|
|
16
|
+
const rowIdOption = Flag.optional(Flag.Int("row-id"));
|
|
17
|
+
const chatIdOption = Flag.optional(Flag.String("chat-id"));
|
|
18
|
+
const kindOption = Flag.withDefault(Flag.Literals("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 = Flag.optional(Flag.
|
|
26
|
-
const formatOption = Flag.withDefault(Flag.
|
|
25
|
+
const userPromptHintOption = Flag.optional(Flag.String("user-prompt-hint"));
|
|
26
|
+
const formatOption = Flag.withDefault(Flag.Literals("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,167 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
|
|
3
|
-
import { Effect, Layer, LogLevel } from "effect";
|
|
4
|
-
import { DataReader, DataStore, PerClientSessionMapWriter, ProjectIdentityNotResolvableError } from "@vitest-agent/sdk";
|
|
5
|
-
//#region src/layers/CliLive.d.ts
|
|
6
|
-
/**
|
|
7
|
-
* Composition layer for the CLI runtime.
|
|
8
|
-
*
|
|
9
|
-
* Wires `DataReader`, `ProjectDiscovery`, `HistoryTracker`,
|
|
10
|
-
* `OutputPipeline`, `SqliteClient`, the DB migrator, the Node platform
|
|
11
|
-
* services, and `Logger` into a single layer the `vitest-agent`
|
|
12
|
-
* bin provides to `Command.run`.
|
|
13
|
-
*
|
|
14
|
-
* @param dbPath - absolute path to the per-project `data.db`
|
|
15
|
-
* @param logLevel - optional log level override; defaults to `Info`
|
|
16
|
-
* @param logFile - optional path for structured log output
|
|
17
|
-
* @public
|
|
18
|
-
*/
|
|
19
|
-
export 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
|
-
//#endregion
|
|
21
|
-
//#region src/layers/SidecarLive.d.ts
|
|
22
|
-
/**
|
|
23
|
-
* SQLite database paths consumed by {@link SidecarLive}.
|
|
24
|
-
*
|
|
25
|
-
* @public
|
|
26
|
-
*/
|
|
27
|
-
interface SidecarPaths {
|
|
28
|
-
/** Absolute path to the per-project `data.db`. */
|
|
29
|
-
readonly perProjectDbPath: string;
|
|
30
|
-
/** Absolute path to the per-client `sessions.db`. */
|
|
31
|
-
readonly sessionMapDbPath: string;
|
|
32
|
-
/** Absolute path to the global `registry.db`. */
|
|
33
|
-
readonly registryDbPath: string;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Build the sidecar Live layer for the supplied SQLite paths.
|
|
37
|
-
*
|
|
38
|
-
* Each store gets its own `SqlClient` connection (separate scopes,
|
|
39
|
-
* independent migrators) so concurrent operations on the three
|
|
40
|
-
* stores don't share lock state.
|
|
41
|
-
*
|
|
42
|
-
* @param paths - the three SQLite database paths to open
|
|
43
|
-
* @public
|
|
44
|
-
*/
|
|
45
|
-
export 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
|
-
//#endregion
|
|
47
|
-
//#region src/lib/internal-register-agent.d.ts
|
|
48
|
-
/**
|
|
49
|
-
* Input for the end-to-end agent registration effect.
|
|
50
|
-
*
|
|
51
|
-
* @public
|
|
52
|
-
*/
|
|
53
|
-
interface RegisterAgentInput {
|
|
54
|
-
/** The host's native session identifier (e.g. Claude's `session_id`). */
|
|
55
|
-
readonly hostSessionId: string;
|
|
56
|
-
/** Absolute path to the host's conversation transcript file. */
|
|
57
|
-
readonly transcriptPath: string;
|
|
58
|
-
/** Working directory of the agent process. */
|
|
59
|
-
readonly cwd: string;
|
|
60
|
-
/** Host kind string (e.g. `"claude-code"`). */
|
|
61
|
-
readonly hostKind: string;
|
|
62
|
-
/** Agent type label (e.g. `"main"` or `"subagent"`). */
|
|
63
|
-
readonly agentType: string;
|
|
64
|
-
/** Normalized project key derived from the workspace root `package.json#name`. */
|
|
65
|
-
readonly projectKey: string;
|
|
66
|
-
/** Agent ID of the parent when registering a subagent. */
|
|
67
|
-
readonly parentAgentId?: string;
|
|
68
|
-
/** Optional idempotency nonce; derived from `hostSessionId + agentType + parentAgentId` when omitted. */
|
|
69
|
-
readonly clientNonce?: string;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Output of the end-to-end agent registration effect.
|
|
73
|
-
*
|
|
74
|
-
* @public
|
|
75
|
-
*/
|
|
76
|
-
interface RegisterAgentOutput {
|
|
77
|
-
/** Canonical UUID assigned to this agent in the per-project store. */
|
|
78
|
-
readonly agentId: string;
|
|
79
|
-
/** UUID of the conversation this agent belongs to. */
|
|
80
|
-
readonly conversationId: string;
|
|
81
|
-
/** Idempotency key used for the `registerAgent` upsert. */
|
|
82
|
-
readonly idempotencyKey: string;
|
|
83
|
-
/** `true` when an existing agent row was recovered rather than inserted. */
|
|
84
|
-
readonly idempotencyHit: boolean;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* End-to-end agent registration.
|
|
88
|
-
*
|
|
89
|
-
* Returns the canonical `agentId` + the conversation it belongs to +
|
|
90
|
-
* the resolved `idempotencyKey`. The `idempotencyHit` flag is `true`
|
|
91
|
-
* when the per-project store recovered an existing agent row instead
|
|
92
|
-
* of inserting a new one.
|
|
93
|
-
*
|
|
94
|
-
* @param input - registration inputs including host identifiers and agent metadata
|
|
95
|
-
* @returns an Effect resolving to `RegisterAgentOutput`
|
|
96
|
-
* @public
|
|
97
|
-
*/
|
|
98
|
-
export declare const registerAgentEffect: (input: RegisterAgentInput) => Effect.Effect<{
|
|
99
|
-
agentId: string;
|
|
100
|
-
conversationId: string;
|
|
101
|
-
mainAgentId: string;
|
|
102
|
-
idempotencyKey: string;
|
|
103
|
-
idempotencyHit: boolean;
|
|
104
|
-
}, import("@vitest-agent/sdk").DataStoreError | import("@vitest-agent/sdk").RegistrationConflictError, DataReader | DataStore | PerClientSessionMapWriter | import("@vitest-agent/sdk").RunContext$>;
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/lib/sidecar-paths.d.ts
|
|
107
|
-
/**
|
|
108
|
-
* Filename of the per-project test-data SQLite database.
|
|
109
|
-
*
|
|
110
|
-
* @public
|
|
111
|
-
*/
|
|
112
|
-
export declare const DATA_DB_FILENAME = "data.db";
|
|
113
|
-
/**
|
|
114
|
-
* Filename of the per-client session-map SQLite database.
|
|
115
|
-
*
|
|
116
|
-
* @public
|
|
117
|
-
*/
|
|
118
|
-
export declare const SESSIONS_DB_FILENAME = "sessions.db";
|
|
119
|
-
/**
|
|
120
|
-
* Filename of the global discovery-registry SQLite database.
|
|
121
|
-
*
|
|
122
|
-
* @public
|
|
123
|
-
*/
|
|
124
|
-
export declare const REGISTRY_DB_FILENAME = "registry.db";
|
|
125
|
-
/**
|
|
126
|
-
* Resolve (and create) the per-project data directory for the supplied
|
|
127
|
-
* normalized `projectKey`. Returns the directory; callers join
|
|
128
|
-
* {@link DATA_DB_FILENAME} onto it for the `data.db` path.
|
|
129
|
-
*
|
|
130
|
-
* @param projectKey - normalized project key (e.g. `@org__pkg`)
|
|
131
|
-
* @returns absolute path to the resolved (and created) data directory
|
|
132
|
-
* @public
|
|
133
|
-
*/
|
|
134
|
-
export declare const resolveProjectDataDir: (projectKey: string) => string;
|
|
135
|
-
/**
|
|
136
|
-
* Resolve (and create) the directory holding the global `registry.db`.
|
|
137
|
-
* Callers join {@link REGISTRY_DB_FILENAME} onto it.
|
|
138
|
-
*
|
|
139
|
-
* @returns absolute path to the resolved (and created) registry directory
|
|
140
|
-
* @public
|
|
141
|
-
*/
|
|
142
|
-
export declare const resolveRegistryDir: () => string;
|
|
143
|
-
/**
|
|
144
|
-
* Resolve the per-client `sessions.db` path.
|
|
145
|
-
*
|
|
146
|
-
* Precedence: the `CLAUDE_PLUGIN_DATA` env var, then
|
|
147
|
-
* `VITEST_AGENT_SESSION_MAP_DIR`, then `~/.vitest-agent/`. Fails
|
|
148
|
-
* with `ProjectIdentityNotResolvableError` when no home directory
|
|
149
|
-
* is resolvable.
|
|
150
|
-
*
|
|
151
|
-
* @returns an Effect resolving to the absolute `sessions.db` path
|
|
152
|
-
* @public
|
|
153
|
-
*/
|
|
154
|
-
export declare const resolveSessionMapPath: () => Effect.Effect<string, ProjectIdentityNotResolvableError>;
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/index.d.ts
|
|
1
|
+
//#region src/version.d.ts
|
|
157
2
|
/**
|
|
158
3
|
* The version of this package, inlined at build time from
|
|
159
4
|
* `package.json#version` via rslib-builder's `__PACKAGE_VERSION__` substitution.
|
|
160
|
-
* Exported for version introspection by downstream tooling
|
|
5
|
+
* Exported for version introspection by downstream tooling, and consumed by
|
|
6
|
+
* `main.ts` to back `Command.run`'s `version` option.
|
|
161
7
|
*
|
|
162
8
|
* @public
|
|
163
9
|
*/
|
|
164
10
|
export declare const CURRENT_CLI_VERSION: string;
|
|
165
11
|
//#endregion
|
|
166
|
-
export type { RegisterAgentInput, RegisterAgentOutput, SidecarPaths };
|
|
167
12
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SidecarLive } from "./layers/SidecarLive.js";
|
|
3
|
-
import { registerAgentEffect } from "./lib/internal-register-agent.js";
|
|
4
|
-
import { DATA_DB_FILENAME, REGISTRY_DB_FILENAME, SESSIONS_DB_FILENAME, resolveProjectDataDir, resolveRegistryDir, resolveSessionMapPath } from "./lib/sidecar-paths.js";
|
|
1
|
+
import { CURRENT_CLI_VERSION } from "./version.js";
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The version of this package, inlined at build time from
|
|
9
|
-
* `package.json#version` via rslib-builder's `__PACKAGE_VERSION__` substitution.
|
|
10
|
-
* Exported for version introspection by downstream tooling.
|
|
11
|
-
*
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
const CURRENT_CLI_VERSION = "2.2.18";
|
|
15
|
-
|
|
16
|
-
//#endregion
|
|
17
|
-
export { CURRENT_CLI_VERSION, CliLive, DATA_DB_FILENAME, REGISTRY_DB_FILENAME, SESSIONS_DB_FILENAME, SidecarLive, registerAgentEffect, resolveProjectDataDir, resolveRegistryDir, resolveSessionMapPath };
|
|
3
|
+
export { CURRENT_CLI_VERSION };
|
package/main.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/main.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Assembled CLI program. Owns the process: resolves the data path, wires the
|
|
4
|
+
* engine's platform layers, and runs the root command via
|
|
5
|
+
* `NodeRuntime.runMain`.
|
|
6
|
+
*
|
|
7
|
+
* `bin.ts` is the published bin shim (`#!/usr/bin/env node` +
|
|
8
|
+
* `main()`); this module is also published as the `./main` subpath so a
|
|
9
|
+
* consumer can invoke the assembled program without going through the shim.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Assembles and runs the CLI program, taking over the process. Not
|
|
15
|
+
* re-exported from `index.ts` — a library consumer's import graph must not
|
|
16
|
+
* pull in the process-owning module.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export declare const main: () => void;
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=main.d.ts.map
|