@graphpilot-oss/graphpilot 0.0.1
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/.editorconfig +15 -0
- package/.github/CODEOWNERS +22 -0
- package/.github/FUNDING.yml +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +19 -0
- package/.github/dependabot.yml +15 -0
- package/.github/workflows/ci.yml +62 -0
- package/.github/workflows/release.yml +50 -0
- package/.prettierignore +19 -0
- package/.prettierrc.json +20 -0
- package/CHANGELOG.md +138 -0
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +111 -0
- package/LICENSE +201 -0
- package/README.md +132 -0
- package/SECURITY.md +44 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +1 -0
- package/bench/README.md +544 -0
- package/bench/results/agent-tier-2026-05-22.md +28 -0
- package/bench/results/agent-tier-summary.md +44 -0
- package/bench/results/baseline-tier-2026-05-22.md +23 -0
- package/bench/results/baseline.json +810 -0
- package/bench/results/baseline.md +28 -0
- package/bench/run-agent-tier-automated.ts +234 -0
- package/bench/run-agent-tier.md +125 -0
- package/bench/run-baseline-tier.ts +200 -0
- package/bench/run.ts +210 -0
- package/bench/runner-baseline.ts +177 -0
- package/bench/runner-graphpilot.ts +131 -0
- package/bench/score-agent-tier.ts +191 -0
- package/bench/score.ts +59 -0
- package/bench/tasks.ts +236 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +162 -0
- package/dist/cli.js.map +1 -0
- package/dist/edges.d.ts +57 -0
- package/dist/edges.js +170 -0
- package/dist/edges.js.map +1 -0
- package/dist/git.d.ts +95 -0
- package/dist/git.js +247 -0
- package/dist/git.js.map +1 -0
- package/dist/graph-schema.d.ts +36 -0
- package/dist/graph-schema.js +208 -0
- package/dist/graph-schema.js.map +1 -0
- package/dist/impact.d.ts +99 -0
- package/dist/impact.js +123 -0
- package/dist/impact.js.map +1 -0
- package/dist/indexer.d.ts +28 -0
- package/dist/indexer.js +111 -0
- package/dist/indexer.js.map +1 -0
- package/dist/interactions.d.ts +46 -0
- package/dist/interactions.js +0 -0
- package/dist/interactions.js.map +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +567 -0
- package/dist/mcp.js.map +1 -0
- package/dist/parser.d.ts +24 -0
- package/dist/parser.js +128 -0
- package/dist/parser.js.map +1 -0
- package/dist/provenance.d.ts +74 -0
- package/dist/provenance.js +95 -0
- package/dist/provenance.js.map +1 -0
- package/dist/query.d.ts +68 -0
- package/dist/query.js +127 -0
- package/dist/query.js.map +1 -0
- package/dist/redact.d.ts +30 -0
- package/dist/redact.js +117 -0
- package/dist/redact.js.map +1 -0
- package/dist/storage.d.ts +42 -0
- package/dist/storage.js +85 -0
- package/dist/storage.js.map +1 -0
- package/dist/symbols.d.ts +20 -0
- package/dist/symbols.js +140 -0
- package/dist/symbols.js.map +1 -0
- package/dist/validation.d.ts +9 -0
- package/dist/validation.js +65 -0
- package/dist/validation.js.map +1 -0
- package/dist/validators.d.ts +55 -0
- package/dist/validators.js +205 -0
- package/dist/validators.js.map +1 -0
- package/dist/watcher.d.ts +86 -0
- package/dist/watcher.js +310 -0
- package/dist/watcher.js.map +1 -0
- package/docs/architecture.md +311 -0
- package/docs/limitations.md +156 -0
- package/docs/mcp-setup.md +231 -0
- package/docs/quickstart.md +202 -0
- package/eslint.config.js +148 -0
- package/lefthook.yml +81 -0
- package/package.json +56 -0
- package/pnpm-workspace.yaml +6 -0
- package/scripts/smoke-stdio.mjs +97 -0
- package/src/cli.ts +171 -0
- package/src/edges.ts +202 -0
- package/src/git.ts +255 -0
- package/src/graph-schema.ts +229 -0
- package/src/impact.ts +218 -0
- package/src/indexer.ts +152 -0
- package/src/interactions.ts +0 -0
- package/src/mcp.ts +652 -0
- package/src/parser.ts +138 -0
- package/src/provenance.ts +115 -0
- package/src/query.ts +148 -0
- package/src/redact.ts +122 -0
- package/src/storage.ts +115 -0
- package/src/symbols.ts +173 -0
- package/src/validation.ts +69 -0
- package/src/validators.ts +253 -0
- package/src/watcher.ts +383 -0
- package/tests/edges.test.ts +175 -0
- package/tests/fixtures/sample.ts +32 -0
- package/tests/git.test.ts +303 -0
- package/tests/graph-schema.test.ts +321 -0
- package/tests/impact.test.ts +454 -0
- package/tests/interactions.test.ts +180 -0
- package/tests/lint-policy.test.ts +106 -0
- package/tests/mcp-stdio.test.ts +171 -0
- package/tests/mcp.test.ts +335 -0
- package/tests/parser.test.ts +31 -0
- package/tests/provenance.test.ts +132 -0
- package/tests/query.test.ts +160 -0
- package/tests/redact.test.ts +167 -0
- package/tests/security.test.ts +144 -0
- package/tests/symbols.test.ts +78 -0
- package/tests/validators.test.ts +193 -0
- package/tests/watcher.test.ts +250 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SymbolRecord } from './symbols.js';
|
|
2
|
+
import type { CallEdge } from './edges.js';
|
|
3
|
+
export interface Graph {
|
|
4
|
+
version: 1;
|
|
5
|
+
repoId: string;
|
|
6
|
+
rootPath: string;
|
|
7
|
+
indexedAt: string;
|
|
8
|
+
filesIndexed: number;
|
|
9
|
+
symbolCount: number;
|
|
10
|
+
edgeCount: number;
|
|
11
|
+
symbols: SymbolRecord[];
|
|
12
|
+
edges: CallEdge[];
|
|
13
|
+
/**
|
|
14
|
+
* Optional git provenance — set when the indexed root lives inside a
|
|
15
|
+
* git worktree. Both fields may be null even within a git repo (e.g.
|
|
16
|
+
* detached HEAD has no branch; an empty repo has no SHA). Older
|
|
17
|
+
* graph.json files written before the v0.1.5 pivot won't have these
|
|
18
|
+
* fields; the schema validator treats them as optional so old graphs
|
|
19
|
+
* still load.
|
|
20
|
+
*/
|
|
21
|
+
indexedSha?: string | null;
|
|
22
|
+
indexedBranch?: string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare function repoIdFor(absRootPath: string): string;
|
|
25
|
+
export declare function repoDir(absRootPath: string): string;
|
|
26
|
+
export declare function graphPath(absRootPath: string): string;
|
|
27
|
+
export declare function saveGraph(graph: Graph): string;
|
|
28
|
+
/**
|
|
29
|
+
* Load and validate a graph from disk. Returns null if the file is missing,
|
|
30
|
+
* unparseable, has a wrong schema version, or fails structural validation.
|
|
31
|
+
*
|
|
32
|
+
* T4 defence: anything from disk is untrusted. We re-parse and re-shape
|
|
33
|
+
* every field before exposing the result to query / MCP layers. String
|
|
34
|
+
* fields are sanitized (control chars stripped, lengths capped) so a
|
|
35
|
+
* crafted graph.json can't smuggle prompt-injection payloads or fake JSON
|
|
36
|
+
* Lines into tool output.
|
|
37
|
+
*
|
|
38
|
+
* Validation errors are written to stderr for diagnostics. The function
|
|
39
|
+
* never throws on bad data — it returns null so the MCP tool layer can
|
|
40
|
+
* surface "no index" cleanly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function loadGraph(absRootPath: string): Graph | null;
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync, chmodSync, renameSync } from 'node:fs';
|
|
5
|
+
import { validateGraph } from './graph-schema.js';
|
|
6
|
+
const isWindows = process.platform === 'win32';
|
|
7
|
+
export function repoIdFor(absRootPath) {
|
|
8
|
+
return createHash('sha256').update(absRootPath).digest('hex').slice(0, 16);
|
|
9
|
+
}
|
|
10
|
+
export function repoDir(absRootPath) {
|
|
11
|
+
return join(homedir(), '.graphpilot', repoIdFor(absRootPath));
|
|
12
|
+
}
|
|
13
|
+
export function graphPath(absRootPath) {
|
|
14
|
+
return join(repoDir(absRootPath), 'graph.json');
|
|
15
|
+
}
|
|
16
|
+
export function saveGraph(graph) {
|
|
17
|
+
const dir = repoDir(graph.rootPath);
|
|
18
|
+
// T7 defence: 0700 dir + 0600 file so other users on shared machines can't
|
|
19
|
+
// read the index. The mkdir/writeFileSync `mode` option only applies on
|
|
20
|
+
// creation, so we explicitly chmod afterwards to fix permissions on any
|
|
21
|
+
// pre-existing files (e.g. an index written before this protection landed).
|
|
22
|
+
// On Windows these modes are silently ignored, which is fine — NTFS ACLs
|
|
23
|
+
// are handled by the user profile boundary.
|
|
24
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
25
|
+
if (!isWindows)
|
|
26
|
+
chmodSync(dir, 0o700);
|
|
27
|
+
const path = graphPath(graph.rootPath);
|
|
28
|
+
// Atomic write — write to a sibling .tmp file and rename. Crash-safe:
|
|
29
|
+
// a partial write never produces a corrupt graph.json that would later
|
|
30
|
+
// fail T4's schema validator and force a full re-index. Defends watch
|
|
31
|
+
// mode (which writes many times) against ungraceful shutdowns.
|
|
32
|
+
const tmpPath = path + '.tmp';
|
|
33
|
+
writeFileSync(tmpPath, JSON.stringify(graph, null, 2), {
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
mode: 0o600,
|
|
36
|
+
});
|
|
37
|
+
if (!isWindows)
|
|
38
|
+
chmodSync(tmpPath, 0o600);
|
|
39
|
+
renameSync(tmpPath, path);
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load and validate a graph from disk. Returns null if the file is missing,
|
|
44
|
+
* unparseable, has a wrong schema version, or fails structural validation.
|
|
45
|
+
*
|
|
46
|
+
* T4 defence: anything from disk is untrusted. We re-parse and re-shape
|
|
47
|
+
* every field before exposing the result to query / MCP layers. String
|
|
48
|
+
* fields are sanitized (control chars stripped, lengths capped) so a
|
|
49
|
+
* crafted graph.json can't smuggle prompt-injection payloads or fake JSON
|
|
50
|
+
* Lines into tool output.
|
|
51
|
+
*
|
|
52
|
+
* Validation errors are written to stderr for diagnostics. The function
|
|
53
|
+
* never throws on bad data — it returns null so the MCP tool layer can
|
|
54
|
+
* surface "no index" cleanly.
|
|
55
|
+
*/
|
|
56
|
+
export function loadGraph(absRootPath) {
|
|
57
|
+
const path = graphPath(absRootPath);
|
|
58
|
+
if (!existsSync(path))
|
|
59
|
+
return null;
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = readFileSync(path, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(raw);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
process.stderr.write(`[graphpilot] graph.json is not valid JSON: ${path}\n`);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const errors = [];
|
|
76
|
+
const validated = validateGraph(parsed, errors);
|
|
77
|
+
if (!validated) {
|
|
78
|
+
process.stderr.write(`[graphpilot] graph.json failed schema validation: ${path}\n` +
|
|
79
|
+
errors.map((e) => ` - ${e}`).join('\n') +
|
|
80
|
+
'\n');
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return validated;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ParsedFile } from './parser.js';
|
|
2
|
+
export type SymbolKind = 'function' | 'class' | 'method' | 'interface' | 'type' | 'variable' | 'enum';
|
|
3
|
+
export interface SymbolRecord {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
kind: SymbolKind;
|
|
7
|
+
file: string;
|
|
8
|
+
line: number;
|
|
9
|
+
column: number;
|
|
10
|
+
endLine: number;
|
|
11
|
+
signature: string;
|
|
12
|
+
exported: boolean;
|
|
13
|
+
parent?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Extract every symbol-defining node from a parsed file.
|
|
17
|
+
* v1 covers: function decls, arrow/function-expr consts, classes, methods,
|
|
18
|
+
* interfaces, type aliases, enums.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractSymbols(parsed: ParsedFile): SymbolRecord[];
|
package/dist/symbols.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { walk } from './parser.js';
|
|
2
|
+
import { redactSecrets } from './redact.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extract every symbol-defining node from a parsed file.
|
|
5
|
+
* v1 covers: function decls, arrow/function-expr consts, classes, methods,
|
|
6
|
+
* interfaces, type aliases, enums.
|
|
7
|
+
*/
|
|
8
|
+
export function extractSymbols(parsed) {
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const node of walk(parsed.tree.rootNode)) {
|
|
11
|
+
extractFromNode(node, parsed, out);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function extractFromNode(node, parsed, out) {
|
|
16
|
+
switch (node.type) {
|
|
17
|
+
case 'function_declaration':
|
|
18
|
+
case 'generator_function_declaration': {
|
|
19
|
+
const name = node.childForFieldName('name')?.text;
|
|
20
|
+
if (!name)
|
|
21
|
+
return;
|
|
22
|
+
out.push(record(node, parsed, name, 'function'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
case 'class_declaration': {
|
|
26
|
+
const name = node.childForFieldName('name')?.text;
|
|
27
|
+
if (!name)
|
|
28
|
+
return;
|
|
29
|
+
out.push(record(node, parsed, name, 'class'));
|
|
30
|
+
extractClassMembers(node, parsed, name, out);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
case 'interface_declaration': {
|
|
34
|
+
const name = node.childForFieldName('name')?.text;
|
|
35
|
+
if (!name)
|
|
36
|
+
return;
|
|
37
|
+
out.push(record(node, parsed, name, 'interface'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case 'type_alias_declaration': {
|
|
41
|
+
const name = node.childForFieldName('name')?.text;
|
|
42
|
+
if (!name)
|
|
43
|
+
return;
|
|
44
|
+
out.push(record(node, parsed, name, 'type'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
case 'enum_declaration': {
|
|
48
|
+
const name = node.childForFieldName('name')?.text;
|
|
49
|
+
if (!name)
|
|
50
|
+
return;
|
|
51
|
+
out.push(record(node, parsed, name, 'enum'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
case 'variable_declarator': {
|
|
55
|
+
// Only emit if value is a function-like (matches Day-2 listFunctions logic).
|
|
56
|
+
const valueNode = node.childForFieldName('value');
|
|
57
|
+
if (!valueNode ||
|
|
58
|
+
!(valueNode.type === 'arrow_function' ||
|
|
59
|
+
valueNode.type === 'function_expression' ||
|
|
60
|
+
valueNode.type === 'function')) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const name = node.childForFieldName('name')?.text;
|
|
64
|
+
if (!name)
|
|
65
|
+
return;
|
|
66
|
+
out.push(record(node, parsed, name, 'variable'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
default:
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function extractClassMembers(classNode, parsed, className, out) {
|
|
74
|
+
const body = classNode.childForFieldName('body');
|
|
75
|
+
if (!body)
|
|
76
|
+
return;
|
|
77
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
78
|
+
const member = body.child(i);
|
|
79
|
+
if (!member)
|
|
80
|
+
continue;
|
|
81
|
+
if (member.type === 'method_definition' || member.type === 'method_signature') {
|
|
82
|
+
const name = member.childForFieldName('name')?.text;
|
|
83
|
+
if (!name)
|
|
84
|
+
continue;
|
|
85
|
+
out.push(record(member, parsed, name, 'method', className));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function record(node, parsed, name, kind, parent) {
|
|
90
|
+
const line = node.startPosition.row + 1;
|
|
91
|
+
const column = node.startPosition.column + 1;
|
|
92
|
+
const endLine = node.endPosition.row + 1;
|
|
93
|
+
const signature = oneLineSignature(node, parsed.source);
|
|
94
|
+
const exported = isExported(node);
|
|
95
|
+
const id = `${parsed.path}#${parent ? parent + '.' : ''}${name}@${line}`;
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
name,
|
|
99
|
+
kind,
|
|
100
|
+
file: parsed.path,
|
|
101
|
+
line,
|
|
102
|
+
column,
|
|
103
|
+
endLine,
|
|
104
|
+
signature,
|
|
105
|
+
exported,
|
|
106
|
+
parent,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Extract a single-line signature from the node. Takes the first line of
|
|
111
|
+
* text up to the body/value, capped at 200 chars. Secrets matching known
|
|
112
|
+
* patterns (T3 defence) are redacted before the line is returned.
|
|
113
|
+
*/
|
|
114
|
+
function oneLineSignature(node, source) {
|
|
115
|
+
// For variable_declarator, climb to the parent lexical/var declaration so we
|
|
116
|
+
// capture "export const foo = ..." rather than just "foo = ...".
|
|
117
|
+
let target = node;
|
|
118
|
+
if (node.type === 'variable_declarator' && node.parent) {
|
|
119
|
+
target = node.parent;
|
|
120
|
+
}
|
|
121
|
+
const raw = source.slice(target.startIndex, target.endIndex);
|
|
122
|
+
const firstLine = raw.split('\n')[0].trim();
|
|
123
|
+
const capped = firstLine.length > 200 ? firstLine.slice(0, 197) + '...' : firstLine;
|
|
124
|
+
// T3: redact known-format secrets (API keys, tokens, JWTs, PEM headers).
|
|
125
|
+
return redactSecrets(capped);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A node is exported if it (or an ancestor variable/lexical decl) is wrapped
|
|
129
|
+
* in an `export_statement`.
|
|
130
|
+
*/
|
|
131
|
+
function isExported(node) {
|
|
132
|
+
let cur = node;
|
|
133
|
+
while (cur) {
|
|
134
|
+
if (cur.type === 'export_statement')
|
|
135
|
+
return true;
|
|
136
|
+
cur = cur.parent;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.js","sourceRoot":"","sources":["../src/symbols.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Max bytes per source file we will read. Anything larger is skipped. */
|
|
2
|
+
export declare const MAX_FILE_BYTES: number;
|
|
3
|
+
/** Max number of files we will index in one run. Hard fail above this. */
|
|
4
|
+
export declare const MAX_FILES_PER_INDEX = 50000;
|
|
5
|
+
/**
|
|
6
|
+
* Returns null if the path is safe to index, or a human-readable reason string
|
|
7
|
+
* if it should be refused.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateRootPath(rawPath: string): string | null;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
/** Max bytes per source file we will read. Anything larger is skipped. */
|
|
5
|
+
export const MAX_FILE_BYTES = 5 * 1024 * 1024; // 5 MB
|
|
6
|
+
/** Max number of files we will index in one run. Hard fail above this. */
|
|
7
|
+
export const MAX_FILES_PER_INDEX = 50_000;
|
|
8
|
+
/**
|
|
9
|
+
* System paths we refuse to index. Indexing these by accident would walk the
|
|
10
|
+
* whole machine, fill disk, and leak system files into ~/.graphpilot/.
|
|
11
|
+
*
|
|
12
|
+
* Each entry must match the *realpath* form (after `fs.realpathSync`). macOS
|
|
13
|
+
* symlinks /etc, /var, /tmp to /private/*, so we include both forms here.
|
|
14
|
+
*/
|
|
15
|
+
const DANGEROUS_PATHS = new Set([
|
|
16
|
+
'/',
|
|
17
|
+
'/bin',
|
|
18
|
+
'/sbin',
|
|
19
|
+
'/usr',
|
|
20
|
+
'/usr/bin',
|
|
21
|
+
'/usr/local',
|
|
22
|
+
'/etc',
|
|
23
|
+
'/var',
|
|
24
|
+
'/tmp',
|
|
25
|
+
'/private',
|
|
26
|
+
'/private/etc',
|
|
27
|
+
'/private/var',
|
|
28
|
+
'/private/tmp',
|
|
29
|
+
'/Library',
|
|
30
|
+
'/System',
|
|
31
|
+
'/Applications',
|
|
32
|
+
'/Volumes',
|
|
33
|
+
'/Users',
|
|
34
|
+
'/home',
|
|
35
|
+
'C:\\Windows',
|
|
36
|
+
'C:\\Windows\\System32',
|
|
37
|
+
'C:\\Program Files',
|
|
38
|
+
'C:\\Program Files (x86)',
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Returns null if the path is safe to index, or a human-readable reason string
|
|
42
|
+
* if it should be refused.
|
|
43
|
+
*/
|
|
44
|
+
export function validateRootPath(rawPath) {
|
|
45
|
+
const abs = resolve(rawPath);
|
|
46
|
+
let real;
|
|
47
|
+
try {
|
|
48
|
+
real = realpathSync(abs);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return `Path does not exist or is not accessible: ${abs}`;
|
|
52
|
+
}
|
|
53
|
+
// Reject any bare Windows drive root (C:\, D:\, etc.) — not just C:\
|
|
54
|
+
if (process.platform === 'win32' && /^[A-Za-z]:\\?$/.test(real)) {
|
|
55
|
+
return `Refusing to index system path: ${real}`;
|
|
56
|
+
}
|
|
57
|
+
if (DANGEROUS_PATHS.has(real)) {
|
|
58
|
+
return `Refusing to index system path: ${real}`;
|
|
59
|
+
}
|
|
60
|
+
if (real === homedir()) {
|
|
61
|
+
return `Refusing to index your home directory directly (${real}). Pass a specific project subdirectory instead.`;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEtD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,GAAG;IACH,MAAM;IACN,OAAO;IACP,MAAM;IACN,UAAU;IACV,YAAY;IACZ,MAAM;IACN,MAAM;IACN,MAAM;IACN,UAAU;IACV,cAAc;IACd,cAAc;IACd,cAAc;IACd,UAAU;IACV,SAAS;IACT,eAAe;IACf,UAAU;IACV,QAAQ;IACR,OAAO;IACP,aAAa;IACb,uBAAuB;IACvB,mBAAmB;IACnB,yBAAyB;CAC1B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,6CAA6C,GAAG,EAAE,CAAC;IAC5D,CAAC;IAED,qEAAqE;IACrE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,OAAO,kCAAkC,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,kCAAkC,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,IAAI,KAAK,OAAO,EAAE,EAAE,CAAC;QACvB,OAAO,mDAAmD,IAAI,kDAAkD,CAAC;IACnH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-rolled validation for MCP tool inputs. No external deps — three tools,
|
|
3
|
+
* each with a handful of fields, makes a library overkill.
|
|
4
|
+
*
|
|
5
|
+
* Every tool validator returns a tagged result:
|
|
6
|
+
* { ok: true, value: <typed args> }
|
|
7
|
+
* { ok: false, error: <human-readable> }
|
|
8
|
+
*
|
|
9
|
+
* Rules (defence-in-depth — JSON schema is declared in the tool catalog
|
|
10
|
+
* too; this is the second wall):
|
|
11
|
+
* - Reject extra unknown keys (defends against agent typos & tampering)
|
|
12
|
+
* - Type-check every field
|
|
13
|
+
* - Range-check numbers (limit is bounded, no NaN)
|
|
14
|
+
* - Length-cap strings (no 10MB symbol names)
|
|
15
|
+
* - Strict enums (no surprise direction values)
|
|
16
|
+
*/
|
|
17
|
+
export type Result<T> = {
|
|
18
|
+
ok: true;
|
|
19
|
+
value: T;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
error: string;
|
|
23
|
+
};
|
|
24
|
+
export interface GpIndexArgs {
|
|
25
|
+
path?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function validateGpIndex(input: unknown): Result<GpIndexArgs>;
|
|
28
|
+
export interface GpRecallArgs {
|
|
29
|
+
query: string;
|
|
30
|
+
limit?: number;
|
|
31
|
+
substring?: boolean;
|
|
32
|
+
path?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function validateGpRecall(input: unknown): Result<GpRecallArgs>;
|
|
35
|
+
export type CallersDirection = 'callers' | 'callees';
|
|
36
|
+
export interface GpCallersArgs {
|
|
37
|
+
symbol: string;
|
|
38
|
+
direction?: CallersDirection;
|
|
39
|
+
limit?: number;
|
|
40
|
+
includeUnresolved?: boolean;
|
|
41
|
+
path?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function validateGpCallers(input: unknown): Result<GpCallersArgs>;
|
|
44
|
+
export type ToolName = 'gp_index' | 'gp_recall' | 'gp_callers' | 'gp_impact' | 'gp_stats';
|
|
45
|
+
export interface GpStatsArgs {
|
|
46
|
+
path?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function validateGpStats(input: unknown): Result<GpStatsArgs>;
|
|
49
|
+
export interface GpImpactArgs {
|
|
50
|
+
symbol: string;
|
|
51
|
+
depth?: number;
|
|
52
|
+
path?: string;
|
|
53
|
+
since?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function validateGpImpact(input: unknown): Result<GpImpactArgs>;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-rolled validation for MCP tool inputs. No external deps — three tools,
|
|
3
|
+
* each with a handful of fields, makes a library overkill.
|
|
4
|
+
*
|
|
5
|
+
* Every tool validator returns a tagged result:
|
|
6
|
+
* { ok: true, value: <typed args> }
|
|
7
|
+
* { ok: false, error: <human-readable> }
|
|
8
|
+
*
|
|
9
|
+
* Rules (defence-in-depth — JSON schema is declared in the tool catalog
|
|
10
|
+
* too; this is the second wall):
|
|
11
|
+
* - Reject extra unknown keys (defends against agent typos & tampering)
|
|
12
|
+
* - Type-check every field
|
|
13
|
+
* - Range-check numbers (limit is bounded, no NaN)
|
|
14
|
+
* - Length-cap strings (no 10MB symbol names)
|
|
15
|
+
* - Strict enums (no surprise direction values)
|
|
16
|
+
*/
|
|
17
|
+
function ok(value) {
|
|
18
|
+
return { ok: true, value };
|
|
19
|
+
}
|
|
20
|
+
function fail(error) {
|
|
21
|
+
return { ok: false, error };
|
|
22
|
+
}
|
|
23
|
+
function isPlainObject(x) {
|
|
24
|
+
return typeof x === 'object' && x !== null && !Array.isArray(x);
|
|
25
|
+
}
|
|
26
|
+
const MAX_STRING_LEN = 2_000;
|
|
27
|
+
function pickString(obj, key, opts = {}) {
|
|
28
|
+
const raw = obj[key];
|
|
29
|
+
if (raw === undefined) {
|
|
30
|
+
if (opts.required)
|
|
31
|
+
return fail(`Missing required field: ${key}`);
|
|
32
|
+
return ok(undefined);
|
|
33
|
+
}
|
|
34
|
+
if (typeof raw !== 'string')
|
|
35
|
+
return fail(`${key} must be a string`);
|
|
36
|
+
const max = opts.max ?? MAX_STRING_LEN;
|
|
37
|
+
if (raw.length > max)
|
|
38
|
+
return fail(`${key} exceeds max length of ${max}`);
|
|
39
|
+
return ok(raw);
|
|
40
|
+
}
|
|
41
|
+
function pickNumber(obj, key, opts = {}) {
|
|
42
|
+
const raw = obj[key];
|
|
43
|
+
if (raw === undefined)
|
|
44
|
+
return ok(undefined);
|
|
45
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
|
|
46
|
+
return fail(`${key} must be a finite number`);
|
|
47
|
+
}
|
|
48
|
+
if (opts.integer && !Number.isInteger(raw)) {
|
|
49
|
+
return fail(`${key} must be an integer`);
|
|
50
|
+
}
|
|
51
|
+
if (opts.min !== undefined && raw < opts.min) {
|
|
52
|
+
return fail(`${key} must be >= ${opts.min}`);
|
|
53
|
+
}
|
|
54
|
+
if (opts.max !== undefined && raw > opts.max) {
|
|
55
|
+
return fail(`${key} must be <= ${opts.max}`);
|
|
56
|
+
}
|
|
57
|
+
return ok(raw);
|
|
58
|
+
}
|
|
59
|
+
function pickBoolean(obj, key) {
|
|
60
|
+
const raw = obj[key];
|
|
61
|
+
if (raw === undefined)
|
|
62
|
+
return ok(undefined);
|
|
63
|
+
if (typeof raw !== 'boolean')
|
|
64
|
+
return fail(`${key} must be a boolean`);
|
|
65
|
+
return ok(raw);
|
|
66
|
+
}
|
|
67
|
+
function pickEnum(obj, key, allowed) {
|
|
68
|
+
const raw = obj[key];
|
|
69
|
+
if (raw === undefined)
|
|
70
|
+
return ok(undefined);
|
|
71
|
+
if (typeof raw !== 'string' || !allowed.includes(raw)) {
|
|
72
|
+
return fail(`${key} must be one of: ${allowed.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
return ok(raw);
|
|
75
|
+
}
|
|
76
|
+
function rejectExtraKeys(obj, allowed) {
|
|
77
|
+
const extras = Object.keys(obj).filter((k) => !allowed.includes(k));
|
|
78
|
+
if (extras.length > 0) {
|
|
79
|
+
return fail(`Unknown field(s): ${extras.join(', ')}. Allowed: ${allowed.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
return ok(true);
|
|
82
|
+
}
|
|
83
|
+
const GP_INDEX_KEYS = ['path'];
|
|
84
|
+
export function validateGpIndex(input) {
|
|
85
|
+
if (!isPlainObject(input))
|
|
86
|
+
return fail('arguments must be an object');
|
|
87
|
+
const extras = rejectExtraKeys(input, GP_INDEX_KEYS);
|
|
88
|
+
if (!extras.ok)
|
|
89
|
+
return fail(extras.error);
|
|
90
|
+
const path = pickString(input, 'path', { max: 1024 });
|
|
91
|
+
if (!path.ok)
|
|
92
|
+
return fail(path.error);
|
|
93
|
+
return ok({ path: path.value });
|
|
94
|
+
}
|
|
95
|
+
const GP_RECALL_KEYS = ['query', 'limit', 'substring', 'path'];
|
|
96
|
+
export function validateGpRecall(input) {
|
|
97
|
+
if (!isPlainObject(input))
|
|
98
|
+
return fail('arguments must be an object');
|
|
99
|
+
const extras = rejectExtraKeys(input, GP_RECALL_KEYS);
|
|
100
|
+
if (!extras.ok)
|
|
101
|
+
return fail(extras.error);
|
|
102
|
+
const query = pickString(input, 'query', { required: true, max: 200 });
|
|
103
|
+
if (!query.ok)
|
|
104
|
+
return fail(query.error);
|
|
105
|
+
if (!query.value || query.value.trim() === '') {
|
|
106
|
+
return fail('query must be a non-empty string');
|
|
107
|
+
}
|
|
108
|
+
const limit = pickNumber(input, 'limit', { min: 1, max: 50, integer: true });
|
|
109
|
+
if (!limit.ok)
|
|
110
|
+
return fail(limit.error);
|
|
111
|
+
const substring = pickBoolean(input, 'substring');
|
|
112
|
+
if (!substring.ok)
|
|
113
|
+
return fail(substring.error);
|
|
114
|
+
const path = pickString(input, 'path', { max: 1024 });
|
|
115
|
+
if (!path.ok)
|
|
116
|
+
return fail(path.error);
|
|
117
|
+
return ok({
|
|
118
|
+
query: query.value,
|
|
119
|
+
limit: limit.value,
|
|
120
|
+
substring: substring.value,
|
|
121
|
+
path: path.value,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const GP_CALLERS_KEYS = ['symbol', 'direction', 'limit', 'includeUnresolved', 'path'];
|
|
125
|
+
export function validateGpCallers(input) {
|
|
126
|
+
if (!isPlainObject(input))
|
|
127
|
+
return fail('arguments must be an object');
|
|
128
|
+
const extras = rejectExtraKeys(input, GP_CALLERS_KEYS);
|
|
129
|
+
if (!extras.ok)
|
|
130
|
+
return fail(extras.error);
|
|
131
|
+
const symbol = pickString(input, 'symbol', { required: true, max: 500 });
|
|
132
|
+
if (!symbol.ok)
|
|
133
|
+
return fail(symbol.error);
|
|
134
|
+
if (!symbol.value || symbol.value.trim() === '') {
|
|
135
|
+
return fail('symbol must be a non-empty string');
|
|
136
|
+
}
|
|
137
|
+
const direction = pickEnum(input, 'direction', ['callers', 'callees']);
|
|
138
|
+
if (!direction.ok)
|
|
139
|
+
return fail(direction.error);
|
|
140
|
+
const limit = pickNumber(input, 'limit', { min: 1, max: 100, integer: true });
|
|
141
|
+
if (!limit.ok)
|
|
142
|
+
return fail(limit.error);
|
|
143
|
+
const includeUnresolved = pickBoolean(input, 'includeUnresolved');
|
|
144
|
+
if (!includeUnresolved.ok)
|
|
145
|
+
return fail(includeUnresolved.error);
|
|
146
|
+
const path = pickString(input, 'path', { max: 1024 });
|
|
147
|
+
if (!path.ok)
|
|
148
|
+
return fail(path.error);
|
|
149
|
+
return ok({
|
|
150
|
+
symbol: symbol.value,
|
|
151
|
+
direction: direction.value,
|
|
152
|
+
limit: limit.value,
|
|
153
|
+
includeUnresolved: includeUnresolved.value,
|
|
154
|
+
path: path.value,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
export function validateGpStats(input) {
|
|
158
|
+
if (!isPlainObject(input))
|
|
159
|
+
return fail('arguments must be an object');
|
|
160
|
+
const extras = rejectExtraKeys(input, ['path']);
|
|
161
|
+
if (!extras.ok)
|
|
162
|
+
return fail(extras.error);
|
|
163
|
+
const path = pickString(input, 'path', { max: 1024 });
|
|
164
|
+
if (!path.ok)
|
|
165
|
+
return fail(path.error);
|
|
166
|
+
return ok({ path: path.value });
|
|
167
|
+
}
|
|
168
|
+
const GP_IMPACT_KEYS = ['symbol', 'depth', 'path', 'since'];
|
|
169
|
+
export function validateGpImpact(input) {
|
|
170
|
+
if (!isPlainObject(input))
|
|
171
|
+
return fail('arguments must be an object');
|
|
172
|
+
const extras = rejectExtraKeys(input, GP_IMPACT_KEYS);
|
|
173
|
+
if (!extras.ok)
|
|
174
|
+
return fail(extras.error);
|
|
175
|
+
const symbol = pickString(input, 'symbol', { required: true, max: 500 });
|
|
176
|
+
if (!symbol.ok)
|
|
177
|
+
return fail(symbol.error);
|
|
178
|
+
if (!symbol.value || symbol.value.trim() === '') {
|
|
179
|
+
return fail('symbol must be a non-empty string');
|
|
180
|
+
}
|
|
181
|
+
// BFS depth: hard-cap at 5. Deeper traversals explode in big repos and
|
|
182
|
+
// rarely add value — depth-3 already covers ~99% of real refactors.
|
|
183
|
+
const depth = pickNumber(input, 'depth', { min: 1, max: 5, integer: true });
|
|
184
|
+
if (!depth.ok)
|
|
185
|
+
return fail(depth.error);
|
|
186
|
+
const path = pickString(input, 'path', { max: 1024 });
|
|
187
|
+
if (!path.ok)
|
|
188
|
+
return fail(path.error);
|
|
189
|
+
// `since` accepts a commit SHA (full or short), tag, or branch name.
|
|
190
|
+
// Capped at 200 chars — refs are normally under 100, this is a sanity
|
|
191
|
+
// bound, not a security one (isomorphic-git handles ref resolution).
|
|
192
|
+
const since = pickString(input, 'since', { max: 200 });
|
|
193
|
+
if (!since.ok)
|
|
194
|
+
return fail(since.error);
|
|
195
|
+
if (since.value !== undefined && since.value.trim() === '') {
|
|
196
|
+
return fail('since must be a non-empty string when provided');
|
|
197
|
+
}
|
|
198
|
+
return ok({
|
|
199
|
+
symbol: symbol.value,
|
|
200
|
+
depth: depth.value,
|
|
201
|
+
path: path.value,
|
|
202
|
+
since: since.value,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"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"}
|