@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.
@@ -1,34 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { CliLive } from "../layers/CliLive.js";
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
- const rootCommand = Command.make("vitest-agent").pipe(Command.withSubcommands([
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 { classifyTestPath, detectNonDefaultDiscoverStrategy, findOwningWorkspace, resolveProjectKeyFromCwd } from "@vitest-agent/sdk";
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.string("host-kind").pipe(Flag.withDescription("Host vendor identifier; e.g. 'claude-code', 'cursor', 'goose'"));
70
- const agentTypeOpt = Flag.string("agent-type").pipe(Flag.withDescription("Agent type, must begin with the host-kind prefix"));
71
- 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)"));
72
- const transcriptPathOpt = Flag.string("transcript-path").pipe(Flag.withDescription("Path to the host's transcript file (basename UUID is the conversation key)"));
73
- const cwdOpt = Flag.string("cwd").pipe(Flag.withDescription("Workspace root directory the agent is running in"));
74
- const parentAgentIdOpt = Flag.optional(Flag.string("parent-agent-id"));
75
- const clientNonceOpt = Flag.optional(Flag.string("client-nonce"));
76
- const projectKeyOverrideOpt = Flag.optional(Flag.string("project-key"));
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 perProjectDbPath = join(resolveProjectDataDir(projectKey), DATA_DB_FILENAME);
89
- const registryDbPath = join(resolveRegistryDir(), REGISTRY_DB_FILENAME);
90
- const sessionMapDbPath = yield* resolveSessionMapPath().pipe(Effect.catchCause(mapDefectToExit));
91
- const sidecar = SidecarLive({
92
- perProjectDbPath,
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.string("agent-id").pipe(Flag.withDescription("The agent_id (UUID) returned by an earlier register-agent call"));
115
- const endedAtOpt = Flag.optional(Flag.integer("ended-at"));
116
- const endHostSessionIdOpt = Flag.optional(Flag.string("host-session-id"));
117
- const endCwdOpt = Flag.string("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Workspace root, used to locate the per-project data.db"));
118
- const endProjectKeyOverrideOpt = Flag.optional(Flag.string("project-key"));
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 perProjectDbPath = join(resolveProjectDataDir(projectKey), DATA_DB_FILENAME);
128
- const registryDbPath = join(resolveRegistryDir(), REGISTRY_DB_FILENAME);
129
- const sessionMapDbPath = yield* resolveSessionMapPath().pipe(Effect.catchCause(mapDefectToExit));
130
- const sidecar = SidecarLive({
131
- perProjectDbPath,
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.string("command").pipe(Flag.withDescription("The Bash command to (possibly) rewrite with VITEST_AGENT_* env-prefix"));
143
- const cwdInjectOpt = Flag.string("cwd").pipe(Flag.withDefault(process.cwd()), Flag.withDescription("Working directory; used to find package.json scripts"));
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.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"));
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 { layer } from "@effect/sql-sqlite-node/SqliteClient";
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.integer("keep-recent"), 30).pipe(Flag.withDescription("Number of most-recent sessions to keep in full"));
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.boolean("yes").pipe(Flag.withDefault(false), Flag.withDescription("Skip the interactive confirmation prompt"));
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.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"));
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
@@ -1,5 +1,5 @@
1
1
  import { formatDoctor } from "../lib/format-doctor.js";
2
- import { DataReader, resolveDataPath } from "@vitest-agent/sdk";
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.choice("format", ["markdown", "json"]), "markdown");
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`);
@@ -1,19 +1,15 @@
1
- import { recordSessionEnd, recordSessionStart } from "../lib/record-session.js";
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.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"));
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
- ...cwd._tag === "Some" && { cwd: cwd.value }
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.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()));
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.string("ended-at").pipe(Flag.withDefault((/* @__PURE__ */ new Date()).toISOString()));
61
- const endReason = Flag.optional(Flag.string("end-reason"));
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.choice("artifact-kind", [
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.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()));
88
- const chatIdOptional = Flag.optional(Flag.string("chat-id")).pipe(Flag.withDescription("Host chat id; omit when --tdd-task-id is supplied instead"));
89
- const tddTaskIdOpt = Flag.optional(Flag.integer("tdd-task-id")).pipe(Flag.withDescription("Explicit TDD task id escape hatch (issue #144): bypasses chat-id -> session -> task resolution entirely"));
90
- const suiteOpt = Flag.choice("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."));
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
- ...opts.cwd._tag === "Some" && { cwd: opts.cwd.value },
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.choice("invocation-method", [
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.string("sha");
156
- const parentShaOpt = Flag.optional(Flag.string("parent-sha"));
157
- const messageOpt = Flag.optional(Flag.string("message"));
158
- const authorOpt = Flag.optional(Flag.string("author"));
159
- const committedAtOpt = Flag.optional(Flag.string("committed-at"));
160
- const branchOpt = Flag.optional(Flag.string("branch"));
161
- const projectOpt = Flag.optional(Flag.string("project"));
162
- const filesArg = Argument.string("files-json").pipe(Argument.withDescription("JSON array of {\"filePath\",\"changeKind\"} objects"));
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,
@@ -1,4 +1,4 @@
1
- import { formatTriageEffect } from "@vitest-agent/sdk";
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.choice("format", [
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.string("project"));
21
- const maxLinesOption = Flag.optional(Flag.integer("max-lines"));
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,
@@ -1,4 +1,4 @@
1
- import { formatWrapupEffect } from "@vitest-agent/sdk";
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.integer("row-id"));
17
- const chatIdOption = Flag.optional(Flag.string("chat-id"));
18
- const kindOption = Flag.withDefault(Flag.choice("kind", [
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.string("user-prompt-hint"));
26
- const formatOption = Flag.withDefault(Flag.choice("format", ["markdown", "json"]), "markdown");
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
- import * as NodeServices from "@effect/platform-node/NodeServices";
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 { CliLive } from "./layers/CliLive.js";
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
- //#region src/index.ts
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