septum 0.1.3 → 0.1.5

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.
Files changed (74) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -102
  3. package/bin/septum.ts +4 -4
  4. package/package.json +63 -63
  5. package/src/cli/commands/check.ts +161 -161
  6. package/src/cli/commands/hook.ts +188 -188
  7. package/src/cli/commands/ingest.ts +29 -29
  8. package/src/cli/commands/init.ts +211 -211
  9. package/src/cli/commands/locate.ts +61 -61
  10. package/src/cli/commands/query.ts +64 -64
  11. package/src/cli/commands/serve.ts +5 -5
  12. package/src/cli/commands/slice.ts +48 -90
  13. package/src/cli/commands/sync.ts +31 -31
  14. package/src/cli/commands/topology.ts +119 -0
  15. package/src/cli/index.ts +210 -199
  16. package/src/cli/templates/hooks.ts +75 -75
  17. package/src/cli/templates/rules.ts +38 -38
  18. package/src/cli/templates/skill.ts +69 -69
  19. package/src/core/boundary/evaluator.ts +232 -210
  20. package/src/core/config/loader.ts +97 -97
  21. package/src/core/config/schema.ts +39 -39
  22. package/src/core/database/client.ts +44 -44
  23. package/src/core/database/repositories/dependency.repository.ts +366 -349
  24. package/src/core/database/repositories/domain.repository.ts +218 -198
  25. package/src/core/database/repositories/environment.repository.ts +207 -0
  26. package/src/core/database/repositories/file.repository.ts +116 -106
  27. package/src/core/database/repositories/meta.repository.ts +33 -33
  28. package/src/core/database/repositories/symbol.repository.ts +443 -424
  29. package/src/core/database/repository.ts +264 -255
  30. package/src/core/database/schema.ts +34 -1
  31. package/src/core/discovery/topology-detector.ts +486 -486
  32. package/src/core/ingestion/hasher.ts +5 -5
  33. package/src/core/ingestion/pipeline.ts +584 -535
  34. package/src/core/parser/behavioral-analyzer.ts +119 -0
  35. package/src/core/parser/boundary-tracker.ts +288 -128
  36. package/src/core/parser/extractors/base.ts +62 -61
  37. package/src/core/parser/extractors/frontend.ts +194 -194
  38. package/src/core/parser/extractors/go.ts +229 -176
  39. package/src/core/parser/extractors/laravel-semantic.ts +1039 -585
  40. package/src/core/parser/extractors/mcp-cli-semantic.ts +199 -199
  41. package/src/core/parser/extractors/nestjs-semantic.ts +119 -119
  42. package/src/core/parser/extractors/php.ts +153 -149
  43. package/src/core/parser/extractors/python.ts +228 -226
  44. package/src/core/parser/extractors/registry.ts +70 -70
  45. package/src/core/parser/extractors/semantic-extractor.interface.ts +21 -21
  46. package/src/core/parser/extractors/typescript.ts +181 -177
  47. package/src/core/parser/schema/providers/laravel-migration-provider.ts +215 -0
  48. package/src/core/parser/schema/providers/prisma-schema-provider.ts +89 -0
  49. package/src/core/parser/schema/schema-provider-registry.ts +43 -0
  50. package/src/core/parser/schema/schema-provider.interface.ts +33 -0
  51. package/src/core/parser/tree-sitter.ts +41 -41
  52. package/src/core/resolver/call-graph-tracer.ts +666 -668
  53. package/src/core/resolver/module-resolver.ts +408 -342
  54. package/src/core/resolver/symbol-locator.ts +543 -543
  55. package/src/core/resolver/vertical-slice-tracer.ts +301 -252
  56. package/src/core/session/session-manager.ts +56 -56
  57. package/src/core/telemetry/telemetry.ts +86 -86
  58. package/src/core/topology/environment-detector.ts +860 -0
  59. package/src/core/topology/types.ts +83 -0
  60. package/src/index.ts +21 -18
  61. package/src/mcp/schemas.ts +578 -238
  62. package/src/mcp/server.ts +875 -656
  63. package/src/mcp/tools/check-boundary.ts +171 -152
  64. package/src/mcp/tools/get-domain-catalog.ts +145 -121
  65. package/src/mcp/tools/get-environment-topology.ts +141 -0
  66. package/src/mcp/tools/get-feature-context.ts +212 -116
  67. package/src/mcp/tools/get-symbol-hotspots.ts +60 -49
  68. package/src/mcp/tools/get-symbol-impact.ts +28 -26
  69. package/src/mcp/tools/get-symbol.ts +65 -63
  70. package/src/mcp/tools/locate-symbol.ts +171 -190
  71. package/src/mcp/tools/register-domain.ts +68 -64
  72. package/src/mcp/tools/register-feature.ts +100 -0
  73. package/src/mcp/tools/trace-vertical-slice.ts +74 -103
  74. package/src/types/index.ts +335 -299
@@ -1,149 +1,153 @@
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, calculateControlFlowNesting } 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
+ const nestingDepth = calculateControlFlowNesting(lines, i, endLine - 1);
116
+ symbols.push({
117
+ name: `${currentContainer}::${name}`,
118
+ kind: "method",
119
+ signature: `${vis} function ${name}(${params})${retType}`,
120
+ visibility: vis,
121
+ line_start: lineNum,
122
+ line_end: endLine,
123
+ line_count: Math.max(1, endLine - lineNum + 1),
124
+ nesting_depth: nestingDepth,
125
+ });
126
+ continue;
127
+ }
128
+ }
129
+
130
+ // Standalone Global Function in PHP
131
+ const funcMatch = /^(?:final\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)(?:\s*:\s*([^;{]+))?/.exec(trimmed);
132
+ if (funcMatch && !currentContainer) {
133
+ const name = funcMatch[1];
134
+ const params = funcMatch[2].trim();
135
+ const retType = funcMatch[3] ? `: ${funcMatch[3].trim()}` : "";
136
+ const endLine = findBraceBlockEnd(lines, i);
137
+ const nestingDepth = calculateControlFlowNesting(lines, i, endLine - 1);
138
+ symbols.push({
139
+ name,
140
+ kind: "function",
141
+ signature: `function ${name}(${params})${retType}`,
142
+ visibility: "public",
143
+ line_start: lineNum,
144
+ line_end: endLine,
145
+ line_count: Math.max(1, endLine - lineNum + 1),
146
+ nesting_depth: nestingDepth,
147
+ });
148
+ }
149
+ }
150
+
151
+ return { symbols, dependencies };
152
+ }
153
+ }