carrick 0.3.103 → 0.3.104

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carrick",
3
- "version": "0.3.103",
3
+ "version": "0.3.104",
4
4
  "description": "Maps your entire TypeScript codebase across services and repositories, giving AI agents full context on existing types, routes, and function behaviours over MCP before they write duplicate or breaking code.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -58,11 +58,11 @@
58
58
  "zod": "^3.23.0"
59
59
  },
60
60
  "optionalDependencies": {
61
- "@carrick-tools/cli-darwin-arm64": "0.3.103",
62
- "@carrick-tools/cli-darwin-x64": "0.3.103",
63
- "@carrick-tools/cli-linux-arm64": "0.3.103",
64
- "@carrick-tools/cli-linux-x64": "0.3.103",
65
- "@carrick-tools/cli-win32-x64": "0.3.103"
61
+ "@carrick-tools/cli-darwin-arm64": "0.3.104",
62
+ "@carrick-tools/cli-darwin-x64": "0.3.104",
63
+ "@carrick-tools/cli-linux-arm64": "0.3.104",
64
+ "@carrick-tools/cli-linux-x64": "0.3.104",
65
+ "@carrick-tools/cli-win32-x64": "0.3.104"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/node": "^24.13.3",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "carrick",
3
3
  "description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
4
- "version": "0.3.103",
4
+ "version": "0.3.104",
5
5
  "author": {
6
6
  "name": "Carrick",
7
7
  "email": "hello@carrick.tools"
@@ -292,6 +292,12 @@ export interface CaptureStubOptions {
292
292
  /** Directory the stub package is written into (created if missing). */
293
293
  outDir: string;
294
294
  tsconfigPath?: string;
295
+ /**
296
+ * The scanned repo's root, the upper bound of the search for a tsconfig
297
+ * above `repoRoot` when none is named (carrick#1776). Without it only
298
+ * `repoRoot` is searched.
299
+ */
300
+ scanRoot?: string;
295
301
  }
296
302
  /** Wire protocol of a matched pair (drives the direction table). */
297
303
  export type ProbeProtocol = 'http' | 'graphql' | 'socket' | 'pubsub';
@@ -171,6 +171,29 @@ export function classifyPair(input) {
171
171
  ...notAFact(`the ${side} sends a form-encoded body`, side),
172
172
  };
173
173
  }
174
+ // 4c. A body of bytes (carrick#1793): a blob, a buffer or a stream has no JSON
175
+ // shape, so a MISMATCH between the containers two sides hold the bytes
176
+ // in (a `Uint8Array` sent, read with `.blob()`) is not a drift. It
177
+ // overrides a mismatch only: bytes that assign to bytes (a stream sent
178
+ // where a stream is read) agree, and that verdict stands. When both
179
+ // sides are bytes the sent side is named, whatever order the
180
+ // diagnostics came in.
181
+ const firedGates = new Set(gateDiags.map((d) => plan.gateLines.get(d.line)));
182
+ const bytesGate = firedGates.has('sent:bytes')
183
+ ? 'sent:bytes'
184
+ : firedGates.has('expected:bytes')
185
+ ? 'expected:bytes'
186
+ : undefined;
187
+ const bytesVerdict = () => {
188
+ const { side } = sideForGate(bytesGate, plan);
189
+ return {
190
+ ...base,
191
+ bucket: 'unverifiable',
192
+ gate: `${side}:bytes`,
193
+ diagnostic: `the ${side} body is bytes (a blob, a buffer or a stream), which has no JSON shape to compare with the other side.`,
194
+ ...notAFact(`the ${side} body is bytes`, side),
195
+ };
196
+ };
174
197
  // 5. Assignment-class error on the DECISIVE assignment line -> incompatible.
175
198
  //
176
199
  // On an `http` pair that line is the JSON wire assignment, not the declared
@@ -183,6 +206,8 @@ export function classifyPair(input) {
183
206
  const decisiveLine = decisiveAssignmentLine(plan);
184
207
  const assignDiag = probeDiags.find((d) => d.line === decisiveLine && ASSIGNMENT_CODES.has(d.code));
185
208
  if (assignDiag) {
209
+ if (bytesGate)
210
+ return bytesVerdict();
186
211
  const text = scrubDiagnostic(assignDiag.message, scrubCtx, plan.sentEndpoint.alias, plan.expectedEndpoint.alias);
187
212
  return {
188
213
  ...base,
@@ -218,6 +243,8 @@ export function classifyPair(input) {
218
243
  if (wireOther) {
219
244
  const declaredMismatch = probeDiags.find((d) => d.line === plan.assignmentLine && ASSIGNMENT_CODES.has(d.code));
220
245
  if (declaredMismatch) {
246
+ if (bytesGate)
247
+ return bytesVerdict();
221
248
  return {
222
249
  ...base,
223
250
  bucket: 'incompatible',
@@ -54,7 +54,7 @@ export declare function fnv1a(input: string): string;
54
54
  * path, so it is byte-stable across runs. */
55
55
  export declare function pairId(spec: CheckPairSpec): string;
56
56
  /** Names each gate line so a TS2344 can be attributed to a specific side+kind. */
57
- export type GateName = 'sent:any' | 'sent:unknown' | 'sent:never' | 'sent:void' | 'sent:form' | 'expected:any' | 'expected:unknown' | 'expected:never' | 'expected:void';
57
+ export type GateName = 'sent:any' | 'sent:unknown' | 'sent:never' | 'sent:void' | 'sent:form' | 'sent:bytes' | 'expected:any' | 'expected:unknown' | 'expected:never' | 'expected:void' | 'expected:bytes';
58
58
  export interface ProbePlan {
59
59
  pairId: string;
60
60
  spec: CheckPairSpec;
@@ -150,6 +150,20 @@ export function buildProbe(spec, packageOf) {
150
150
  push(`type IsFormBody<T> = 0 extends 1 & T ? false : [T] extends [never] ? false : [T] extends [FormData | URLSearchParams] ? true : false;`);
151
151
  gateLines.set(push(`type _G_sent_form = Assert<Not<IsFormBody<Sent>>>;`), 'sent:form');
152
152
  }
153
+ // carrick#1793: a body of bytes (a blob, a buffer, a typed array, a stream)
154
+ // has no JSON shape. The wire carries the bytes whatever either side holds
155
+ // them in, so a route sending a `Uint8Array` read with `.blob()` is not a
156
+ // mismatch, and neither is the same route read as text. Either side, either
157
+ // half: a file upload states no fields a declared body could be compared
158
+ // with. The classifier lets this gate override a mismatch only, so bytes
159
+ // that assign to bytes still read compatible. `null`/`undefined` are
160
+ // dropped first, so `Blob | null` still gates, while a bare `null`,
161
+ // `undefined` or `never` leaves nothing to test.
162
+ if (spec.protocol === 'http') {
163
+ push(`type IsByteBody<T> = 0 extends 1 & T ? false : [NonNullable<T>] extends [never] ? false : [NonNullable<T>] extends [Blob | ArrayBuffer | SharedArrayBuffer | ArrayBufferView | ReadableStream<any>] ? true : false;`);
164
+ gateLines.set(push(`type _G_sent_bytes = Assert<Not<IsByteBody<Sent>>>;`), 'sent:bytes');
165
+ gateLines.set(push(`type _G_expected_bytes = Assert<Not<IsByteBody<Expected>>>;`), 'expected:bytes');
166
+ }
153
167
  push(`declare const sent: Sent;`);
154
168
  let assignmentLine;
155
169
  if (spec.protocol === 'graphql') {
@@ -31,6 +31,7 @@ import type { CaptureStubOptions, CaptureStubResult } from './api.js';
31
31
  export type { CaptureStubOptions, CaptureStubResult } from './api.js';
32
32
  export { DenoProject, findDenoConfig } from './deno-project.js';
33
33
  export { serviceConfigPath } from './project-references.js';
34
+ export { findServiceTsconfig } from './service-config.js';
34
35
  export { runCheck } from './check.js';
35
36
  export type { CheckProgress } from './check.js';
36
37
  export { jsonWireDeclarations } from './check-probe.js';
@@ -39,10 +39,12 @@ import { selfCheckStub } from './self-check.js';
39
39
  import { collectSpecifiers, isRelative, packageNameOf } from './specifiers.js';
40
40
  import { DenoProject, findDenoConfig } from './deno-project.js';
41
41
  import { emitsAlike, ProjectGraph } from './project-references.js';
42
+ import { findServiceTsconfig } from './service-config.js';
42
43
  import { placeEmittedTree } from './outside-root.js';
43
44
  import { WriteGuard } from './guarded-fs.js';
44
45
  export { DenoProject, findDenoConfig } from './deno-project.js';
45
46
  export { serviceConfigPath } from './project-references.js';
47
+ export { findServiceTsconfig } from './service-config.js';
46
48
  // v2 check core ("tsc as the judge"). Same bundle, same seam: the sidecar
47
49
  // reaches it only through this door (index.js).
48
50
  export { runCheck } from './check.js';
@@ -127,9 +129,11 @@ export function captureStub(opts) {
127
129
  const packageName = `@carrick/${sanitizeServiceName(opts.serviceName)}`;
128
130
  const stubDir = path.resolve(opts.outDir);
129
131
  const errors = [];
132
+ // The named config, else the one the init'd project reads too
133
+ // (carrick#1776). A service with neither is typed under defaults below.
130
134
  const configPath = opts.tsconfigPath
131
135
  ? path.resolve(repoRoot, opts.tsconfigPath)
132
- : path.join(repoRoot, 'tsconfig.json');
136
+ : findServiceTsconfig(repoRoot, opts.scanRoot) ?? path.join(repoRoot, 'tsconfig.json');
133
137
  let parsed;
134
138
  // The config the emit's options came from: the named one, or the project
135
139
  // that owns the most anchors' files (carrick#1604).
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Which tsconfig types a service that names none (carrick#1776).
3
+ *
4
+ * Both readers of a service, the init'd project and capture, ask this one
5
+ * function, so they type the service under the same options.
6
+ *
7
+ * The service root is searched first, for the names a service keeps its own
8
+ * config under. Above it, the search is `tsc`'s own when it is run with no
9
+ * `-p`: the nearest `tsconfig.json` in an ancestor directory. A layout that
10
+ * keeps one config above several services (their module resolution, `paths`,
11
+ * `lib`) is typed under that config, as the repo's own `tsc` types it; without
12
+ * it, every import only that config resolves read `any`.
13
+ *
14
+ * The walk stops at the scan root, inclusive: a config outside the scanned
15
+ * tree is never read. With no scan root, or a service outside it, only the
16
+ * service root is searched.
17
+ */
18
+ /** Absolute path of the config that types the service, or undefined for none. */
19
+ export declare function findServiceTsconfig(serviceRoot: string, scanRoot?: string): string | undefined;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Which tsconfig types a service that names none (carrick#1776).
3
+ *
4
+ * Both readers of a service, the init'd project and capture, ask this one
5
+ * function, so they type the service under the same options.
6
+ *
7
+ * The service root is searched first, for the names a service keeps its own
8
+ * config under. Above it, the search is `tsc`'s own when it is run with no
9
+ * `-p`: the nearest `tsconfig.json` in an ancestor directory. A layout that
10
+ * keeps one config above several services (their module resolution, `paths`,
11
+ * `lib`) is typed under that config, as the repo's own `tsc` types it; without
12
+ * it, every import only that config resolves read `any`.
13
+ *
14
+ * The walk stops at the scan root, inclusive: a config outside the scanned
15
+ * tree is never read. With no scan root, or a service outside it, only the
16
+ * service root is searched.
17
+ */
18
+ import * as fs from 'node:fs';
19
+ import * as path from 'node:path';
20
+ /** The names a service root is searched for, in order. */
21
+ const SERVICE_ROOT_NAMES = ['tsconfig.json', 'tsconfig.build.json', 'tsconfig.app.json'];
22
+ /** The name an ancestor directory is searched for, as `tsc` searches. */
23
+ const ANCESTOR_NAME = 'tsconfig.json';
24
+ /** Absolute path of the config that types the service, or undefined for none. */
25
+ export function findServiceTsconfig(serviceRoot, scanRoot) {
26
+ const root = path.resolve(serviceRoot);
27
+ for (const name of SERVICE_ROOT_NAMES) {
28
+ const candidate = path.join(root, name);
29
+ if (fs.existsSync(candidate))
30
+ return candidate;
31
+ }
32
+ if (scanRoot === undefined)
33
+ return undefined;
34
+ // Containment is decided on real paths, so a symlink on either end cannot
35
+ // hide the service inside the scan root or place it outside; the walk then
36
+ // climbs the path as given, one directory per level between them.
37
+ const fromBound = path.relative(realPath(scanRoot), realPath(root));
38
+ if (fromBound === '' || fromBound.startsWith('..') || path.isAbsolute(fromBound))
39
+ return undefined;
40
+ let dir = root;
41
+ for (let level = fromBound.split(path.sep).length; level > 0; level--) {
42
+ dir = path.dirname(dir);
43
+ const candidate = path.join(dir, ANCESTOR_NAME);
44
+ if (fs.existsSync(candidate))
45
+ return candidate;
46
+ }
47
+ return undefined;
48
+ }
49
+ /** The path with every symlink resolved; the path as given when it does not exist. */
50
+ function realPath(p) {
51
+ try {
52
+ return fs.realpathSync(p);
53
+ }
54
+ catch {
55
+ return path.resolve(p);
56
+ }
57
+ }
@@ -125,6 +125,7 @@ function handleInit(request) {
125
125
  projectLoader = new ProjectLoader({
126
126
  repoRoot: request.repo_root,
127
127
  tsconfigPath: request.tsconfig_path,
128
+ scanRoot: request.scan_root,
128
129
  tsconfigSnapshot: request.tsconfig_snapshot,
129
130
  pinnedDependencies: request.pinned_dependencies,
130
131
  });
@@ -213,6 +214,7 @@ function handleCaptureV2(request) {
213
214
  anchors: request.anchors,
214
215
  outDir: request.out_dir,
215
216
  tsconfigPath: request.tsconfig_path,
217
+ scanRoot: request.scan_root,
216
218
  });
217
219
  return {
218
220
  request_id: request.request_id,
@@ -19,6 +19,12 @@ export interface ProjectLoaderOptions {
19
19
  repoRoot: string;
20
20
  /** Optional path to tsconfig.json (relative to repo root or absolute) */
21
21
  tsconfigPath?: string;
22
+ /**
23
+ * The scanned repo's root, the upper bound of the search for a tsconfig
24
+ * above `repoRoot` when none is named (carrick#1776). Without it only
25
+ * `repoRoot` is searched.
26
+ */
27
+ scanRoot?: string;
22
28
  /** Optional tsconfig snapshot (closed/merged) - preferred over tsconfigPath */
23
29
  tsconfigSnapshot?: TsconfigSnapshot;
24
30
  /** Optional pinned dependencies for this repo */
@@ -69,6 +75,7 @@ export declare class ProjectLoader {
69
75
  private readonly ownerProjects;
70
76
  private readonly repoRoot;
71
77
  private readonly tsconfigPath;
78
+ private readonly scanRoot;
72
79
  private readonly tsconfigSnapshot;
73
80
  private readonly pinnedDependencies;
74
81
  private initialized;
@@ -140,9 +147,10 @@ export declare class ProjectLoader {
140
147
  */
141
148
  getPinnedDependencies(): PinnedDependencySnapshot | undefined;
142
149
  /**
143
- * Find the tsconfig.json file to use
150
+ * The tsconfig to build from: the named one when it exists, else the one
151
+ * `findServiceTsconfig` finds for the service, which capture also reads.
144
152
  *
145
- * @returns Absolute path to tsconfig.json, or undefined if not found
153
+ * @returns Absolute path to the tsconfig, or undefined if not found
146
154
  */
147
155
  private findTsConfig;
148
156
  /**
@@ -12,7 +12,7 @@
12
12
  import { Project } from 'ts-morph';
13
13
  import * as path from 'node:path';
14
14
  import * as fs from 'node:fs';
15
- import { DenoProject, findDenoConfig, serviceConfigPath } from './capture/index.js';
15
+ import { DenoProject, findDenoConfig, findServiceTsconfig, serviceConfigPath } from './capture/index.js';
16
16
  import { moduleFormatResolutionHost } from './module-format.js';
17
17
  import { ExternalImports, registerExternalImports } from './origin.js';
18
18
  /**
@@ -125,6 +125,7 @@ export class ProjectLoader {
125
125
  ownerProjects = new Map();
126
126
  repoRoot;
127
127
  tsconfigPath;
128
+ scanRoot;
128
129
  tsconfigSnapshot;
129
130
  pinnedDependencies;
130
131
  initialized = false;
@@ -141,6 +142,7 @@ export class ProjectLoader {
141
142
  ? options.tsconfigPath
142
143
  : path.resolve(this.repoRoot, options.tsconfigPath);
143
144
  }
145
+ this.scanRoot = options.scanRoot;
144
146
  // Store snapshot if provided
145
147
  this.tsconfigSnapshot = options.tsconfigSnapshot;
146
148
  this.pinnedDependencies = options.pinnedDependencies;
@@ -422,30 +424,19 @@ export class ProjectLoader {
422
424
  return this.pinnedDependencies;
423
425
  }
424
426
  /**
425
- * Find the tsconfig.json file to use
427
+ * The tsconfig to build from: the named one when it exists, else the one
428
+ * `findServiceTsconfig` finds for the service, which capture also reads.
426
429
  *
427
- * @returns Absolute path to tsconfig.json, or undefined if not found
430
+ * @returns Absolute path to the tsconfig, or undefined if not found
428
431
  */
429
432
  findTsConfig() {
430
- // If a specific path was provided, try to use it
431
433
  if (this.tsconfigPath) {
432
434
  if (fs.existsSync(this.tsconfigPath)) {
433
435
  return this.tsconfigPath;
434
436
  }
435
437
  this.log(`Specified tsconfig not found: ${this.tsconfigPath}`);
436
438
  }
437
- // Try common tsconfig locations
438
- const candidates = [
439
- path.join(this.repoRoot, 'tsconfig.json'),
440
- path.join(this.repoRoot, 'tsconfig.build.json'),
441
- path.join(this.repoRoot, 'tsconfig.app.json'),
442
- ];
443
- for (const candidate of candidates) {
444
- if (fs.existsSync(candidate)) {
445
- return candidate;
446
- }
447
- }
448
- return undefined;
439
+ return findServiceTsconfig(this.repoRoot, this.scanRoot);
449
440
  }
450
441
  /**
451
442
  * Add source files from common project locations when no tsconfig is found.
@@ -110,6 +110,12 @@ export interface InitRequest extends BaseRequest {
110
110
  action: 'init';
111
111
  repo_root: string;
112
112
  tsconfig_path?: string;
113
+ /**
114
+ * The scanned repo's root: with no tsconfig named or in `repo_root`, the
115
+ * nearest `tsconfig.json` above `repo_root` up to this root types the
116
+ * service (carrick#1776). Without it only `repo_root` is searched.
117
+ */
118
+ scan_root?: string;
113
119
  /** Optional tsconfig snapshot (closed/merged) - preferred over tsconfig_path */
114
120
  tsconfig_snapshot?: TsconfigSnapshot;
115
121
  /** Optional pinned dependencies for this repo */
@@ -141,6 +147,8 @@ export interface CaptureV2Request extends BaseRequest {
141
147
  out_dir: string;
142
148
  /** Optional explicit tsconfig path (defaults to <repo_root>/tsconfig.json) */
143
149
  tsconfig_path?: string;
150
+ /** The scanned repo's root, as on `init` (carrick#1776). */
151
+ scan_root?: string;
144
152
  }
145
153
  /**
146
154
  * Response for the capture_v2 action
@@ -69,6 +69,7 @@ export declare const InitRequestSchema: z.ZodObject<{
69
69
  action: z.ZodLiteral<"init">;
70
70
  repo_root: z.ZodString;
71
71
  tsconfig_path: z.ZodOptional<z.ZodString>;
72
+ scan_root: z.ZodOptional<z.ZodString>;
72
73
  tsconfig_snapshot: z.ZodOptional<z.ZodObject<{
73
74
  compilerOptions: z.ZodObject<{
74
75
  module: z.ZodOptional<z.ZodString>;
@@ -161,6 +162,7 @@ export declare const InitRequestSchema: z.ZodObject<{
161
162
  action: "init";
162
163
  repo_root: string;
163
164
  tsconfig_path?: string | undefined;
165
+ scan_root?: string | undefined;
164
166
  tsconfig_snapshot?: {
165
167
  compilerOptions: {
166
168
  module?: string | undefined;
@@ -187,6 +189,7 @@ export declare const InitRequestSchema: z.ZodObject<{
187
189
  action: "init";
188
190
  repo_root: string;
189
191
  tsconfig_path?: string | undefined;
192
+ scan_root?: string | undefined;
190
193
  tsconfig_snapshot?: {
191
194
  compilerOptions: {
192
195
  module?: string | undefined;
@@ -344,6 +347,7 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
344
347
  }>]>, "many">;
345
348
  out_dir: z.ZodString;
346
349
  tsconfig_path: z.ZodOptional<z.ZodString>;
350
+ scan_root: z.ZodOptional<z.ZodString>;
347
351
  }, "strip", z.ZodTypeAny, {
348
352
  request_id: string;
349
353
  action: "capture_v2";
@@ -381,6 +385,7 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
381
385
  })[];
382
386
  out_dir: string;
383
387
  tsconfig_path?: string | undefined;
388
+ scan_root?: string | undefined;
384
389
  }, {
385
390
  request_id: string;
386
391
  action: "capture_v2";
@@ -418,6 +423,7 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
418
423
  })[];
419
424
  out_dir: string;
420
425
  tsconfig_path?: string | undefined;
426
+ scan_root?: string | undefined;
421
427
  }>;
422
428
  export declare const CheckV2RequestSchema: z.ZodObject<{
423
429
  request_id: z.ZodString;
@@ -1810,6 +1816,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
1810
1816
  action: z.ZodLiteral<"init">;
1811
1817
  repo_root: z.ZodString;
1812
1818
  tsconfig_path: z.ZodOptional<z.ZodString>;
1819
+ scan_root: z.ZodOptional<z.ZodString>;
1813
1820
  tsconfig_snapshot: z.ZodOptional<z.ZodObject<{
1814
1821
  compilerOptions: z.ZodObject<{
1815
1822
  module: z.ZodOptional<z.ZodString>;
@@ -1902,6 +1909,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
1902
1909
  action: "init";
1903
1910
  repo_root: string;
1904
1911
  tsconfig_path?: string | undefined;
1912
+ scan_root?: string | undefined;
1905
1913
  tsconfig_snapshot?: {
1906
1914
  compilerOptions: {
1907
1915
  module?: string | undefined;
@@ -1928,6 +1936,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
1928
1936
  action: "init";
1929
1937
  repo_root: string;
1930
1938
  tsconfig_path?: string | undefined;
1939
+ scan_root?: string | undefined;
1931
1940
  tsconfig_snapshot?: {
1932
1941
  compilerOptions: {
1933
1942
  module?: string | undefined;
@@ -2083,6 +2092,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2083
2092
  }>]>, "many">;
2084
2093
  out_dir: z.ZodString;
2085
2094
  tsconfig_path: z.ZodOptional<z.ZodString>;
2095
+ scan_root: z.ZodOptional<z.ZodString>;
2086
2096
  }, "strip", z.ZodTypeAny, {
2087
2097
  request_id: string;
2088
2098
  action: "capture_v2";
@@ -2120,6 +2130,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2120
2130
  })[];
2121
2131
  out_dir: string;
2122
2132
  tsconfig_path?: string | undefined;
2133
+ scan_root?: string | undefined;
2123
2134
  }, {
2124
2135
  request_id: string;
2125
2136
  action: "capture_v2";
@@ -2157,6 +2168,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
2157
2168
  })[];
2158
2169
  out_dir: string;
2159
2170
  tsconfig_path?: string | undefined;
2171
+ scan_root?: string | undefined;
2160
2172
  }>, z.ZodObject<{
2161
2173
  request_id: z.ZodString;
2162
2174
  } & {
@@ -136,6 +136,7 @@ export const InitRequestSchema = BaseRequestSchema.extend({
136
136
  action: z.literal('init'),
137
137
  repo_root: z.string().min(1, 'Repo root cannot be empty'),
138
138
  tsconfig_path: z.string().optional(),
139
+ scan_root: z.string().min(1).optional(),
139
140
  tsconfig_snapshot: TsconfigSnapshotSchema.optional(),
140
141
  pinned_dependencies: PinnedDependencySnapshotSchema.optional(),
141
142
  });
@@ -192,6 +193,7 @@ export const CaptureV2RequestSchema = BaseRequestSchema.extend({
192
193
  anchors: z.array(CaptureAnchorRequestSchema).min(1, 'At least one anchor is required'),
193
194
  out_dir: z.string().min(1, 'Output dir cannot be empty'),
194
195
  tsconfig_path: z.string().optional(),
196
+ scan_root: z.string().min(1).optional(),
195
197
  });
196
198
  const CheckStubInputSchema = z.object({
197
199
  service_name: z.string().min(1),