@abdulmunimjemal/codescope 0.2.0 → 0.3.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/README.md +25 -9
- package/dist/cli.js +329 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +64 -5
- package/dist/index.js +295 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -200,6 +200,13 @@ declare class GraphStore {
|
|
|
200
200
|
kind?: "call" | "method" | "import";
|
|
201
201
|
limit?: number;
|
|
202
202
|
}): RefRow[];
|
|
203
|
+
/**
|
|
204
|
+
* Files whose imports reference a module basename (e.g. `store` for
|
|
205
|
+
* `src/store.ts`). Matches `import … from "./store"`, `"../src/store.js"`,
|
|
206
|
+
* etc. Used by affected-test analysis to follow import edges, which — unlike
|
|
207
|
+
* call edges — reliably reach test files (tests import the module under test).
|
|
208
|
+
*/
|
|
209
|
+
findImporters(moduleBasename: string): string[];
|
|
203
210
|
/** The symbols defined in a file, in source order. */
|
|
204
211
|
fileOutline(path: string): SymbolRow[];
|
|
205
212
|
/** Distinct (callee, callKind) pairs invoked from inside symbols named `from`. */
|
|
@@ -298,11 +305,15 @@ declare function parseSource(langId: string, source: string): Promise<ParseResul
|
|
|
298
305
|
* "member access" node — so definitions, calls, and imports are all described
|
|
299
306
|
* declaratively here and interpreted by a single generic walker.
|
|
300
307
|
*/
|
|
301
|
-
type NameStrategy = "field" | "c_declarator";
|
|
308
|
+
type NameStrategy = "field" | "c_declarator" | "first_typed";
|
|
302
309
|
interface DefRule {
|
|
303
310
|
kind: SymbolKind;
|
|
304
|
-
/** How to read the definition's name (default:
|
|
311
|
+
/** How to read the definition's name (default: `field` on `name`). */
|
|
305
312
|
name?: NameStrategy;
|
|
313
|
+
/** For the `field` strategy: which field holds the name (default `name`). */
|
|
314
|
+
nameField?: string;
|
|
315
|
+
/** For the `first_typed` strategy: pick the first named child of these types. */
|
|
316
|
+
nameTypes?: string[];
|
|
306
317
|
}
|
|
307
318
|
interface CallRule {
|
|
308
319
|
/** AST node type for this kind of call. */
|
|
@@ -360,13 +371,61 @@ declare function createServer(store: GraphStore): McpServer;
|
|
|
360
371
|
/** Connect a codescope server to stdio (the transport coding agents speak). */
|
|
361
372
|
declare function runStdioServer(store: GraphStore): Promise<void>;
|
|
362
373
|
|
|
374
|
+
/** Whether a repo-relative path looks like a test file. */
|
|
375
|
+
declare function isTestFile(path: string): boolean;
|
|
376
|
+
interface AffectedResult {
|
|
377
|
+
changed: string[];
|
|
378
|
+
/** Every file that defines a symbol transitively affected by the changes. */
|
|
379
|
+
impactedFiles: string[];
|
|
380
|
+
/** The impacted files that look like tests — the suite worth re-running. */
|
|
381
|
+
tests: string[];
|
|
382
|
+
}
|
|
383
|
+
declare function affected(store: GraphStore, changedPaths: string[], opts?: {
|
|
384
|
+
depth?: number;
|
|
385
|
+
}): AffectedResult;
|
|
386
|
+
|
|
387
|
+
/** Agents whose config is plain JSON with an `mcpServers` map (auto-wirable). */
|
|
388
|
+
type AgentId = "claude" | "cursor";
|
|
389
|
+
declare const SUPPORTED_AGENTS: readonly AgentId[];
|
|
390
|
+
interface ServerEntry {
|
|
391
|
+
command: string;
|
|
392
|
+
args: string[];
|
|
393
|
+
}
|
|
394
|
+
/** The MCP server entry agents need to launch codescope over stdio. */
|
|
395
|
+
declare function serverEntry(serveTarget?: string): ServerEntry;
|
|
396
|
+
/** The config file codescope writes for a given agent. */
|
|
397
|
+
declare function configPath(agent: AgentId, root: string, global?: boolean): string;
|
|
398
|
+
interface InstallOutcome {
|
|
399
|
+
agent: AgentId;
|
|
400
|
+
path: string;
|
|
401
|
+
action: "added" | "updated";
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Idempotently wire codescope into an agent's MCP config. Preserves any other
|
|
405
|
+
* servers already configured and overwrites only the `codescope` entry.
|
|
406
|
+
*/
|
|
407
|
+
declare function installInto(agent: AgentId, root: string, opts?: {
|
|
408
|
+
global?: boolean;
|
|
409
|
+
serveTarget?: string;
|
|
410
|
+
}): InstallOutcome;
|
|
411
|
+
/** Wire codescope into one or more agents. */
|
|
412
|
+
declare function install(root: string, opts?: {
|
|
413
|
+
agents?: AgentId[];
|
|
414
|
+
global?: boolean;
|
|
415
|
+
serveTarget?: string;
|
|
416
|
+
}): InstallOutcome[];
|
|
417
|
+
/** Copy-paste config for agents whose config isn't plain JSON (e.g. Codex, TOML). */
|
|
418
|
+
declare function codexSnippet(serveTarget?: string): string;
|
|
419
|
+
|
|
363
420
|
declare function formatSymbols(rows: SymbolRow[]): string;
|
|
364
421
|
declare function formatRefs(rows: RefRow[]): string;
|
|
365
422
|
declare function formatNeighborhood(n: Neighborhood): string;
|
|
366
423
|
declare function formatImpact(rows: ImpactRow[]): string;
|
|
367
424
|
declare function formatContext(b: ContextBundle): string;
|
|
425
|
+
declare function formatAffected(r: AffectedResult): string;
|
|
368
426
|
declare function formatStats(s: IndexStats): string;
|
|
369
427
|
|
|
428
|
+
declare const format_formatAffected: typeof formatAffected;
|
|
370
429
|
declare const format_formatContext: typeof formatContext;
|
|
371
430
|
declare const format_formatImpact: typeof formatImpact;
|
|
372
431
|
declare const format_formatNeighborhood: typeof formatNeighborhood;
|
|
@@ -374,10 +433,10 @@ declare const format_formatRefs: typeof formatRefs;
|
|
|
374
433
|
declare const format_formatStats: typeof formatStats;
|
|
375
434
|
declare const format_formatSymbols: typeof formatSymbols;
|
|
376
435
|
declare namespace format {
|
|
377
|
-
export { format_formatContext as formatContext, format_formatImpact as formatImpact, format_formatNeighborhood as formatNeighborhood, format_formatRefs as formatRefs, format_formatStats as formatStats, format_formatSymbols as formatSymbols };
|
|
436
|
+
export { format_formatAffected as formatAffected, format_formatContext as formatContext, format_formatImpact as formatImpact, format_formatNeighborhood as formatNeighborhood, format_formatRefs as formatRefs, format_formatStats as formatStats, format_formatSymbols as formatSymbols };
|
|
378
437
|
}
|
|
379
438
|
|
|
380
439
|
/** The codescope version. Kept in sync with package.json at release time. */
|
|
381
|
-
declare const VERSION = "0.
|
|
440
|
+
declare const VERSION = "0.3.0";
|
|
382
441
|
|
|
383
|
-
export { type ContextBundle, type FileIndexOutcome, type FileMeta, GraphStore, type ImpactRow, type IndexOptions, type IndexRunResult, type IndexStats, Indexer, LANGUAGES, type LanguageConfig, type Neighborhood, type ParseResult, type ParsedRef, type ParsedSymbol, type RefKind, type RefRow, SUPPORTED_EXTENSIONS, type SymbolKind, type SymbolRow, VERSION, type WatchEvents, type WatchHandle, type WatchOptions, createServer, format, languageForPath, parseSource, runStdioServer, watch };
|
|
442
|
+
export { type AffectedResult, type AgentId, type ContextBundle, type FileIndexOutcome, type FileMeta, GraphStore, type ImpactRow, type IndexOptions, type IndexRunResult, type IndexStats, Indexer, type InstallOutcome, LANGUAGES, type LanguageConfig, type Neighborhood, type ParseResult, type ParsedRef, type ParsedSymbol, type RefKind, type RefRow, SUPPORTED_AGENTS, SUPPORTED_EXTENSIONS, type SymbolKind, type SymbolRow, VERSION, type WatchEvents, type WatchHandle, type WatchOptions, affected, codexSnippet, configPath, createServer, format, install, installInto, isTestFile, languageForPath, parseSource, runStdioServer, serverEntry, watch };
|
package/dist/index.js
CHANGED
|
@@ -307,6 +307,18 @@ var GraphStore = class {
|
|
|
307
307
|
).all(name, limit);
|
|
308
308
|
return rows.map(toRefRow);
|
|
309
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Files whose imports reference a module basename (e.g. `store` for
|
|
312
|
+
* `src/store.ts`). Matches `import … from "./store"`, `"../src/store.js"`,
|
|
313
|
+
* etc. Used by affected-test analysis to follow import edges, which — unlike
|
|
314
|
+
* call edges — reliably reach test files (tests import the module under test).
|
|
315
|
+
*/
|
|
316
|
+
findImporters(moduleBasename2) {
|
|
317
|
+
return this.db.prepare(
|
|
318
|
+
`SELECT DISTINCT f.path FROM refs r JOIN files f ON f.id = r.file_id
|
|
319
|
+
WHERE r.kind = 'import' AND (r.name = ? OR r.name LIKE ? OR r.name LIKE ?)`
|
|
320
|
+
).all(moduleBasename2, `%/${moduleBasename2}`, `%/${moduleBasename2}.%`).map((r) => r.path);
|
|
321
|
+
}
|
|
310
322
|
/** The symbols defined in a file, in source order. */
|
|
311
323
|
fileOutline(path) {
|
|
312
324
|
return this.db.prepare(
|
|
@@ -683,6 +695,118 @@ var php = {
|
|
|
683
695
|
importRules: [{ type: "namespace_use_declaration", childTypes: ["namespace_use_clause"] }],
|
|
684
696
|
exportTypes: /* @__PURE__ */ new Set()
|
|
685
697
|
};
|
|
698
|
+
var scala = {
|
|
699
|
+
id: "scala",
|
|
700
|
+
wasm: "scala",
|
|
701
|
+
defs: {
|
|
702
|
+
class_definition: { kind: "class" },
|
|
703
|
+
object_definition: { kind: "class" },
|
|
704
|
+
trait_definition: { kind: "interface" },
|
|
705
|
+
function_definition: { kind: "method" },
|
|
706
|
+
type_definition: { kind: "type" }
|
|
707
|
+
},
|
|
708
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
709
|
+
nestedFunctionsAreMethods: false,
|
|
710
|
+
callRules: [
|
|
711
|
+
{ type: "call_expression", fnField: "function", memberTypes: ["field_expression"], memberField: "field" }
|
|
712
|
+
],
|
|
713
|
+
importRules: [{ type: "import_declaration", childTypes: ["stable_identifier", "identifier"] }],
|
|
714
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
715
|
+
};
|
|
716
|
+
var solidity = {
|
|
717
|
+
id: "solidity",
|
|
718
|
+
wasm: "solidity",
|
|
719
|
+
defs: {
|
|
720
|
+
contract_declaration: { kind: "class" },
|
|
721
|
+
interface_declaration: { kind: "interface" },
|
|
722
|
+
library_declaration: { kind: "class" },
|
|
723
|
+
function_definition: { kind: "method" },
|
|
724
|
+
modifier_definition: { kind: "function" },
|
|
725
|
+
struct_declaration: { kind: "class" },
|
|
726
|
+
enum_declaration: { kind: "enum" }
|
|
727
|
+
},
|
|
728
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
729
|
+
nestedFunctionsAreMethods: false,
|
|
730
|
+
callRules: [{ type: "call_expression", fnField: "function" }],
|
|
731
|
+
importRules: [{ type: "import_directive", field: "source" }],
|
|
732
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
733
|
+
};
|
|
734
|
+
var zig = {
|
|
735
|
+
id: "zig",
|
|
736
|
+
wasm: "zig",
|
|
737
|
+
defs: { function_declaration: { kind: "function" } },
|
|
738
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
739
|
+
nestedFunctionsAreMethods: false,
|
|
740
|
+
callRules: [{ type: "call_expression", fnField: "function" }],
|
|
741
|
+
importRules: [],
|
|
742
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
743
|
+
};
|
|
744
|
+
var kotlin = {
|
|
745
|
+
id: "kotlin",
|
|
746
|
+
wasm: "kotlin",
|
|
747
|
+
defs: {
|
|
748
|
+
class_declaration: { kind: "class", name: "first_typed", nameTypes: ["type_identifier"] },
|
|
749
|
+
object_declaration: { kind: "class", name: "first_typed", nameTypes: ["type_identifier"] },
|
|
750
|
+
function_declaration: { kind: "function", name: "first_typed", nameTypes: ["simple_identifier"] }
|
|
751
|
+
},
|
|
752
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
753
|
+
nestedFunctionsAreMethods: true,
|
|
754
|
+
callRules: [],
|
|
755
|
+
importRules: [{ type: "import_header", childTypes: ["identifier"] }],
|
|
756
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
757
|
+
};
|
|
758
|
+
var objc = {
|
|
759
|
+
id: "objc",
|
|
760
|
+
wasm: "objc",
|
|
761
|
+
defs: {
|
|
762
|
+
class_interface: { kind: "class", name: "first_typed", nameTypes: ["identifier"] },
|
|
763
|
+
class_implementation: { kind: "class", name: "first_typed", nameTypes: ["identifier"] },
|
|
764
|
+
method_declaration: { kind: "method", name: "first_typed", nameTypes: ["identifier"] },
|
|
765
|
+
method_definition: { kind: "method", name: "first_typed", nameTypes: ["identifier"] }
|
|
766
|
+
},
|
|
767
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
768
|
+
nestedFunctionsAreMethods: false,
|
|
769
|
+
callRules: [{ type: "call_expression", fnField: "function" }],
|
|
770
|
+
importRules: [{ type: "preproc_include", field: "path" }],
|
|
771
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
772
|
+
};
|
|
773
|
+
var lua = {
|
|
774
|
+
id: "lua",
|
|
775
|
+
wasm: "lua",
|
|
776
|
+
defs: {
|
|
777
|
+
function_definition_statement: { kind: "function" },
|
|
778
|
+
local_function_definition_statement: { kind: "function" }
|
|
779
|
+
},
|
|
780
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
781
|
+
nestedFunctionsAreMethods: false,
|
|
782
|
+
callRules: [],
|
|
783
|
+
importRules: [],
|
|
784
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
785
|
+
};
|
|
786
|
+
var bash = {
|
|
787
|
+
id: "bash",
|
|
788
|
+
wasm: "bash",
|
|
789
|
+
defs: { function_definition: { kind: "function" } },
|
|
790
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
791
|
+
nestedFunctionsAreMethods: false,
|
|
792
|
+
callRules: [],
|
|
793
|
+
importRules: [],
|
|
794
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
795
|
+
};
|
|
796
|
+
var ocaml = {
|
|
797
|
+
id: "ocaml",
|
|
798
|
+
wasm: "ocaml",
|
|
799
|
+
defs: {
|
|
800
|
+
let_binding: { kind: "function", name: "field", nameField: "pattern" },
|
|
801
|
+
module_definition: { kind: "class" },
|
|
802
|
+
type_definition: { kind: "type" }
|
|
803
|
+
},
|
|
804
|
+
functionBindings: /* @__PURE__ */ new Set(),
|
|
805
|
+
nestedFunctionsAreMethods: false,
|
|
806
|
+
callRules: [],
|
|
807
|
+
importRules: [],
|
|
808
|
+
exportTypes: /* @__PURE__ */ new Set()
|
|
809
|
+
};
|
|
686
810
|
var LANGUAGES = {
|
|
687
811
|
typescript,
|
|
688
812
|
tsx,
|
|
@@ -695,7 +819,15 @@ var LANGUAGES = {
|
|
|
695
819
|
c,
|
|
696
820
|
cpp,
|
|
697
821
|
csharp,
|
|
698
|
-
php
|
|
822
|
+
php,
|
|
823
|
+
scala,
|
|
824
|
+
solidity,
|
|
825
|
+
zig,
|
|
826
|
+
kotlin,
|
|
827
|
+
objc,
|
|
828
|
+
lua,
|
|
829
|
+
bash,
|
|
830
|
+
ocaml
|
|
699
831
|
};
|
|
700
832
|
var EXT_TO_LANG = {
|
|
701
833
|
".ts": "typescript",
|
|
@@ -720,7 +852,19 @@ var EXT_TO_LANG = {
|
|
|
720
852
|
".hpp": "cpp",
|
|
721
853
|
".hh": "cpp",
|
|
722
854
|
".cs": "csharp",
|
|
723
|
-
".php": "php"
|
|
855
|
+
".php": "php",
|
|
856
|
+
".scala": "scala",
|
|
857
|
+
".sc": "scala",
|
|
858
|
+
".sol": "solidity",
|
|
859
|
+
".zig": "zig",
|
|
860
|
+
".kt": "kotlin",
|
|
861
|
+
".kts": "kotlin",
|
|
862
|
+
".m": "objc",
|
|
863
|
+
".lua": "lua",
|
|
864
|
+
".sh": "bash",
|
|
865
|
+
".bash": "bash",
|
|
866
|
+
".ml": "ocaml",
|
|
867
|
+
".mli": "ocaml"
|
|
724
868
|
};
|
|
725
869
|
var SUPPORTED_EXTENSIONS = Object.keys(EXT_TO_LANG);
|
|
726
870
|
function languageForPath(path) {
|
|
@@ -806,7 +950,7 @@ function classify(node, lang) {
|
|
|
806
950
|
return null;
|
|
807
951
|
}
|
|
808
952
|
function buildSymbol(node, rule, container, containerKind, lang) {
|
|
809
|
-
const name = symbolName(node, rule
|
|
953
|
+
const name = symbolName(node, rule);
|
|
810
954
|
if (!name) return null;
|
|
811
955
|
let kind = rule.kind;
|
|
812
956
|
if (kind === "function" && lang.nestedFunctionsAreMethods && containerKind === "class") {
|
|
@@ -826,9 +970,20 @@ function buildSymbol(node, rule, container, containerKind, lang) {
|
|
|
826
970
|
endByte: node.endIndex
|
|
827
971
|
};
|
|
828
972
|
}
|
|
829
|
-
function symbolName(node,
|
|
830
|
-
|
|
831
|
-
|
|
973
|
+
function symbolName(node, rule) {
|
|
974
|
+
switch (rule.name ?? "field") {
|
|
975
|
+
case "c_declarator":
|
|
976
|
+
return cDeclaratorName(node);
|
|
977
|
+
case "first_typed": {
|
|
978
|
+
const types = rule.nameTypes ?? [];
|
|
979
|
+
for (const child of node.namedChildren) {
|
|
980
|
+
if (types.includes(child.type)) return child.text;
|
|
981
|
+
}
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
default:
|
|
985
|
+
return node.childForFieldName(rule.nameField ?? "name")?.text ?? null;
|
|
986
|
+
}
|
|
832
987
|
}
|
|
833
988
|
function cDeclaratorName(node) {
|
|
834
989
|
let decl = node.childForFieldName("declarator");
|
|
@@ -1073,9 +1228,62 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
1073
1228
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1074
1229
|
import { z } from "zod";
|
|
1075
1230
|
|
|
1231
|
+
// src/affected.ts
|
|
1232
|
+
var TEST_PATTERNS = [
|
|
1233
|
+
/(^|\/)(tests?|spec|specs|__tests__|e2e|integration)\//i,
|
|
1234
|
+
/\.(test|spec)\.[a-z]+$/i,
|
|
1235
|
+
// foo.test.ts, foo.spec.js
|
|
1236
|
+
/_test\.[a-z]+$/i,
|
|
1237
|
+
// foo_test.go, foo_test.py, foo_test.rs
|
|
1238
|
+
/(^|\/)test_[^/]+\.(py|rb)$/i,
|
|
1239
|
+
// test_foo.py
|
|
1240
|
+
/(Test|Tests|Spec)\.[a-z]+$/i,
|
|
1241
|
+
// FooTest.java, FooTests.cs, FooSpec.scala
|
|
1242
|
+
/_spec\.rb$/i
|
|
1243
|
+
// foo_spec.rb
|
|
1244
|
+
];
|
|
1245
|
+
function isTestFile(path) {
|
|
1246
|
+
return TEST_PATTERNS.some((re) => re.test(path));
|
|
1247
|
+
}
|
|
1248
|
+
function moduleBasename(path) {
|
|
1249
|
+
const base = path.slice(path.lastIndexOf("/") + 1);
|
|
1250
|
+
const dot = base.indexOf(".");
|
|
1251
|
+
return dot === -1 ? base : base.slice(0, dot);
|
|
1252
|
+
}
|
|
1253
|
+
function affected(store, changedPaths, opts = {}) {
|
|
1254
|
+
const depth = opts.depth ?? 4;
|
|
1255
|
+
const impactedFiles = new Set(changedPaths);
|
|
1256
|
+
for (const path of changedPaths) {
|
|
1257
|
+
for (const sym of store.fileOutline(path)) {
|
|
1258
|
+
for (const caller of store.impact(sym.name, { depth })) {
|
|
1259
|
+
impactedFiles.add(caller.file);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
let frontier = [...changedPaths];
|
|
1264
|
+
for (let d = 0; d < depth && frontier.length > 0; d++) {
|
|
1265
|
+
const next = [];
|
|
1266
|
+
for (const path of frontier) {
|
|
1267
|
+
for (const importer of store.findImporters(moduleBasename(path))) {
|
|
1268
|
+
if (!impactedFiles.has(importer)) {
|
|
1269
|
+
impactedFiles.add(importer);
|
|
1270
|
+
next.push(importer);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
frontier = next;
|
|
1275
|
+
}
|
|
1276
|
+
return {
|
|
1277
|
+
changed: changedPaths,
|
|
1278
|
+
impactedFiles: [...impactedFiles].sort(),
|
|
1279
|
+
tests: [...impactedFiles].filter(isTestFile).sort()
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1076
1283
|
// src/format.ts
|
|
1077
1284
|
var format_exports = {};
|
|
1078
1285
|
__export(format_exports, {
|
|
1286
|
+
formatAffected: () => formatAffected,
|
|
1079
1287
|
formatContext: () => formatContext,
|
|
1080
1288
|
formatImpact: () => formatImpact,
|
|
1081
1289
|
formatNeighborhood: () => formatNeighborhood,
|
|
@@ -1137,6 +1345,14 @@ function formatContext(b) {
|
|
|
1137
1345
|
}
|
|
1138
1346
|
return lines.join("\n");
|
|
1139
1347
|
}
|
|
1348
|
+
function formatAffected(r) {
|
|
1349
|
+
if (r.tests.length === 0) {
|
|
1350
|
+
return `No test files appear affected by ${r.changed.length} changed file(s).`;
|
|
1351
|
+
}
|
|
1352
|
+
const lines = [`${r.tests.length} test file(s) affected by your changes:`, ""];
|
|
1353
|
+
for (const t of r.tests) lines.push(` ${t}`);
|
|
1354
|
+
return lines.join("\n");
|
|
1355
|
+
}
|
|
1140
1356
|
function formatStats(s) {
|
|
1141
1357
|
const kinds = Object.entries(s.byKind).sort((a, b) => b[1] - a[1]).map(([k, n]) => `${k}=${n}`).join(" ");
|
|
1142
1358
|
const langs = Object.entries(s.byLang).sort((a, b) => b[1] - a[1]).map(([k, n]) => `${k}=${n}`).join(" ");
|
|
@@ -1149,7 +1365,7 @@ function formatStats(s) {
|
|
|
1149
1365
|
}
|
|
1150
1366
|
|
|
1151
1367
|
// src/version.ts
|
|
1152
|
-
var VERSION = "0.
|
|
1368
|
+
var VERSION = "0.3.0";
|
|
1153
1369
|
|
|
1154
1370
|
// src/mcp.ts
|
|
1155
1371
|
var KIND = z.enum(["function", "method", "class", "interface", "type", "enum", "variable"]);
|
|
@@ -1269,6 +1485,18 @@ function createServer(store) {
|
|
|
1269
1485
|
},
|
|
1270
1486
|
async ({ name, depth, limit }) => textResult(formatNeighborhood(store.neighborhood(name, { depth, limit })))
|
|
1271
1487
|
);
|
|
1488
|
+
server.registerTool(
|
|
1489
|
+
"affected",
|
|
1490
|
+
{
|
|
1491
|
+
title: "Affected tests",
|
|
1492
|
+
description: "Given a list of changed files, return the test files likely affected \u2014 the symbols those files define, walked through their transitive callers, filtered to tests. Use before running a suite to know what's worth re-running.",
|
|
1493
|
+
inputSchema: {
|
|
1494
|
+
files: z.array(z.string()).describe("repo-relative paths of the changed files"),
|
|
1495
|
+
depth: z.number().int().min(1).max(6).optional()
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
async ({ files, depth }) => textResult(formatAffected(affected(store, files, { depth })))
|
|
1499
|
+
);
|
|
1272
1500
|
server.registerTool(
|
|
1273
1501
|
"stats",
|
|
1274
1502
|
{
|
|
@@ -1285,17 +1513,77 @@ async function runStdioServer(store) {
|
|
|
1285
1513
|
const transport = new StdioServerTransport();
|
|
1286
1514
|
await server.connect(transport);
|
|
1287
1515
|
}
|
|
1516
|
+
|
|
1517
|
+
// src/install.ts
|
|
1518
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1519
|
+
import { homedir } from "os";
|
|
1520
|
+
import { dirname, join } from "path";
|
|
1521
|
+
var PACKAGE = "@abdulmunimjemal/codescope";
|
|
1522
|
+
var MCP_SERVER_NAME = "codescope";
|
|
1523
|
+
var SUPPORTED_AGENTS = ["claude", "cursor"];
|
|
1524
|
+
function serverEntry(serveTarget = ".") {
|
|
1525
|
+
return { command: "npx", args: ["-y", PACKAGE, "mcp", serveTarget] };
|
|
1526
|
+
}
|
|
1527
|
+
function configPath(agent, root, global = false) {
|
|
1528
|
+
switch (agent) {
|
|
1529
|
+
case "claude":
|
|
1530
|
+
return global ? join(homedir(), ".mcp.json") : join(root, ".mcp.json");
|
|
1531
|
+
case "cursor":
|
|
1532
|
+
return global ? join(homedir(), ".cursor", "mcp.json") : join(root, ".cursor", "mcp.json");
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function readJson(path) {
|
|
1536
|
+
if (!existsSync(path)) return {};
|
|
1537
|
+
try {
|
|
1538
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
1539
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1540
|
+
} catch {
|
|
1541
|
+
return {};
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
function installInto(agent, root, opts = {}) {
|
|
1545
|
+
const path = configPath(agent, root, opts.global ?? false);
|
|
1546
|
+
const config = readJson(path);
|
|
1547
|
+
const existing = config.mcpServers;
|
|
1548
|
+
const servers = existing && typeof existing === "object" ? existing : {};
|
|
1549
|
+
const existed = Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME);
|
|
1550
|
+
servers[MCP_SERVER_NAME] = serverEntry(opts.serveTarget);
|
|
1551
|
+
config.mcpServers = servers;
|
|
1552
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1553
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}
|
|
1554
|
+
`);
|
|
1555
|
+
return { agent, path, action: existed ? "updated" : "added" };
|
|
1556
|
+
}
|
|
1557
|
+
function install(root, opts = {}) {
|
|
1558
|
+
const agents = opts.agents ?? [...SUPPORTED_AGENTS];
|
|
1559
|
+
return agents.map((agent) => installInto(agent, root, opts));
|
|
1560
|
+
}
|
|
1561
|
+
function codexSnippet(serveTarget = ".") {
|
|
1562
|
+
return [
|
|
1563
|
+
"[mcp_servers.codescope]",
|
|
1564
|
+
'command = "npx"',
|
|
1565
|
+
`args = ["-y", "${PACKAGE}", "mcp", "${serveTarget}"]`
|
|
1566
|
+
].join("\n");
|
|
1567
|
+
}
|
|
1288
1568
|
export {
|
|
1289
1569
|
GraphStore,
|
|
1290
1570
|
Indexer,
|
|
1291
1571
|
LANGUAGES,
|
|
1572
|
+
SUPPORTED_AGENTS,
|
|
1292
1573
|
SUPPORTED_EXTENSIONS,
|
|
1293
1574
|
VERSION,
|
|
1575
|
+
affected,
|
|
1576
|
+
codexSnippet,
|
|
1577
|
+
configPath,
|
|
1294
1578
|
createServer,
|
|
1295
1579
|
format_exports as format,
|
|
1580
|
+
install,
|
|
1581
|
+
installInto,
|
|
1582
|
+
isTestFile,
|
|
1296
1583
|
languageForPath,
|
|
1297
1584
|
parseSource,
|
|
1298
1585
|
runStdioServer,
|
|
1586
|
+
serverEntry,
|
|
1299
1587
|
watch
|
|
1300
1588
|
};
|
|
1301
1589
|
//# sourceMappingURL=index.js.map
|