septum 0.1.0 → 0.1.2
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/LICENSE +21 -21
- package/README.md +100 -100
- package/bin/septum.ts +4 -4
- package/package.json +63 -62
- package/src/cli/commands/check.ts +161 -161
- package/src/cli/commands/hook.ts +188 -188
- package/src/cli/commands/ingest.ts +29 -29
- package/src/cli/commands/init.ts +211 -211
- package/src/cli/commands/locate.ts +61 -61
- package/src/cli/commands/query.ts +64 -64
- package/src/cli/commands/serve.ts +5 -5
- package/src/cli/commands/slice.ts +90 -90
- package/src/cli/commands/sync.ts +31 -31
- package/src/cli/index.ts +199 -178
- package/src/cli/templates/hooks.ts +75 -75
- package/src/cli/templates/rules.ts +38 -38
- package/src/cli/templates/skill.ts +66 -66
- package/src/core/boundary/evaluator.ts +210 -210
- package/src/core/config/loader.ts +84 -84
- package/src/core/config/schema.ts +39 -39
- package/src/core/database/client.ts +44 -132
- package/src/core/database/repositories/dependency.repository.ts +279 -279
- package/src/core/database/repositories/domain.repository.ts +198 -198
- package/src/core/database/repositories/file.repository.ts +99 -99
- package/src/core/database/repositories/meta.repository.ts +33 -33
- package/src/core/database/repositories/symbol.repository.ts +424 -424
- package/src/core/database/repository.ts +242 -242
- package/src/core/database/schema.ts +120 -0
- package/src/core/discovery/topology-detector.ts +418 -418
- package/src/core/ingestion/hasher.ts +5 -5
- package/src/core/ingestion/pipeline.ts +438 -438
- package/src/core/parser/boundary-tracker.ts +128 -128
- package/src/core/parser/extractors/base.ts +60 -60
- package/src/core/parser/extractors/frontend.ts +194 -194
- package/src/core/parser/extractors/go.ts +176 -176
- package/src/core/parser/extractors/laravel-semantic.ts +426 -426
- package/src/core/parser/extractors/mcp-cli-semantic.ts +199 -199
- package/src/core/parser/extractors/nestjs-semantic.ts +119 -119
- package/src/core/parser/extractors/php.ts +149 -149
- package/src/core/parser/extractors/python.ts +226 -226
- package/src/core/parser/extractors/registry.ts +49 -49
- package/src/core/parser/extractors/semantic-extractor.interface.ts +21 -21
- package/src/core/parser/extractors/typescript.ts +177 -177
- package/src/core/parser/tree-sitter.ts +39 -39
- package/src/core/resolver/call-graph-tracer.ts +668 -668
- package/src/core/resolver/module-resolver.ts +342 -323
- package/src/core/resolver/symbol-locator.ts +543 -405
- package/src/core/resolver/vertical-slice-tracer.ts +237 -236
- package/src/core/session/session-manager.ts +56 -56
- package/src/core/telemetry/telemetry.ts +86 -86
- package/src/index.ts +18 -18
- package/src/mcp/schemas.ts +68 -68
- package/src/mcp/server.ts +612 -602
- package/src/mcp/tools/check-boundary.ts +102 -102
- package/src/mcp/tools/get-domain-catalog.ts +91 -91
- package/src/mcp/tools/get-feature-context.ts +116 -116
- package/src/mcp/tools/get-symbol-hotspots.ts +49 -49
- package/src/mcp/tools/get-symbol-impact.ts +26 -26
- package/src/mcp/tools/get-symbol.ts +63 -63
- package/src/mcp/tools/locate-symbol.ts +74 -74
- package/src/mcp/tools/register-domain.ts +64 -64
- package/src/mcp/tools/trace-vertical-slice.ts +102 -102
- package/src/types/index.ts +296 -296
- package/src/version.ts +3 -0
- package/src/core/database/schema.sql +0 -92
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
import type { ExtractedDependency, ExtractedSymbol, ParsedFileAST, Visibility } from "../../../types/index.ts";
|
|
2
|
-
import { findBraceBlockEnd } from "../boundary-tracker.ts";
|
|
3
|
-
import type { CodeExtractor } from "./base.ts";
|
|
4
|
-
|
|
5
|
-
export class PHPExtractor implements CodeExtractor {
|
|
6
|
-
public canHandle(filePath: string): boolean {
|
|
7
|
-
return /\.php$/i.test(filePath);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public async extract(filePath: string, content: string): Promise<ParsedFileAST> {
|
|
11
|
-
const symbols: ExtractedSymbol[] = [];
|
|
12
|
-
const dependencies: ExtractedDependency[] = [];
|
|
13
|
-
|
|
14
|
-
const lines = content.split("\n");
|
|
15
|
-
|
|
16
|
-
// 1. Extract PHP use / import statements
|
|
17
|
-
const useRegex = /^\s*use\s+([^;]+);/;
|
|
18
|
-
for (let i = 0; i < lines.length; i++) {
|
|
19
|
-
const line = lines[i];
|
|
20
|
-
const match = useRegex.exec(line);
|
|
21
|
-
if (match) {
|
|
22
|
-
const fullTarget = match[1].trim();
|
|
23
|
-
// Remove 'function ' or 'const ' prefix if any
|
|
24
|
-
const cleaned = fullTarget.replace(/^(function|const)\s+/, "");
|
|
25
|
-
const isExternal =
|
|
26
|
-
cleaned.startsWith("Illuminate\\") ||
|
|
27
|
-
cleaned.startsWith("Symfony\\") ||
|
|
28
|
-
cleaned.startsWith("Psr\\") ||
|
|
29
|
-
cleaned.startsWith("Composer\\");
|
|
30
|
-
|
|
31
|
-
dependencies.push({
|
|
32
|
-
target: cleaned,
|
|
33
|
-
statement: line.trim(),
|
|
34
|
-
line_number: i + 1,
|
|
35
|
-
is_external: isExternal,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 2. Extract PHP Class / Interface / Trait / Enum
|
|
41
|
-
let currentContainer: string | null = null;
|
|
42
|
-
let currentContainerEndLine = 0;
|
|
43
|
-
|
|
44
|
-
for (let i = 0; i < lines.length; i++) {
|
|
45
|
-
const line = lines[i];
|
|
46
|
-
const trimmed = line.trim();
|
|
47
|
-
const lineNum = i + 1;
|
|
48
|
-
|
|
49
|
-
if (currentContainer && lineNum > currentContainerEndLine) {
|
|
50
|
-
currentContainer = null;
|
|
51
|
-
currentContainerEndLine = 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Class / Interface / Trait / Enum header
|
|
55
|
-
const classMatch = /^(?:abstract\s+|final\s+|readonly\s+)*(class|interface|trait|enum)\s+([A-Za-z0-9_]+)/.exec(trimmed);
|
|
56
|
-
if (classMatch) {
|
|
57
|
-
currentContainer = classMatch[2];
|
|
58
|
-
const endLine = findBraceBlockEnd(lines, i);
|
|
59
|
-
currentContainerEndLine = endLine;
|
|
60
|
-
symbols.push({
|
|
61
|
-
name: classMatch[2],
|
|
62
|
-
kind: classMatch[1] as any,
|
|
63
|
-
signature: trimmed.replace(/\{$/, "").trim(),
|
|
64
|
-
visibility: "public",
|
|
65
|
-
line_start: lineNum,
|
|
66
|
-
line_end: endLine,
|
|
67
|
-
line_count: Math.max(1, endLine - lineNum + 1),
|
|
68
|
-
});
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Methods or Properties in class/interface/trait/enum
|
|
73
|
-
if (currentContainer) {
|
|
74
|
-
// PHP Constant: const STATUS_ACTIVE = 'active';
|
|
75
|
-
const constMatch = /^(?:(public|protected|private)\s+)?const\s+([A-Za-z0-9_]+)\s*=/.exec(trimmed);
|
|
76
|
-
if (constMatch) {
|
|
77
|
-
const vis = (constMatch[1] as Visibility) || "public";
|
|
78
|
-
const constName = constMatch[2];
|
|
79
|
-
symbols.push({
|
|
80
|
-
name: `${currentContainer}::${constName}`,
|
|
81
|
-
kind: "property",
|
|
82
|
-
signature: `${vis} const ${constName}`,
|
|
83
|
-
visibility: vis,
|
|
84
|
-
line_start: lineNum,
|
|
85
|
-
line_end: lineNum,
|
|
86
|
-
line_count: 1,
|
|
87
|
-
});
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// PHP Property: public ?string $foo = null; or private readonly Bar $bar;
|
|
92
|
-
const propMatch = /^(?:(public|protected|private)\s+)?(?:readonly\s+)?(?:static\s+)?(?:\??[A-Za-z0-9_\\\\]+\s+)?\$([A-Za-z0-9_]+)(?:\s*=[^;]+)?;\s*$/.exec(trimmed);
|
|
93
|
-
if (propMatch && !trimmed.includes("function ")) {
|
|
94
|
-
const vis = (propMatch[1] as Visibility) || "public";
|
|
95
|
-
const propName = `$${propMatch[2]}`;
|
|
96
|
-
symbols.push({
|
|
97
|
-
name: `${currentContainer}::${propName}`,
|
|
98
|
-
kind: "property",
|
|
99
|
-
signature: `${vis} ${trimmed.replace(/;\s*$/, "")}`,
|
|
100
|
-
visibility: vis,
|
|
101
|
-
line_start: lineNum,
|
|
102
|
-
line_end: lineNum,
|
|
103
|
-
line_count: 1,
|
|
104
|
-
});
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const methodMatch = /^(?:(public|protected|private)\s+)?(?:static\s+)?(?:final\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)(?:\s*:\s*([^;{]+))?/.exec(trimmed);
|
|
109
|
-
if (methodMatch) {
|
|
110
|
-
const vis = (methodMatch[1] as Visibility) || "public";
|
|
111
|
-
const name = methodMatch[2];
|
|
112
|
-
const params = methodMatch[3].trim();
|
|
113
|
-
const retType = methodMatch[4] ? `: ${methodMatch[4].trim()}` : "";
|
|
114
|
-
const endLine = findBraceBlockEnd(lines, i);
|
|
115
|
-
symbols.push({
|
|
116
|
-
name: `${currentContainer}::${name}`,
|
|
117
|
-
kind: "method",
|
|
118
|
-
signature: `${vis} function ${name}(${params})${retType}`,
|
|
119
|
-
visibility: vis,
|
|
120
|
-
line_start: lineNum,
|
|
121
|
-
line_end: endLine,
|
|
122
|
-
line_count: Math.max(1, endLine - lineNum + 1),
|
|
123
|
-
});
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Standalone Global Function in PHP
|
|
129
|
-
const funcMatch = /^(?:final\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)(?:\s*:\s*([^;{]+))?/.exec(trimmed);
|
|
130
|
-
if (funcMatch && !currentContainer) {
|
|
131
|
-
const name = funcMatch[1];
|
|
132
|
-
const params = funcMatch[2].trim();
|
|
133
|
-
const retType = funcMatch[3] ? `: ${funcMatch[3].trim()}` : "";
|
|
134
|
-
const endLine = findBraceBlockEnd(lines, i);
|
|
135
|
-
symbols.push({
|
|
136
|
-
name,
|
|
137
|
-
kind: "function",
|
|
138
|
-
signature: `function ${name}(${params})${retType}`,
|
|
139
|
-
visibility: "public",
|
|
140
|
-
line_start: lineNum,
|
|
141
|
-
line_end: endLine,
|
|
142
|
-
line_count: Math.max(1, endLine - lineNum + 1),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return { symbols, dependencies };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
1
|
+
import type { ExtractedDependency, ExtractedSymbol, ParsedFileAST, Visibility } from "../../../types/index.ts";
|
|
2
|
+
import { findBraceBlockEnd } from "../boundary-tracker.ts";
|
|
3
|
+
import type { CodeExtractor } from "./base.ts";
|
|
4
|
+
|
|
5
|
+
export class PHPExtractor implements CodeExtractor {
|
|
6
|
+
public canHandle(filePath: string): boolean {
|
|
7
|
+
return /\.php$/i.test(filePath);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public async extract(filePath: string, content: string): Promise<ParsedFileAST> {
|
|
11
|
+
const symbols: ExtractedSymbol[] = [];
|
|
12
|
+
const dependencies: ExtractedDependency[] = [];
|
|
13
|
+
|
|
14
|
+
const lines = content.split("\n");
|
|
15
|
+
|
|
16
|
+
// 1. Extract PHP use / import statements
|
|
17
|
+
const useRegex = /^\s*use\s+([^;]+);/;
|
|
18
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19
|
+
const line = lines[i];
|
|
20
|
+
const match = useRegex.exec(line);
|
|
21
|
+
if (match) {
|
|
22
|
+
const fullTarget = match[1].trim();
|
|
23
|
+
// Remove 'function ' or 'const ' prefix if any
|
|
24
|
+
const cleaned = fullTarget.replace(/^(function|const)\s+/, "");
|
|
25
|
+
const isExternal =
|
|
26
|
+
cleaned.startsWith("Illuminate\\") ||
|
|
27
|
+
cleaned.startsWith("Symfony\\") ||
|
|
28
|
+
cleaned.startsWith("Psr\\") ||
|
|
29
|
+
cleaned.startsWith("Composer\\");
|
|
30
|
+
|
|
31
|
+
dependencies.push({
|
|
32
|
+
target: cleaned,
|
|
33
|
+
statement: line.trim(),
|
|
34
|
+
line_number: i + 1,
|
|
35
|
+
is_external: isExternal,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Extract PHP Class / Interface / Trait / Enum
|
|
41
|
+
let currentContainer: string | null = null;
|
|
42
|
+
let currentContainerEndLine = 0;
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < lines.length; i++) {
|
|
45
|
+
const line = lines[i];
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
const lineNum = i + 1;
|
|
48
|
+
|
|
49
|
+
if (currentContainer && lineNum > currentContainerEndLine) {
|
|
50
|
+
currentContainer = null;
|
|
51
|
+
currentContainerEndLine = 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Class / Interface / Trait / Enum header
|
|
55
|
+
const classMatch = /^(?:abstract\s+|final\s+|readonly\s+)*(class|interface|trait|enum)\s+([A-Za-z0-9_]+)/.exec(trimmed);
|
|
56
|
+
if (classMatch) {
|
|
57
|
+
currentContainer = classMatch[2];
|
|
58
|
+
const endLine = findBraceBlockEnd(lines, i);
|
|
59
|
+
currentContainerEndLine = endLine;
|
|
60
|
+
symbols.push({
|
|
61
|
+
name: classMatch[2],
|
|
62
|
+
kind: classMatch[1] as any,
|
|
63
|
+
signature: trimmed.replace(/\{$/, "").trim(),
|
|
64
|
+
visibility: "public",
|
|
65
|
+
line_start: lineNum,
|
|
66
|
+
line_end: endLine,
|
|
67
|
+
line_count: Math.max(1, endLine - lineNum + 1),
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Methods or Properties in class/interface/trait/enum
|
|
73
|
+
if (currentContainer) {
|
|
74
|
+
// PHP Constant: const STATUS_ACTIVE = 'active';
|
|
75
|
+
const constMatch = /^(?:(public|protected|private)\s+)?const\s+([A-Za-z0-9_]+)\s*=/.exec(trimmed);
|
|
76
|
+
if (constMatch) {
|
|
77
|
+
const vis = (constMatch[1] as Visibility) || "public";
|
|
78
|
+
const constName = constMatch[2];
|
|
79
|
+
symbols.push({
|
|
80
|
+
name: `${currentContainer}::${constName}`,
|
|
81
|
+
kind: "property",
|
|
82
|
+
signature: `${vis} const ${constName}`,
|
|
83
|
+
visibility: vis,
|
|
84
|
+
line_start: lineNum,
|
|
85
|
+
line_end: lineNum,
|
|
86
|
+
line_count: 1,
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// PHP Property: public ?string $foo = null; or private readonly Bar $bar;
|
|
92
|
+
const propMatch = /^(?:(public|protected|private)\s+)?(?:readonly\s+)?(?:static\s+)?(?:\??[A-Za-z0-9_\\\\]+\s+)?\$([A-Za-z0-9_]+)(?:\s*=[^;]+)?;\s*$/.exec(trimmed);
|
|
93
|
+
if (propMatch && !trimmed.includes("function ")) {
|
|
94
|
+
const vis = (propMatch[1] as Visibility) || "public";
|
|
95
|
+
const propName = `$${propMatch[2]}`;
|
|
96
|
+
symbols.push({
|
|
97
|
+
name: `${currentContainer}::${propName}`,
|
|
98
|
+
kind: "property",
|
|
99
|
+
signature: `${vis} ${trimmed.replace(/;\s*$/, "")}`,
|
|
100
|
+
visibility: vis,
|
|
101
|
+
line_start: lineNum,
|
|
102
|
+
line_end: lineNum,
|
|
103
|
+
line_count: 1,
|
|
104
|
+
});
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const methodMatch = /^(?:(public|protected|private)\s+)?(?:static\s+)?(?:final\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)(?:\s*:\s*([^;{]+))?/.exec(trimmed);
|
|
109
|
+
if (methodMatch) {
|
|
110
|
+
const vis = (methodMatch[1] as Visibility) || "public";
|
|
111
|
+
const name = methodMatch[2];
|
|
112
|
+
const params = methodMatch[3].trim();
|
|
113
|
+
const retType = methodMatch[4] ? `: ${methodMatch[4].trim()}` : "";
|
|
114
|
+
const endLine = findBraceBlockEnd(lines, i);
|
|
115
|
+
symbols.push({
|
|
116
|
+
name: `${currentContainer}::${name}`,
|
|
117
|
+
kind: "method",
|
|
118
|
+
signature: `${vis} function ${name}(${params})${retType}`,
|
|
119
|
+
visibility: vis,
|
|
120
|
+
line_start: lineNum,
|
|
121
|
+
line_end: endLine,
|
|
122
|
+
line_count: Math.max(1, endLine - lineNum + 1),
|
|
123
|
+
});
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Standalone Global Function in PHP
|
|
129
|
+
const funcMatch = /^(?:final\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)(?:\s*:\s*([^;{]+))?/.exec(trimmed);
|
|
130
|
+
if (funcMatch && !currentContainer) {
|
|
131
|
+
const name = funcMatch[1];
|
|
132
|
+
const params = funcMatch[2].trim();
|
|
133
|
+
const retType = funcMatch[3] ? `: ${funcMatch[3].trim()}` : "";
|
|
134
|
+
const endLine = findBraceBlockEnd(lines, i);
|
|
135
|
+
symbols.push({
|
|
136
|
+
name,
|
|
137
|
+
kind: "function",
|
|
138
|
+
signature: `function ${name}(${params})${retType}`,
|
|
139
|
+
visibility: "public",
|
|
140
|
+
line_start: lineNum,
|
|
141
|
+
line_end: endLine,
|
|
142
|
+
line_count: Math.max(1, endLine - lineNum + 1),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { symbols, dependencies };
|
|
148
|
+
}
|
|
149
|
+
}
|