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,238 +1,578 @@
1
- import { z } from "zod";
2
-
3
- function withAliases(raw: unknown, aliases: Record<string, string>): unknown {
4
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
5
- const obj = { ...(raw as Record<string, unknown>) };
6
- for (const [alias, target] of Object.entries(aliases)) {
7
- if (obj[alias] !== undefined && obj[target] === undefined) {
8
- obj[target] = obj[alias];
9
- }
10
- }
11
- return obj;
12
- }
13
-
14
- function normalizeBoundaryArgs(raw: unknown): unknown {
15
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
16
- const obj = { ...(raw as Record<string, unknown>) };
17
- const aliases: Record<string, string> = {
18
- filePath: "file_path",
19
- file: "file_path",
20
- path: "file_path",
21
- filePaths: "file_paths",
22
- files: "file_paths",
23
- paths: "file_paths",
24
- proposedImports: "proposed_imports",
25
- imports: "proposed_imports",
26
- featureKey: "feature_key",
27
- feature: "feature_key",
28
- };
29
- for (const [alias, target] of Object.entries(aliases)) {
30
- if (obj[alias] !== undefined && obj[target] === undefined) {
31
- obj[target] = obj[alias];
32
- }
33
- }
34
- if (Array.isArray(obj.file_path) && !obj.file_paths) {
35
- obj.file_paths = obj.file_path;
36
- delete obj.file_path;
37
- }
38
- if (typeof obj.file_paths === "string" && !obj.file_path) {
39
- obj.file_path = obj.file_paths;
40
- delete obj.file_paths;
41
- }
42
- return obj;
43
- }
44
-
45
- export const GetDomainCatalogSchema = z.preprocess(
46
- (args) =>
47
- withAliases(args, {
48
- domain_name: "domain",
49
- domainName: "domain",
50
- name: "domain",
51
- archetype_filter: "archetype",
52
- archetypeFilter: "archetype",
53
- type: "archetype",
54
- workspace: "workspace_path",
55
- workspacePath: "workspace_path",
56
- project_root: "workspace_path",
57
- target_path: "workspace_path",
58
- path: "workspace_path",
59
- }),
60
- z.object({
61
- domain: z.string().optional(),
62
- archetype: z.string().optional(),
63
- workspace_path: z.string().optional(),
64
- })
65
- );
66
- export type GetDomainCatalogArgs = z.infer<typeof GetDomainCatalogSchema>;
67
-
68
- export const GetFeatureContextSchema = z.preprocess(
69
- (args) =>
70
- withAliases(args, {
71
- feature_key: "feature",
72
- featureKey: "feature",
73
- name: "feature",
74
- workspace: "workspace_path",
75
- workspacePath: "workspace_path",
76
- }),
77
- z.object({
78
- feature: z.string().min(1, "feature is required"),
79
- workspace_path: z.string().optional(),
80
- })
81
- );
82
- export type GetFeatureContextArgs = z.infer<typeof GetFeatureContextSchema>;
83
-
84
- export const LocateSymbolSchema = z.preprocess(
85
- (args) =>
86
- withAliases(args, {
87
- symbol: "query",
88
- name: "query",
89
- symbol_name: "query",
90
- symbolName: "query",
91
- target: "query",
92
- domain_name: "domain",
93
- domainName: "domain",
94
- max: "limit",
95
- workspace: "workspace_path",
96
- workspacePath: "workspace_path",
97
- project_root: "workspace_path",
98
- target_path: "workspace_path",
99
- path: "workspace_path",
100
- }),
101
- z.object({
102
- query: z.string().min(1, "query is required"),
103
- domain: z.string().optional(),
104
- limit: z.number().int().positive().optional().default(10),
105
- workspace_path: z.string().optional(),
106
- })
107
- );
108
- export type LocateSymbolArgs = z.infer<typeof LocateSymbolSchema>;
109
-
110
- export const GetSymbolSchema = z.preprocess(
111
- (args) =>
112
- withAliases(args, {
113
- query: "symbol",
114
- name: "symbol",
115
- symbol_name: "symbol",
116
- symbolName: "symbol",
117
- target: "symbol",
118
- domain_name: "domain",
119
- domainName: "domain",
120
- includeDependencies: "include_dependencies",
121
- include_deps: "include_dependencies",
122
- includeDeps: "include_dependencies",
123
- dependencies: "include_dependencies",
124
- deps: "include_dependencies",
125
- }),
126
- z.object({
127
- symbol: z.string().min(1, "symbol is required"),
128
- domain: z.string().optional(),
129
- include_dependencies: z.boolean().optional().default(true),
130
- })
131
- );
132
- export type GetSymbolArgs = z.infer<typeof GetSymbolSchema>;
133
-
134
- export const GetSymbolImpactSchema = z.preprocess(
135
- (args) =>
136
- withAliases(args, {
137
- query: "symbol",
138
- name: "symbol",
139
- symbol_name: "symbol",
140
- symbolName: "symbol",
141
- file_path: "symbol",
142
- filePath: "symbol",
143
- file: "symbol",
144
- path: "symbol",
145
- target: "symbol",
146
- }),
147
- z.object({
148
- symbol: z.string().min(1, "symbol is required"),
149
- })
150
- );
151
- export type GetSymbolImpactArgs = z.infer<typeof GetSymbolImpactSchema>;
152
-
153
- export const TraceVerticalSliceSchema = z.preprocess(
154
- (args) =>
155
- withAliases(args, {
156
- route: "query",
157
- uri: "query",
158
- route_uri: "query",
159
- routeUri: "query",
160
- symbol: "query",
161
- name: "query",
162
- feature: "query",
163
- feature_key: "query",
164
- featureKey: "query",
165
- maxDepth: "max_depth",
166
- depth: "max_depth",
167
- }),
168
- z.object({
169
- query: z.string().min(1, "query is required"),
170
- max_depth: z.number().int().positive().optional().default(5),
171
- })
172
- );
173
- export type TraceVerticalSliceArgs = z.infer<typeof TraceVerticalSliceSchema>;
174
-
175
- export const CheckBoundarySchema = z.preprocess(
176
- normalizeBoundaryArgs,
177
- z.object({
178
- file_path: z.string().optional(),
179
- file_paths: z.array(z.string()).optional(),
180
- proposed_imports: z.array(z.string()).default([]),
181
- feature_key: z.string().optional(),
182
- })
183
- );
184
- export type CheckBoundaryArgs = z.infer<typeof CheckBoundarySchema>;
185
-
186
- export const GetSymbolHotspotsSchema = z.preprocess(
187
- (args) =>
188
- withAliases(args, {
189
- domain_name: "domain",
190
- domainName: "domain",
191
- max: "limit",
192
- }),
193
- z.object({
194
- domain: z.string().optional(),
195
- limit: z.number().int().positive().optional().default(10),
196
- })
197
- );
198
- export type GetSymbolHotspotsArgs = z.infer<typeof GetSymbolHotspotsSchema>;
199
-
200
- export const RegisterDomainSchema = z.preprocess(
201
- (args) =>
202
- withAliases(args, {
203
- domain: "name",
204
- domain_name: "name",
205
- domainName: "name",
206
- root_path: "root",
207
- rootPath: "root",
208
- path: "root",
209
- allowedDependencies: "allowed_dependencies",
210
- allowed: "allowed_dependencies",
211
- forbiddenDependencies: "forbidden_dependencies",
212
- forbidden: "forbidden_dependencies",
213
- archetype: "archetypes",
214
- ingestNow: "ingest_now",
215
- }),
216
- z.object({
217
- name: z.string().min(1, "Domain name is required"),
218
- root: z.string().min(1, "Domain root path is required"),
219
- description: z.string().optional(),
220
- allowed_dependencies: z.array(z.string()).optional(),
221
- forbidden_dependencies: z.array(z.string()).optional(),
222
- archetypes: z.record(z.string()).optional(),
223
- ingest_now: z.boolean().optional().default(false),
224
- })
225
- );
226
- export type RegisterDomainArgs = z.infer<typeof RegisterDomainSchema>;
227
-
228
- export const AutoDiscoverDomainsSchema = z.preprocess(
229
- (args) =>
230
- withAliases(args, {
231
- save: "persist",
232
- }),
233
- z.object({
234
- persist: z.boolean().optional().default(true),
235
- })
236
- );
237
- export type AutoDiscoverDomainsArgs = z.infer<typeof AutoDiscoverDomainsSchema>;
238
-
1
+ import { z } from "zod";
2
+
3
+ function withAliases(raw: unknown, aliases: Record<string, string>): unknown {
4
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
5
+ const obj = { ...(raw as Record<string, unknown>) };
6
+ for (const [alias, target] of Object.entries(aliases)) {
7
+ if (obj[alias] !== undefined && obj[target] === undefined) {
8
+ obj[target] = obj[alias];
9
+ }
10
+ }
11
+ return obj;
12
+ }
13
+
14
+ export function extractSingleStringFallback(
15
+ raw: unknown,
16
+ targetField: string,
17
+ ignoredKeys: string[] = ["workspace_path", "workspacePath", "workspace", "project_root", "projectRoot"]
18
+ ): unknown {
19
+ if (typeof raw === "string") {
20
+ return { [targetField]: raw };
21
+ }
22
+ if (Array.isArray(raw) && raw.length === 1 && typeof raw[0] === "string") {
23
+ return { [targetField]: raw[0] };
24
+ }
25
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
26
+ const obj = { ...(raw as Record<string, unknown>) };
27
+ if (obj[targetField] !== undefined && typeof obj[targetField] === "string" && (obj[targetField] as string).trim() !== "") {
28
+ return obj;
29
+ }
30
+ const nonIgnoredEntries = Object.entries(obj).filter(
31
+ ([key, val]) => !ignoredKeys.includes(key) && typeof val === "string" && (val as string).trim() !== ""
32
+ );
33
+ if (nonIgnoredEntries.length === 1) {
34
+ obj[targetField] = nonIgnoredEntries[0][1];
35
+ }
36
+ return obj;
37
+ }
38
+ return raw;
39
+ }
40
+
41
+ export function normalizeFeatureContextArgs(raw: unknown): unknown {
42
+ const coerced = extractSingleStringFallback(raw, "feature_key");
43
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
44
+ const obj = { ...(coerced as Record<string, unknown>) };
45
+
46
+ const aliases: Record<string, string> = {
47
+ feature: "feature_key",
48
+ featureKey: "feature_key",
49
+ feature_name: "feature_key",
50
+ featureName: "feature_key",
51
+ feat: "feature_key",
52
+ name: "feature_key",
53
+ key: "feature_key",
54
+ task: "feature_key",
55
+ task_name: "feature_key",
56
+ target: "feature_key",
57
+ context: "feature_key",
58
+ slug: "feature_key",
59
+ workspace: "workspace_path",
60
+ workspacePath: "workspace_path",
61
+ project_root: "workspace_path",
62
+ projectRoot: "workspace_path",
63
+ };
64
+
65
+ for (const [alias, target] of Object.entries(aliases)) {
66
+ if (obj[alias] !== undefined && obj[target] === undefined) {
67
+ obj[target] = obj[alias];
68
+ }
69
+ }
70
+
71
+ // Populate feature as backward-compatible alias
72
+ if (obj.feature_key !== undefined && obj.feature === undefined) {
73
+ obj.feature = obj.feature_key;
74
+ } else if (obj.feature !== undefined && obj.feature_key === undefined) {
75
+ obj.feature_key = obj.feature;
76
+ }
77
+
78
+ return obj;
79
+ }
80
+
81
+ function normalizeBoundaryArgs(raw: unknown): unknown {
82
+ if (Array.isArray(raw) && raw.length > 1 && raw.every((item) => typeof item === "string")) {
83
+ raw = { file_paths: raw };
84
+ }
85
+ const coerced = extractSingleStringFallback(raw, "file_path");
86
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
87
+ const obj = { ...(coerced as Record<string, unknown>) };
88
+ const aliases: Record<string, string> = {
89
+ filePath: "file_path",
90
+ file: "file_path",
91
+ path: "file_path",
92
+ from_file: "file_path",
93
+ fromFile: "file_path",
94
+ source_file: "file_path",
95
+ sourceFile: "file_path",
96
+ filePaths: "file_paths",
97
+ files: "file_paths",
98
+ paths: "file_paths",
99
+ proposedImports: "proposed_imports",
100
+ imports: "proposed_imports",
101
+ featureKey: "feature_key",
102
+ feature: "feature_key",
103
+ feature_name: "feature_key",
104
+ featureName: "feature_key",
105
+ name: "feature_key",
106
+ key: "feature_key",
107
+ };
108
+ for (const [alias, target] of Object.entries(aliases)) {
109
+ if (obj[alias] !== undefined && obj[target] === undefined) {
110
+ obj[target] = obj[alias];
111
+ }
112
+ }
113
+
114
+ if (obj.feature_key !== undefined && obj.feature === undefined) {
115
+ obj.feature = obj.feature_key;
116
+ } else if (obj.feature !== undefined && obj.feature_key === undefined) {
117
+ obj.feature_key = obj.feature;
118
+ }
119
+
120
+ if (obj.to_file || obj.toFile) {
121
+ const rawTo = obj.to_file ?? obj.toFile;
122
+ const toFiles = Array.isArray(rawTo) ? rawTo.map(String) : [String(rawTo)];
123
+ if (!obj.proposed_imports) {
124
+ obj.proposed_imports = toFiles;
125
+ } else if (Array.isArray(obj.proposed_imports)) {
126
+ obj.proposed_imports = [...obj.proposed_imports, ...toFiles];
127
+ }
128
+ }
129
+ if (Array.isArray(obj.file_path) && !obj.file_paths) {
130
+ obj.file_paths = obj.file_path;
131
+ delete obj.file_path;
132
+ }
133
+ if (typeof obj.file_paths === "string" && !obj.file_path) {
134
+ obj.file_path = obj.file_paths;
135
+ delete obj.file_paths;
136
+ }
137
+ return obj;
138
+ }
139
+
140
+ export function normalizeDomainCatalogArgs(raw: unknown): unknown {
141
+ const coerced = extractSingleStringFallback(raw, "domain");
142
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
143
+ const obj = withAliases(coerced, {
144
+ domain_name: "domain",
145
+ domainName: "domain",
146
+ name: "domain",
147
+ archetype_filter: "archetype",
148
+ archetypeFilter: "archetype",
149
+ type: "archetype",
150
+ workspace: "workspace_path",
151
+ workspacePath: "workspace_path",
152
+ project_root: "workspace_path",
153
+ target_path: "workspace_path",
154
+ path: "workspace_path",
155
+ }) as Record<string, unknown>;
156
+ if (obj.domain !== undefined && obj.name === undefined) {
157
+ obj.name = obj.domain;
158
+ } else if (obj.name !== undefined && obj.domain === undefined) {
159
+ obj.domain = obj.name;
160
+ }
161
+ return obj;
162
+ }
163
+
164
+ export const GetDomainCatalogSchema = z.preprocess(
165
+ normalizeDomainCatalogArgs,
166
+ z.object({
167
+ domain: z.string().optional(),
168
+ name: z.string().optional(),
169
+ archetype: z.string().optional(),
170
+ workspace_path: z.string().optional(),
171
+ })
172
+ );
173
+ export type GetDomainCatalogArgs = z.infer<typeof GetDomainCatalogSchema>;
174
+
175
+ export const GetFeatureContextSchema = z.preprocess(
176
+ normalizeFeatureContextArgs,
177
+ z.object({
178
+ feature_key: z.string().optional(),
179
+ feature: z.string().optional(),
180
+ workspace_path: z.string().optional(),
181
+ })
182
+ );
183
+ export type GetFeatureContextArgs = z.infer<typeof GetFeatureContextSchema>;
184
+
185
+ export function normalizeLocateSymbolArgs(raw: unknown): unknown {
186
+ const coerced = extractSingleStringFallback(raw, "query");
187
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
188
+ const obj = withAliases(coerced, {
189
+ symbol: "query",
190
+ name: "query",
191
+ symbol_name: "query",
192
+ symbolName: "query",
193
+ target: "query",
194
+ domain_name: "domain",
195
+ domainName: "domain",
196
+ max: "limit",
197
+ workspace: "workspace_path",
198
+ workspacePath: "workspace_path",
199
+ project_root: "workspace_path",
200
+ target_path: "workspace_path",
201
+ path: "workspace_path",
202
+ }) as Record<string, unknown>;
203
+ if (obj.query !== undefined && obj.symbol === undefined) {
204
+ obj.symbol = obj.query;
205
+ } else if (obj.symbol !== undefined && obj.query === undefined) {
206
+ obj.query = obj.symbol;
207
+ }
208
+ return obj;
209
+ }
210
+
211
+ export const LocateSymbolSchema = z.preprocess(
212
+ normalizeLocateSymbolArgs,
213
+ z.object({
214
+ query: z.string().min(1, "query is required"),
215
+ symbol: z.string().optional(),
216
+ domain: z.string().optional(),
217
+ limit: z.number().int().positive().optional().default(10),
218
+ workspace_path: z.string().optional(),
219
+ })
220
+ );
221
+ export type LocateSymbolArgs = z.infer<typeof LocateSymbolSchema>;
222
+
223
+ export function normalizeGetSymbolArgs(raw: unknown): unknown {
224
+ const coerced = extractSingleStringFallback(raw, "symbol");
225
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
226
+ const obj = withAliases(coerced, {
227
+ query: "symbol",
228
+ name: "symbol",
229
+ symbol_name: "symbol",
230
+ symbolName: "symbol",
231
+ target: "symbol",
232
+ domain_name: "domain",
233
+ domainName: "domain",
234
+ includeDependencies: "include_dependencies",
235
+ include_deps: "include_dependencies",
236
+ includeDeps: "include_dependencies",
237
+ dependencies: "include_dependencies",
238
+ deps: "include_dependencies",
239
+ }) as Record<string, unknown>;
240
+ if (obj.symbol !== undefined && obj.query === undefined) {
241
+ obj.query = obj.symbol;
242
+ } else if (obj.query !== undefined && obj.symbol === undefined) {
243
+ obj.symbol = obj.query;
244
+ }
245
+ return obj;
246
+ }
247
+
248
+ export const GetSymbolSchema = z.preprocess(
249
+ normalizeGetSymbolArgs,
250
+ z.object({
251
+ symbol: z.string().min(1, "symbol is required"),
252
+ query: z.string().optional(),
253
+ domain: z.string().optional(),
254
+ include_dependencies: z.boolean().optional().default(true),
255
+ })
256
+ );
257
+ export type GetSymbolArgs = z.infer<typeof GetSymbolSchema>;
258
+
259
+ export function normalizeGetSymbolImpactArgs(raw: unknown): unknown {
260
+ const coerced = extractSingleStringFallback(raw, "symbol");
261
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
262
+ const obj = withAliases(coerced, {
263
+ query: "symbol",
264
+ name: "symbol",
265
+ symbol_name: "symbol",
266
+ symbolName: "symbol",
267
+ file_path: "symbol",
268
+ filePath: "symbol",
269
+ file: "symbol",
270
+ path: "symbol",
271
+ target: "symbol",
272
+ }) as Record<string, unknown>;
273
+ if (obj.symbol !== undefined && obj.query === undefined) {
274
+ obj.query = obj.symbol;
275
+ } else if (obj.query !== undefined && obj.symbol === undefined) {
276
+ obj.symbol = obj.query;
277
+ }
278
+ return obj;
279
+ }
280
+
281
+ export const GetSymbolImpactSchema = z.preprocess(
282
+ normalizeGetSymbolImpactArgs,
283
+ z.object({
284
+ symbol: z.string().min(1, "symbol is required"),
285
+ query: z.string().optional(),
286
+ })
287
+ );
288
+ export type GetSymbolImpactArgs = z.infer<typeof GetSymbolImpactSchema>;
289
+
290
+ export function normalizeTraceVerticalSliceArgs(raw: unknown): unknown {
291
+ const coerced = extractSingleStringFallback(raw, "query");
292
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
293
+ const obj = withAliases(coerced, {
294
+ route: "query",
295
+ uri: "query",
296
+ route_uri: "query",
297
+ routeUri: "query",
298
+ entry_file: "query",
299
+ entryFile: "query",
300
+ file_path: "query",
301
+ filePath: "query",
302
+ file: "query",
303
+ path: "query",
304
+ symbol: "query",
305
+ name: "query",
306
+ feature: "query",
307
+ feature_key: "query",
308
+ featureKey: "query",
309
+ maxDepth: "max_depth",
310
+ depth: "max_depth",
311
+ }) as Record<string, unknown>;
312
+ if (obj.query !== undefined && obj.symbol === undefined) {
313
+ obj.symbol = obj.query;
314
+ } else if (obj.symbol !== undefined && obj.query === undefined) {
315
+ obj.query = obj.symbol;
316
+ }
317
+ return obj;
318
+ }
319
+
320
+ export const TraceVerticalSliceSchema = z.preprocess(
321
+ normalizeTraceVerticalSliceArgs,
322
+ z.object({
323
+ query: z.string().min(1, "query is required"),
324
+ symbol: z.string().optional(),
325
+ max_depth: z.number().int().positive().optional().default(5),
326
+ })
327
+ );
328
+ export type TraceVerticalSliceArgs = z.infer<typeof TraceVerticalSliceSchema>;
329
+
330
+ export const CheckBoundarySchema = z.preprocess(
331
+ normalizeBoundaryArgs,
332
+ z.object({
333
+ file_path: z.string().optional(),
334
+ file_paths: z.array(z.string()).optional(),
335
+ proposed_imports: z.array(z.string()).default([]),
336
+ feature_key: z.string().optional(),
337
+ })
338
+ );
339
+ export type CheckBoundaryArgs = z.infer<typeof CheckBoundarySchema>;
340
+
341
+ export const GetSymbolHotspotsSchema = z.preprocess(
342
+ (args) =>
343
+ withAliases(args, {
344
+ domain_name: "domain",
345
+ domainName: "domain",
346
+ max: "limit",
347
+ nesting: "min_nesting",
348
+ minNesting: "min_nesting",
349
+ lines: "min_lines",
350
+ minLines: "min_lines",
351
+ sortBy: "sort_by",
352
+ }),
353
+ z.object({
354
+ min_lines: z.number().int().nonnegative().optional(),
355
+ min_nesting: z.number().int().positive().optional(),
356
+ kind: z.string().optional(),
357
+ domain: z.string().optional(),
358
+ limit: z.number().int().positive().optional().default(10),
359
+ sort_by: z.enum(["lines", "nesting", "risk_score"]).optional(),
360
+ })
361
+ );
362
+ export type GetSymbolHotspotsArgs = z.infer<typeof GetSymbolHotspotsSchema>;
363
+
364
+ export function normalizeRegisterDomainArgs(raw: unknown): unknown {
365
+ const coerced = extractSingleStringFallback(raw, "domain", [
366
+ "root",
367
+ "root_path",
368
+ "rootPath",
369
+ "path",
370
+ "root_dir",
371
+ "workspace_path",
372
+ "workspacePath",
373
+ "workspace",
374
+ "project_root",
375
+ "projectRoot",
376
+ ]);
377
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
378
+ const obj = withAliases(coerced, {
379
+ name: "domain",
380
+ domain_name: "domain",
381
+ domainName: "domain",
382
+ root_path: "root",
383
+ rootPath: "root",
384
+ path: "root",
385
+ allowedDependencies: "allowed_dependencies",
386
+ allowed: "allowed_dependencies",
387
+ forbiddenDependencies: "forbidden_dependencies",
388
+ forbidden: "forbidden_dependencies",
389
+ archetype: "archetypes",
390
+ ingestNow: "ingest_now",
391
+ }) as Record<string, unknown>;
392
+ if (obj.domain !== undefined && obj.name === undefined) {
393
+ obj.name = obj.domain;
394
+ } else if (obj.name !== undefined && obj.domain === undefined) {
395
+ obj.domain = obj.name;
396
+ }
397
+ return obj;
398
+ }
399
+
400
+ export const RegisterDomainSchema = z.preprocess(
401
+ normalizeRegisterDomainArgs,
402
+ z.object({
403
+ domain: z.string().min(1, "Domain name/identifier is required"),
404
+ name: z.string().optional(),
405
+ root: z.string().min(1, "Domain root path is required"),
406
+ description: z.string().optional(),
407
+ allowed_dependencies: z.array(z.string()).optional(),
408
+ forbidden_dependencies: z.array(z.string()).optional(),
409
+ archetypes: z.record(z.string()).optional(),
410
+ ingest_now: z.boolean().optional().default(false),
411
+ })
412
+ );
413
+ export type RegisterDomainArgs = z.infer<typeof RegisterDomainSchema>;
414
+
415
+ export function normalizeRegisterFeatureArgs(raw: unknown): unknown {
416
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
417
+ const obj = { ...(raw as Record<string, unknown>) };
418
+
419
+ const aliases: Record<string, string> = {
420
+ feature: "feature_key",
421
+ featureKey: "feature_key",
422
+ feature_name: "feature_key",
423
+ featureName: "feature_key",
424
+ feat: "feature_key",
425
+ name: "feature_key",
426
+ key: "feature_key",
427
+ domain_name: "domain",
428
+ domainName: "domain",
429
+ touchpoints: "allowed_touchpoints",
430
+ allowedTouchpoints: "allowed_touchpoints",
431
+ touchpoint: "allowed_touchpoints",
432
+ files: "allowed_touchpoints",
433
+ allowed_files: "allowed_touchpoints",
434
+ reuseSymbols: "reuse_symbols",
435
+ reuse: "reuse_symbols",
436
+ symbols: "reuse_symbols",
437
+ inputContract: "input_contract",
438
+ outputContract: "output_contract",
439
+ persistToConfig: "persist_to_config",
440
+ persist: "persist_to_config",
441
+ workspace: "workspace_path",
442
+ workspacePath: "workspace_path",
443
+ project_root: "workspace_path",
444
+ projectRoot: "workspace_path",
445
+ };
446
+
447
+ for (const [alias, target] of Object.entries(aliases)) {
448
+ if (obj[alias] !== undefined && obj[target] === undefined) {
449
+ obj[target] = obj[alias];
450
+ }
451
+ }
452
+
453
+ // Symmetrical contract: feature_key <-> feature
454
+ if (obj.feature_key !== undefined && obj.feature === undefined) {
455
+ obj.feature = obj.feature_key;
456
+ } else if (obj.feature !== undefined && obj.feature_key === undefined) {
457
+ obj.feature_key = obj.feature;
458
+ }
459
+
460
+ // Symmetrical contract: domain <-> domain_name
461
+ if (obj.domain !== undefined && obj.domain_name === undefined) {
462
+ obj.domain_name = obj.domain;
463
+ } else if (obj.domain_name !== undefined && obj.domain === undefined) {
464
+ obj.domain = obj.domain_name;
465
+ }
466
+
467
+ // Array coercion for allowed_touchpoints
468
+ if (typeof obj.allowed_touchpoints === "string") {
469
+ obj.allowed_touchpoints = [obj.allowed_touchpoints];
470
+ } else if (!Array.isArray(obj.allowed_touchpoints) && obj.allowed_touchpoints !== undefined) {
471
+ obj.allowed_touchpoints = [];
472
+ }
473
+
474
+ // Array coercion for reuse_symbols
475
+ if (typeof obj.reuse_symbols === "string") {
476
+ obj.reuse_symbols = [obj.reuse_symbols];
477
+ }
478
+
479
+ return obj;
480
+ }
481
+
482
+ export const RegisterFeatureSchema = z.preprocess(
483
+ normalizeRegisterFeatureArgs,
484
+ z.object({
485
+ feature_key: z.string().min(1, "Feature key is required"),
486
+ feature: z.string().optional(),
487
+ domain: z.string().min(1, "Domain name is required"),
488
+ domain_name: z.string().optional(),
489
+ description: z.string().optional(),
490
+ allowed_touchpoints: z.array(z.string()).default([]),
491
+ reuse_symbols: z.array(z.string()).optional(),
492
+ input_contract: z.record(z.unknown()).optional(),
493
+ output_contract: z.record(z.unknown()).optional(),
494
+ persist_to_config: z.boolean().optional().default(false),
495
+ workspace_path: z.string().optional(),
496
+ })
497
+ );
498
+ export type RegisterFeatureArgs = z.infer<typeof RegisterFeatureSchema>;
499
+
500
+ export const AutoDiscoverDomainsSchema = z.preprocess(
501
+ (args) =>
502
+ withAliases(args, {
503
+ save: "persist",
504
+ }),
505
+ z.object({
506
+ persist: z.boolean().optional().default(true),
507
+ })
508
+ );
509
+ export type AutoDiscoverDomainsArgs = z.infer<typeof AutoDiscoverDomainsSchema>;
510
+
511
+ function normalizeTopologyArgs(raw: unknown): unknown {
512
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
513
+ const obj = { ...(raw as Record<string, unknown>) };
514
+ if (obj.resolve_node !== undefined && obj.resolveNode === undefined) {
515
+ obj.resolveNode = obj.resolve_node;
516
+ }
517
+ if (obj.resolve_layer !== undefined && obj.resolve === undefined) {
518
+ obj.resolve = obj.resolve_layer;
519
+ }
520
+ if (obj.resolveLayer !== undefined && obj.resolve === undefined) {
521
+ obj.resolve = obj.resolveLayer;
522
+ }
523
+ if (typeof obj.resolveNode === "object" && obj.resolveNode !== null) {
524
+ obj.resolveNode = withAliases(obj.resolveNode, {
525
+ canonicalTag: "canonical_tag",
526
+ domainOrIp: "domain_or_ip",
527
+ connectedTo: "connected_to",
528
+ nodeId: "id",
529
+ node_id: "id",
530
+ nodeName: "name",
531
+ node_name: "name",
532
+ });
533
+ }
534
+ return obj;
535
+ }
536
+
537
+ export const GetEnvironmentTopologySchema = z.preprocess(
538
+ (args) => normalizeTopologyArgs(args),
539
+ z.object({
540
+ resolve: z
541
+ .object({
542
+ layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
543
+ platform: z.string().min(1, "platform name cannot be empty"),
544
+ constraints: z.array(z.string()).optional(),
545
+ })
546
+ .optional(),
547
+ resolveNode: z
548
+ .object({
549
+ id: z.string().min(1, "Node id cannot be empty"),
550
+ layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
551
+ name: z.string().min(1, "Node name cannot be empty"),
552
+ platform: z.string().min(1, "Platform cannot be empty"),
553
+ canonical_tag: z
554
+ .enum([
555
+ "shared_hosting",
556
+ "vps",
557
+ "docker",
558
+ "serverless",
559
+ "kubernetes",
560
+ "managed_db",
561
+ "cdn_edge",
562
+ "paas",
563
+ "static_cdn",
564
+ "unspecified",
565
+ ])
566
+ .optional(),
567
+ domain_or_ip: z.string().optional(),
568
+ role: z.string().optional(),
569
+ constraints: z.array(z.string()).optional(),
570
+ connected_to: z.array(z.string()).optional(),
571
+ })
572
+ .optional(),
573
+ refresh: z.boolean().optional().default(false),
574
+ })
575
+ );
576
+ export type GetEnvironmentTopologyArgs = z.infer<typeof GetEnvironmentTopologySchema>;
577
+
578
+