carrick 0.3.120 → 0.3.122

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.120",
3
+ "version": "0.3.122",
4
4
  "carrickCache": {
5
5
  "cache_version": 22,
6
6
  "intent_cache_version": 2
@@ -113,11 +113,11 @@
113
113
  "zod": "^3.23.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "@carrick-tools/cli-darwin-arm64": "0.3.120",
117
- "@carrick-tools/cli-darwin-x64": "0.3.120",
118
- "@carrick-tools/cli-linux-arm64": "0.3.120",
119
- "@carrick-tools/cli-linux-x64": "0.3.120",
120
- "@carrick-tools/cli-win32-x64": "0.3.120"
116
+ "@carrick-tools/cli-darwin-arm64": "0.3.122",
117
+ "@carrick-tools/cli-darwin-x64": "0.3.122",
118
+ "@carrick-tools/cli-linux-arm64": "0.3.122",
119
+ "@carrick-tools/cli-linux-x64": "0.3.122",
120
+ "@carrick-tools/cli-win32-x64": "0.3.122"
121
121
  },
122
122
  "devDependencies": {
123
123
  "@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.120",
4
+ "version": "0.3.122",
5
5
  "author": {
6
6
  "name": "Carrick",
7
7
  "email": "hello@carrick.tools"
@@ -1694,7 +1694,7 @@ class DeclarationReader {
1694
1694
  for (const signature of callee.signatures) {
1695
1695
  const count = signature.getParameters().length;
1696
1696
  for (let arg = 0; arg < count; arg++) {
1697
- if (this.literalsAt(signature, { arg }).has(claim.name))
1697
+ if (this.literalsAt(signature, { arg }, callee.holder).has(claim.name))
1698
1698
  return VERIFIED;
1699
1699
  }
1700
1700
  }
@@ -2127,9 +2127,11 @@ class DeclarationReader {
2127
2127
  /**
2128
2128
  * The string literals a slot spells: the literal parts of its type, through
2129
2129
  * a type parameter's constraint, and the property names of a map a `keyof`
2130
- * reads, both as instantiated and as written at the declaration.
2130
+ * reads, both as instantiated and as written at the declaration. `holder`
2131
+ * is the type the member was read from: a map that is a type parameter of
2132
+ * a base class is read as the holder's class chain binds it.
2131
2133
  */
2132
- literalsAt(signature, at) {
2134
+ literalsAt(signature, at, holder) {
2133
2135
  const literals = new Set();
2134
2136
  let slot = this.parameterAt(signature, at.arg);
2135
2137
  if (slot !== undefined && slot !== VARIADIC && at.key !== undefined) {
@@ -2137,16 +2139,16 @@ class DeclarationReader {
2137
2139
  slot = property ? this.checker.getTypeOfSymbol(property) : undefined;
2138
2140
  }
2139
2141
  if (slot !== undefined && slot !== VARIADIC)
2140
- this.spell(slot, literals, 0);
2142
+ this.spell(slot, literals, 0, holder);
2141
2143
  if (at.key === undefined) {
2142
2144
  const parameters = signature.getDeclaration()?.parameters;
2143
2145
  const node = parameters?.[Math.min(at.arg, parameters.length - 1)]?.type;
2144
2146
  if (node)
2145
- this.spell(this.checker.getTypeFromTypeNode(node), literals, 0);
2147
+ this.spell(this.checker.getTypeFromTypeNode(node), literals, 0, holder);
2146
2148
  }
2147
2149
  return literals;
2148
2150
  }
2149
- spell(type, into, depth) {
2151
+ spell(type, into, depth, holder) {
2150
2152
  if (depth > 6)
2151
2153
  return;
2152
2154
  if (type.isStringLiteral()) {
@@ -2155,16 +2157,18 @@ class DeclarationReader {
2155
2157
  }
2156
2158
  if (type.isUnionOrIntersection()) {
2157
2159
  for (const part of type.types)
2158
- this.spell(part, into, depth + 1);
2160
+ this.spell(part, into, depth + 1, holder);
2159
2161
  return;
2160
2162
  }
2161
2163
  if (type.flags & ts.TypeFlags.TypeParameter) {
2162
2164
  const constraintNode = this.declaredConstraint(type);
2163
- this.spell(constraintNode ? this.checker.getTypeFromTypeNode(constraintNode) : this.checker.getBaseConstraintOfType(type) ?? type, into, depth + 1);
2165
+ this.spell(constraintNode ? this.checker.getTypeFromTypeNode(constraintNode) : this.checker.getBaseConstraintOfType(type) ?? type, into, depth + 1, holder);
2164
2166
  return;
2165
2167
  }
2166
2168
  if (type.flags & ts.TypeFlags.Index) {
2167
2169
  let map = type.type;
2170
+ if (map.flags & ts.TypeFlags.TypeParameter)
2171
+ map = this.heritageArgument(map, holder);
2168
2172
  if (map.flags & ts.TypeFlags.TypeParameter) {
2169
2173
  map = this.checker.getDefaultFromTypeParameter(map) ?? this.checker.getBaseConstraintOfType(map) ?? map;
2170
2174
  }
@@ -2174,6 +2178,49 @@ class DeclarationReader {
2174
2178
  }
2175
2179
  }
2176
2180
  }
2181
+ /**
2182
+ * The type argument a class's type parameter takes in `holder`'s class
2183
+ * chain: the argument a heritage clause passes for it
2184
+ * (`class Channel<L> extends Emitter<L, Reserved>` binds Emitter's second
2185
+ * parameter to `Reserved`), followed down the chain while that argument is
2186
+ * a type parameter of a class below. A parameter no heritage clause binds
2187
+ * comes back as it is (or as the last parameter it was bound to), for the
2188
+ * caller to read at its default.
2189
+ */
2190
+ heritageArgument(parameter, holder) {
2191
+ const bases = [];
2192
+ const seen = new Set();
2193
+ const visit = (type, depth) => {
2194
+ if (depth > 8)
2195
+ return;
2196
+ if (type.isIntersection()) {
2197
+ for (const part of type.types)
2198
+ visit(part, depth + 1);
2199
+ return;
2200
+ }
2201
+ const target = (type.objectFlags ?? 0) & ts.ObjectFlags.Reference ? type.target : type;
2202
+ if (!((target.objectFlags ?? 0) & ts.ObjectFlags.ClassOrInterface) || seen.has(target))
2203
+ return;
2204
+ seen.add(target);
2205
+ for (const base of this.checker.getBaseTypes(target)) {
2206
+ if ((base.objectFlags ?? 0) & ts.ObjectFlags.Reference)
2207
+ bases.push(base);
2208
+ visit(base, depth + 1);
2209
+ }
2210
+ };
2211
+ visit(this.checker.getNonNullableType(holder), 0);
2212
+ let bound = parameter;
2213
+ for (let step = 0; step <= bases.length && bound.flags & ts.TypeFlags.TypeParameter; step++) {
2214
+ const base = bases.find(each => (each.target.typeParameters ?? []).includes(bound));
2215
+ if (!base)
2216
+ break;
2217
+ const argument = this.checker.getTypeArguments(base)[base.target.typeParameters.indexOf(bound)];
2218
+ if (!argument)
2219
+ break;
2220
+ bound = argument;
2221
+ }
2222
+ return bound;
2223
+ }
2177
2224
  /**
2178
2225
  * The callee of a message claim: the claim's member `path` walked from the
2179
2226
  * receiver hop by hop (`client.tasks.trigger`), then `member` of the object
@@ -2219,7 +2266,7 @@ class DeclarationReader {
2219
2266
  ? library
2220
2267
  : library.filter(signature => this.isOwnFile(this.signatureFile(signature, type, form), home));
2221
2268
  if (own.length > 0)
2222
- return { signatures: own };
2269
+ return { signatures: own, holder: holder.type };
2223
2270
  if (library.length > 0)
2224
2271
  return { failure: failed('member_inherited') };
2225
2272
  return { failure: this.slotFailure(type, form === 'new' ? 'member_not_constructible' : 'member_not_callable') };
@@ -2517,10 +2564,10 @@ class DeclarationReader {
2517
2564
  // null member reads them.
2518
2565
  const call = this.ownCallee(holder, [], null, 'call');
2519
2566
  if ('signatures' in call)
2520
- fills.push(() => (receiver.call = this.listSignatures(call.signatures, take)));
2567
+ fills.push(() => (receiver.call = this.listSignatures(call.signatures, holder.type, take)));
2521
2568
  const construct = this.ownCallee(holder, [], null, 'new');
2522
2569
  if ('signatures' in construct) {
2523
- fills.push(() => (receiver.construct = this.listSignatures(construct.signatures, take)));
2570
+ fills.push(() => (receiver.construct = this.listSignatures(construct.signatures, holder.type, take)));
2524
2571
  }
2525
2572
  if (this.isOpenTop(type))
2526
2573
  return receiver;
@@ -2537,12 +2584,12 @@ class DeclarationReader {
2537
2584
  const own = 'signatures' in callee;
2538
2585
  const member = { name: property.getName(), own, signatures: [] };
2539
2586
  receiver.members.push(member);
2540
- fills.push(() => (member.signatures = this.listSignatures(own ? callee.signatures : library, take)));
2587
+ fills.push(() => (member.signatures = this.listSignatures(own ? callee.signatures : library, holder.type, take)));
2541
2588
  }
2542
2589
  });
2543
2590
  return receiver;
2544
2591
  }
2545
- listSignatures(signatures, take) {
2592
+ listSignatures(signatures, holder, take) {
2546
2593
  const listed = [];
2547
2594
  for (const signature of signatures) {
2548
2595
  if (!take())
@@ -2572,7 +2619,7 @@ class DeclarationReader {
2572
2619
  if (keys.length > 0)
2573
2620
  param.keys = keys;
2574
2621
  }
2575
- const literals = this.literalsAt(signature, { arg: index });
2622
+ const literals = this.literalsAt(signature, { arg: index }, holder);
2576
2623
  if (literals.size > 0)
2577
2624
  param.literals = [...literals].sort();
2578
2625
  params.push(param);
@@ -2686,7 +2733,7 @@ class DeclarationReader {
2686
2733
  return declaration !== undefined && this.isLibraryFile(declaration.getSourceFile());
2687
2734
  });
2688
2735
  if (signatures.length > 0)
2689
- return { signatures };
2736
+ return { signatures, holder: type };
2690
2737
  return { failure: this.slotFailure(type, 'member_not_callable') };
2691
2738
  }
2692
2739
  /**