septum 0.1.4 → 0.1.6

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 (70) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -105
  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 +216 -188
  7. package/src/cli/commands/ingest.ts +29 -29
  8. package/src/cli/commands/init.ts +213 -211
  9. package/src/cli/commands/locate.ts +61 -61
  10. package/src/cli/commands/query.ts +104 -64
  11. package/src/cli/commands/serve.ts +5 -5
  12. package/src/cli/commands/slice.ts +50 -48
  13. package/src/cli/commands/sync.ts +31 -31
  14. package/src/cli/index.ts +220 -210
  15. package/src/cli/templates/hooks.ts +75 -75
  16. package/src/cli/templates/rules.ts +50 -38
  17. package/src/cli/templates/skill.ts +65 -69
  18. package/src/core/boundary/evaluator.ts +429 -210
  19. package/src/core/config/loader.ts +117 -97
  20. package/src/core/config/schema.ts +39 -39
  21. package/src/core/database/client.ts +44 -44
  22. package/src/core/database/repositories/dependency.repository.ts +552 -349
  23. package/src/core/database/repositories/domain.repository.ts +275 -198
  24. package/src/core/database/repositories/file.repository.ts +117 -106
  25. package/src/core/database/repositories/meta.repository.ts +33 -33
  26. package/src/core/database/repositories/symbol.repository.ts +482 -443
  27. package/src/core/database/repository.ts +281 -260
  28. package/src/core/database/schema.ts +154 -153
  29. package/src/core/discovery/topology-detector.ts +486 -486
  30. package/src/core/ingestion/hasher.ts +5 -5
  31. package/src/core/ingestion/pipeline.ts +606 -535
  32. package/src/core/parser/boundary-tracker.ts +433 -288
  33. package/src/core/parser/extractors/base.ts +62 -62
  34. package/src/core/parser/extractors/blade.ts +158 -158
  35. package/src/core/parser/extractors/frontend.ts +147 -194
  36. package/src/core/parser/extractors/go.ts +408 -180
  37. package/src/core/parser/extractors/laravel-semantic.ts +1045 -901
  38. package/src/core/parser/extractors/mcp-cli-semantic.ts +328 -199
  39. package/src/core/parser/extractors/nestjs-semantic.ts +134 -119
  40. package/src/core/parser/extractors/php.ts +415 -153
  41. package/src/core/parser/extractors/python.ts +460 -228
  42. package/src/core/parser/extractors/registry.ts +70 -70
  43. package/src/core/parser/extractors/semantic-extractor.interface.ts +21 -21
  44. package/src/core/parser/extractors/typescript.ts +668 -181
  45. package/src/core/parser/tree-sitter.ts +41 -41
  46. package/src/core/parser/wasm-loader.ts +271 -0
  47. package/src/core/resolver/call-graph-tracer.ts +698 -668
  48. package/src/core/resolver/module-resolver.ts +479 -342
  49. package/src/core/resolver/path-resolver.ts +147 -147
  50. package/src/core/resolver/symbol-locator.ts +543 -543
  51. package/src/core/resolver/vertical-slice-tracer.ts +323 -285
  52. package/src/core/session/session-manager.ts +56 -56
  53. package/src/core/telemetry/telemetry.ts +86 -86
  54. package/src/core/topology/environment-detector.ts +109 -6
  55. package/src/index.ts +21 -21
  56. package/src/mcp/schemas.ts +695 -286
  57. package/src/mcp/server.ts +927 -716
  58. package/src/mcp/tools/check-boundary.ts +307 -152
  59. package/src/mcp/tools/get-domain-catalog.ts +155 -121
  60. package/src/mcp/tools/get-environment-topology.ts +3 -6
  61. package/src/mcp/tools/get-feature-context.ts +227 -116
  62. package/src/mcp/tools/get-symbol-hotspots.ts +54 -59
  63. package/src/mcp/tools/get-symbol-impact.ts +28 -26
  64. package/src/mcp/tools/get-symbol.ts +65 -63
  65. package/src/mcp/tools/locate-symbol.ts +169 -190
  66. package/src/mcp/tools/register-domain.ts +59 -64
  67. package/src/mcp/tools/register-feature.ts +91 -0
  68. package/src/mcp/tools/trace-vertical-slice.ts +74 -48
  69. package/src/types/index.ts +366 -315
  70. package/src/version.ts +3 -3
@@ -1,286 +1,695 @@
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
- nesting: "min_nesting",
193
- minNesting: "min_nesting",
194
- lines: "min_lines",
195
- minLines: "min_lines",
196
- sortBy: "sort_by",
197
- }),
198
- z.object({
199
- min_lines: z.number().int().nonnegative().optional(),
200
- min_nesting: z.number().int().positive().optional(),
201
- kind: z.string().optional(),
202
- domain: z.string().optional(),
203
- limit: z.number().int().positive().optional().default(10),
204
- sort_by: z.enum(["lines", "nesting", "risk_score"]).optional(),
205
- })
206
- );
207
- export type GetSymbolHotspotsArgs = z.infer<typeof GetSymbolHotspotsSchema>;
208
-
209
- export const RegisterDomainSchema = z.preprocess(
210
- (args) =>
211
- withAliases(args, {
212
- domain: "name",
213
- domain_name: "name",
214
- domainName: "name",
215
- root_path: "root",
216
- rootPath: "root",
217
- path: "root",
218
- allowedDependencies: "allowed_dependencies",
219
- allowed: "allowed_dependencies",
220
- forbiddenDependencies: "forbidden_dependencies",
221
- forbidden: "forbidden_dependencies",
222
- archetype: "archetypes",
223
- ingestNow: "ingest_now",
224
- }),
225
- z.object({
226
- name: z.string().min(1, "Domain name is required"),
227
- root: z.string().min(1, "Domain root path is required"),
228
- description: z.string().optional(),
229
- allowed_dependencies: z.array(z.string()).optional(),
230
- forbidden_dependencies: z.array(z.string()).optional(),
231
- archetypes: z.record(z.string()).optional(),
232
- ingest_now: z.boolean().optional().default(false),
233
- })
234
- );
235
- export type RegisterDomainArgs = z.infer<typeof RegisterDomainSchema>;
236
-
237
- export const AutoDiscoverDomainsSchema = z.preprocess(
238
- (args) =>
239
- withAliases(args, {
240
- save: "persist",
241
- }),
242
- z.object({
243
- persist: z.boolean().optional().default(true),
244
- })
245
- );
246
- export type AutoDiscoverDomainsArgs = z.infer<typeof AutoDiscoverDomainsSchema>;
247
-
248
- export const GetEnvironmentTopologySchema = z.object({
249
- resolve: z
250
- .object({
251
- layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
252
- platform: z.string().min(1, "platform name cannot be empty"),
253
- constraints: z.array(z.string()).optional(),
254
- })
255
- .optional(),
256
- resolveNode: z
257
- .object({
258
- id: z.string().min(1, "Node id cannot be empty"),
259
- layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
260
- name: z.string().min(1, "Node name cannot be empty"),
261
- platform: z.string().min(1, "Platform cannot be empty"),
262
- canonical_tag: z
263
- .enum([
264
- "shared_hosting",
265
- "vps",
266
- "docker",
267
- "serverless",
268
- "kubernetes",
269
- "managed_db",
270
- "cdn_edge",
271
- "paas",
272
- "static_cdn",
273
- "unspecified",
274
- ])
275
- .optional(),
276
- domain_or_ip: z.string().optional(),
277
- role: z.string().optional(),
278
- constraints: z.array(z.string()).optional(),
279
- connected_to: z.array(z.string()).optional(),
280
- })
281
- .optional(),
282
- refresh: z.boolean().optional().default(false),
283
- });
284
- export type GetEnvironmentTopologyArgs = z.infer<typeof GetEnvironmentTopologySchema>;
285
-
286
-
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
+ export 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
+ "workspace_path",
87
+ "workspacePath",
88
+ "workspace",
89
+ "project_root",
90
+ "projectRoot",
91
+ "scope",
92
+ "domain",
93
+ "domain_name",
94
+ "domainName",
95
+ "feature_key",
96
+ "featureKey",
97
+ "feature",
98
+ "feature_name",
99
+ "featureName",
100
+ "name",
101
+ "key",
102
+ ]);
103
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
104
+ const obj = { ...(coerced as Record<string, unknown>) };
105
+ const aliases: Record<string, string> = {
106
+ filePath: "file_path",
107
+ file: "file_path",
108
+ path: "file_path",
109
+ from_file: "file_path",
110
+ fromFile: "file_path",
111
+ source_file: "file_path",
112
+ sourceFile: "file_path",
113
+ filePaths: "file_paths",
114
+ files: "file_paths",
115
+ paths: "file_paths",
116
+ proposedImports: "proposed_imports",
117
+ imports: "proposed_imports",
118
+ featureKey: "feature_key",
119
+ feature: "feature_key",
120
+ feature_name: "feature_key",
121
+ featureName: "feature_key",
122
+ name: "feature_key",
123
+ key: "feature_key",
124
+ domain_name: "domain",
125
+ domainName: "domain",
126
+ workspace: "workspace_path",
127
+ workspacePath: "workspace_path",
128
+ project_root: "workspace_path",
129
+ projectRoot: "workspace_path",
130
+ };
131
+ for (const [alias, target] of Object.entries(aliases)) {
132
+ if (obj[alias] !== undefined && obj[target] === undefined) {
133
+ obj[target] = obj[alias];
134
+ }
135
+ }
136
+
137
+ if (obj.feature_key !== undefined && obj.feature === undefined) {
138
+ obj.feature = obj.feature_key;
139
+ } else if (obj.feature !== undefined && obj.feature_key === undefined) {
140
+ obj.feature_key = obj.feature;
141
+ }
142
+
143
+ if (obj.to_file || obj.toFile) {
144
+ const rawTo = obj.to_file ?? obj.toFile;
145
+ const toFiles = Array.isArray(rawTo) ? rawTo.map(String) : [String(rawTo)];
146
+ if (!obj.proposed_imports) {
147
+ obj.proposed_imports = toFiles;
148
+ } else if (Array.isArray(obj.proposed_imports)) {
149
+ obj.proposed_imports = [...obj.proposed_imports, ...toFiles];
150
+ }
151
+ }
152
+ if (Array.isArray(obj.file_path) && !obj.file_paths) {
153
+ obj.file_paths = obj.file_path;
154
+ delete obj.file_path;
155
+ }
156
+ if (typeof obj.file_paths === "string" && !obj.file_path) {
157
+ obj.file_path = obj.file_paths;
158
+ delete obj.file_paths;
159
+ }
160
+ if (obj.staged === true && obj.scope === undefined) {
161
+ obj.scope = "staged";
162
+ }
163
+ if (obj.domain !== undefined && obj.file_path === undefined && obj.file_paths === undefined) {
164
+ obj.scope = "domain";
165
+ }
166
+ return obj;
167
+ }
168
+
169
+ export function normalizeDomainCatalogArgs(raw: unknown): unknown {
170
+ const coerced = extractSingleStringFallback(raw, "domain", [
171
+ "workspace_path",
172
+ "workspacePath",
173
+ "workspace",
174
+ "project_root",
175
+ "projectRoot",
176
+ "target_path",
177
+ "path",
178
+ "archetype_filter",
179
+ "archetype",
180
+ "archetypeFilter",
181
+ "type",
182
+ "detail_level",
183
+ "detailLevel",
184
+ "mode",
185
+ "format",
186
+ "file_filter",
187
+ "fileFilter",
188
+ "file",
189
+ "filter",
190
+ ]);
191
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
192
+ const obj = withAliases(coerced, {
193
+ domain_name: "domain",
194
+ domainName: "domain",
195
+ name: "domain",
196
+ archetype: "archetype_filter",
197
+ archetypeFilter: "archetype_filter",
198
+ type: "archetype_filter",
199
+ detailLevel: "detail_level",
200
+ mode: "detail_level",
201
+ format: "detail_level",
202
+ fileFilter: "file_filter",
203
+ file: "file_filter",
204
+ filter: "file_filter",
205
+ workspace: "workspace_path",
206
+ workspacePath: "workspace_path",
207
+ project_root: "workspace_path",
208
+ projectRoot: "workspace_path",
209
+ target_path: "workspace_path",
210
+ path: "workspace_path",
211
+ }) as Record<string, unknown>;
212
+ if (obj.domain !== undefined && obj.name === undefined) {
213
+ obj.name = obj.domain;
214
+ } else if (obj.name !== undefined && obj.domain === undefined) {
215
+ obj.domain = obj.name;
216
+ }
217
+ if (obj.archetype_filter !== undefined && obj.archetype === undefined) {
218
+ obj.archetype = obj.archetype_filter;
219
+ } else if (obj.archetype !== undefined && obj.archetype_filter === undefined) {
220
+ obj.archetype_filter = obj.archetype;
221
+ }
222
+ if (obj.full === true && obj.detail_level === undefined) {
223
+ obj.detail_level = "full";
224
+ }
225
+ return obj;
226
+ }
227
+
228
+ export const GetDomainCatalogSchema = z.preprocess(
229
+ normalizeDomainCatalogArgs,
230
+ z.object({
231
+ domain: z.string().optional(),
232
+ name: z.string().optional(),
233
+ archetype_filter: z.string().optional(),
234
+ archetype: z.string().optional(),
235
+ detail_level: z.enum(["compact", "full"]).optional().default("compact"),
236
+ file_filter: z.string().optional(),
237
+ workspace_path: z.string().optional(),
238
+ })
239
+ );
240
+ export type GetDomainCatalogArgs = z.infer<typeof GetDomainCatalogSchema>;
241
+
242
+ export const GetFeatureContextSchema = z.preprocess(
243
+ normalizeFeatureContextArgs,
244
+ z.object({
245
+ feature_key: z.string().optional(),
246
+ feature: z.string().optional(),
247
+ workspace_path: z.string().optional(),
248
+ })
249
+ );
250
+ export type GetFeatureContextArgs = z.infer<typeof GetFeatureContextSchema>;
251
+
252
+ export const ClearFeatureContextSchema = z
253
+ .object({
254
+ workspace_path: z.string().optional(),
255
+ })
256
+ .passthrough();
257
+ export type ClearFeatureContextArgs = z.infer<typeof ClearFeatureContextSchema>;
258
+
259
+ export function normalizeLocateSymbolArgs(raw: unknown): unknown {
260
+ const coerced = extractSingleStringFallback(raw, "query", [
261
+ "workspace_path",
262
+ "workspacePath",
263
+ "workspace",
264
+ "project_root",
265
+ "projectRoot",
266
+ "target_path",
267
+ "path",
268
+ "domain",
269
+ "domain_name",
270
+ "domainName",
271
+ ]);
272
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
273
+ const obj = withAliases(coerced, {
274
+ symbol: "query",
275
+ name: "query",
276
+ symbol_name: "query",
277
+ symbolName: "query",
278
+ target: "query",
279
+ domain_name: "domain",
280
+ domainName: "domain",
281
+ max: "limit",
282
+ workspace: "workspace_path",
283
+ workspacePath: "workspace_path",
284
+ project_root: "workspace_path",
285
+ projectRoot: "workspace_path",
286
+ target_path: "workspace_path",
287
+ path: "workspace_path",
288
+ }) as Record<string, unknown>;
289
+ if (obj.query !== undefined && obj.symbol === undefined) {
290
+ obj.symbol = obj.query;
291
+ } else if (obj.symbol !== undefined && obj.query === undefined) {
292
+ obj.query = obj.symbol;
293
+ }
294
+ return obj;
295
+ }
296
+
297
+ export const LocateSymbolSchema = z.preprocess(
298
+ normalizeLocateSymbolArgs,
299
+ z.object({
300
+ query: z.string().min(1, "query is required"),
301
+ symbol: z.string().optional(),
302
+ domain: z.string().optional(),
303
+ limit: z.number().int().min(1).max(50).optional().default(10),
304
+ workspace_path: z.string().optional(),
305
+ })
306
+ );
307
+ export type LocateSymbolArgs = z.infer<typeof LocateSymbolSchema>;
308
+
309
+ export function normalizeGetSymbolArgs(raw: unknown): unknown {
310
+ const coerced = extractSingleStringFallback(raw, "symbol", [
311
+ "workspace_path",
312
+ "workspacePath",
313
+ "workspace",
314
+ "project_root",
315
+ "projectRoot",
316
+ "domain",
317
+ "domain_name",
318
+ "domainName",
319
+ ]);
320
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
321
+ const obj = withAliases(coerced, {
322
+ query: "symbol",
323
+ name: "symbol",
324
+ symbol_name: "symbol",
325
+ symbolName: "symbol",
326
+ target: "symbol",
327
+ domain_name: "domain",
328
+ domainName: "domain",
329
+ includeDependencies: "include_dependencies",
330
+ include_deps: "include_dependencies",
331
+ includeDeps: "include_dependencies",
332
+ dependencies: "include_dependencies",
333
+ deps: "include_dependencies",
334
+ workspace: "workspace_path",
335
+ workspacePath: "workspace_path",
336
+ project_root: "workspace_path",
337
+ projectRoot: "workspace_path",
338
+ }) as Record<string, unknown>;
339
+ if (obj.symbol !== undefined && obj.query === undefined) {
340
+ obj.query = obj.symbol;
341
+ } else if (obj.query !== undefined && obj.symbol === undefined) {
342
+ obj.symbol = obj.query;
343
+ }
344
+ return obj;
345
+ }
346
+
347
+ export const GetSymbolSchema = z.preprocess(
348
+ normalizeGetSymbolArgs,
349
+ z.object({
350
+ symbol: z.string().min(1, "symbol is required"),
351
+ query: z.string().optional(),
352
+ domain: z.string().optional(),
353
+ include_dependencies: z.boolean().optional().default(true),
354
+ workspace_path: z.string().optional(),
355
+ })
356
+ );
357
+ export type GetSymbolArgs = z.infer<typeof GetSymbolSchema>;
358
+
359
+ export function normalizeGetSymbolImpactArgs(raw: unknown): unknown {
360
+ const coerced = extractSingleStringFallback(raw, "symbol");
361
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
362
+ const obj = withAliases(coerced, {
363
+ query: "symbol",
364
+ name: "symbol",
365
+ symbol_name: "symbol",
366
+ symbolName: "symbol",
367
+ file_path: "symbol",
368
+ filePath: "symbol",
369
+ file: "symbol",
370
+ path: "symbol",
371
+ target: "symbol",
372
+ workspace: "workspace_path",
373
+ workspacePath: "workspace_path",
374
+ project_root: "workspace_path",
375
+ projectRoot: "workspace_path",
376
+ }) as Record<string, unknown>;
377
+ if (obj.symbol !== undefined && obj.query === undefined) {
378
+ obj.query = obj.symbol;
379
+ } else if (obj.query !== undefined && obj.symbol === undefined) {
380
+ obj.symbol = obj.query;
381
+ }
382
+ return obj;
383
+ }
384
+
385
+ export const GetSymbolImpactSchema = z.preprocess(
386
+ normalizeGetSymbolImpactArgs,
387
+ z.object({
388
+ symbol: z.string().min(1, "symbol is required"),
389
+ query: z.string().optional(),
390
+ workspace_path: z.string().optional(),
391
+ })
392
+ );
393
+ export type GetSymbolImpactArgs = z.infer<typeof GetSymbolImpactSchema>;
394
+
395
+ export function normalizeTraceVerticalSliceArgs(raw: unknown): unknown {
396
+ const coerced = extractSingleStringFallback(raw, "query");
397
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
398
+ const obj = withAliases(coerced, {
399
+ route: "query",
400
+ uri: "query",
401
+ route_uri: "query",
402
+ routeUri: "query",
403
+ entry_file: "query",
404
+ entryFile: "query",
405
+ file_path: "query",
406
+ filePath: "query",
407
+ file: "query",
408
+ path: "query",
409
+ symbol: "query",
410
+ name: "query",
411
+ feature: "query",
412
+ feature_key: "query",
413
+ featureKey: "query",
414
+ maxDepth: "max_depth",
415
+ depth: "max_depth",
416
+ workspace: "workspace_path",
417
+ workspacePath: "workspace_path",
418
+ project_root: "workspace_path",
419
+ projectRoot: "workspace_path",
420
+ }) as Record<string, unknown>;
421
+ if (obj.query !== undefined && obj.symbol === undefined) {
422
+ obj.symbol = obj.query;
423
+ } else if (obj.symbol !== undefined && obj.query === undefined) {
424
+ obj.query = obj.symbol;
425
+ }
426
+ if (obj.max_depth !== undefined && obj.max_depth !== null && !Number.isNaN(Number(obj.max_depth))) {
427
+ obj.max_depth = Math.min(Math.max(Math.trunc(Number(obj.max_depth)), 1), 8);
428
+ }
429
+ return obj;
430
+ }
431
+
432
+ export const TraceVerticalSliceSchema = z.preprocess(
433
+ normalizeTraceVerticalSliceArgs,
434
+ z.object({
435
+ query: z.string().min(1, "query is required"),
436
+ symbol: z.string().optional(),
437
+ max_depth: z.number().int().min(1).max(8).optional().default(5),
438
+ workspace_path: z.string().optional(),
439
+ })
440
+ );
441
+ export type TraceVerticalSliceArgs = z.infer<typeof TraceVerticalSliceSchema>;
442
+
443
+ export const CheckBoundarySchema = z.preprocess(
444
+ normalizeBoundaryArgs,
445
+ z.object({
446
+ file_path: z.string().optional(),
447
+ file_paths: z.array(z.string()).optional(),
448
+ proposed_imports: z.array(z.string()).optional().default([]),
449
+ feature_key: z.string().optional(),
450
+ scope: z.enum(["file", "all", "staged", "domain"]).optional(),
451
+ domain: z.string().optional(),
452
+ staged: z.boolean().optional(),
453
+ workspace_path: z.string().optional(),
454
+ })
455
+ );
456
+ export type CheckBoundaryArgs = z.infer<typeof CheckBoundarySchema>;
457
+
458
+ export const GetSymbolHotspotsSchema = z.preprocess(
459
+ (args) =>
460
+ withAliases(args, {
461
+ domain_name: "domain",
462
+ domainName: "domain",
463
+ max: "limit",
464
+ nesting: "min_nesting",
465
+ minNesting: "min_nesting",
466
+ lines: "min_lines",
467
+ minLines: "min_lines",
468
+ sortBy: "sort_by",
469
+ workspace: "workspace_path",
470
+ workspacePath: "workspace_path",
471
+ project_root: "workspace_path",
472
+ projectRoot: "workspace_path",
473
+ }),
474
+ z.object({
475
+ min_lines: z.number().int().nonnegative().optional(),
476
+ min_nesting: z.number().int().positive().optional(),
477
+ kind: z.string().optional(),
478
+ domain: z.string().optional(),
479
+ limit: z.number().int().positive().optional().default(30),
480
+ sort_by: z.enum(["lines", "nesting", "risk_score"]).optional(),
481
+ workspace_path: z.string().optional(),
482
+ })
483
+ );
484
+ export type GetSymbolHotspotsArgs = z.infer<typeof GetSymbolHotspotsSchema>;
485
+
486
+ export function normalizeRegisterDomainArgs(raw: unknown): unknown {
487
+ const coerced = extractSingleStringFallback(raw, "domain", [
488
+ "root",
489
+ "root_path",
490
+ "rootPath",
491
+ "path",
492
+ "root_dir",
493
+ "workspace_path",
494
+ "workspacePath",
495
+ "workspace",
496
+ "project_root",
497
+ "projectRoot",
498
+ ]);
499
+ if (typeof coerced !== "object" || coerced === null || Array.isArray(coerced)) return coerced;
500
+ const obj = withAliases(coerced, {
501
+ name: "domain",
502
+ domain_name: "domain",
503
+ domainName: "domain",
504
+ root_path: "root",
505
+ rootPath: "root",
506
+ path: "root",
507
+ allowedDependencies: "allowed_dependencies",
508
+ allowed: "allowed_dependencies",
509
+ forbiddenDependencies: "forbidden_dependencies",
510
+ forbidden: "forbidden_dependencies",
511
+ archetype: "archetypes",
512
+ ingestNow: "ingest_now",
513
+ workspace: "workspace_path",
514
+ workspacePath: "workspace_path",
515
+ project_root: "workspace_path",
516
+ projectRoot: "workspace_path",
517
+ }) as Record<string, unknown>;
518
+ if (obj.domain !== undefined && obj.name === undefined) {
519
+ obj.name = obj.domain;
520
+ } else if (obj.name !== undefined && obj.domain === undefined) {
521
+ obj.domain = obj.name;
522
+ }
523
+ return obj;
524
+ }
525
+
526
+ export const RegisterDomainSchema = z.preprocess(
527
+ normalizeRegisterDomainArgs,
528
+ z.object({
529
+ domain: z.string().min(1, "Domain name/identifier is required"),
530
+ name: z.string().optional(),
531
+ root: z.string().min(1, "Domain root path is required"),
532
+ description: z.string().optional(),
533
+ allowed_dependencies: z.array(z.string()).optional(),
534
+ forbidden_dependencies: z.array(z.string()).optional(),
535
+ archetypes: z.record(z.string()).optional(),
536
+ ingest_now: z.boolean().optional().default(false),
537
+ workspace_path: z.string().optional(),
538
+ })
539
+ );
540
+ export type RegisterDomainArgs = z.infer<typeof RegisterDomainSchema>;
541
+
542
+ export function normalizeRegisterFeatureArgs(raw: unknown): unknown {
543
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
544
+ const obj = { ...(raw as Record<string, unknown>) };
545
+
546
+ const aliases: Record<string, string> = {
547
+ feature: "feature_key",
548
+ featureKey: "feature_key",
549
+ feature_name: "feature_key",
550
+ featureName: "feature_key",
551
+ feat: "feature_key",
552
+ name: "feature_key",
553
+ key: "feature_key",
554
+ domain_name: "domain",
555
+ domainName: "domain",
556
+ touchpoints: "allowed_touchpoints",
557
+ allowedTouchpoints: "allowed_touchpoints",
558
+ touchpoint: "allowed_touchpoints",
559
+ files: "allowed_touchpoints",
560
+ allowed_files: "allowed_touchpoints",
561
+ reuseSymbols: "reuse_symbols",
562
+ reuse: "reuse_symbols",
563
+ symbols: "reuse_symbols",
564
+ inputContract: "input_contract",
565
+ outputContract: "output_contract",
566
+ workspace: "workspace_path",
567
+ workspacePath: "workspace_path",
568
+ project_root: "workspace_path",
569
+ projectRoot: "workspace_path",
570
+ };
571
+
572
+ for (const [alias, target] of Object.entries(aliases)) {
573
+ if (obj[alias] !== undefined && obj[target] === undefined) {
574
+ obj[target] = obj[alias];
575
+ }
576
+ }
577
+
578
+ // Symmetrical contract: feature_key <-> feature
579
+ if (obj.feature_key !== undefined && obj.feature === undefined) {
580
+ obj.feature = obj.feature_key;
581
+ } else if (obj.feature !== undefined && obj.feature_key === undefined) {
582
+ obj.feature_key = obj.feature;
583
+ }
584
+
585
+ // Symmetrical contract: domain <-> domain_name
586
+ if (obj.domain !== undefined && obj.domain_name === undefined) {
587
+ obj.domain_name = obj.domain;
588
+ } else if (obj.domain_name !== undefined && obj.domain === undefined) {
589
+ obj.domain = obj.domain_name;
590
+ }
591
+
592
+ // Array coercion for allowed_touchpoints
593
+ if (typeof obj.allowed_touchpoints === "string") {
594
+ obj.allowed_touchpoints = [obj.allowed_touchpoints];
595
+ } else if (!Array.isArray(obj.allowed_touchpoints) && obj.allowed_touchpoints !== undefined) {
596
+ obj.allowed_touchpoints = [];
597
+ }
598
+
599
+ // Array coercion for reuse_symbols
600
+ if (typeof obj.reuse_symbols === "string") {
601
+ obj.reuse_symbols = [obj.reuse_symbols];
602
+ }
603
+
604
+ return obj;
605
+ }
606
+
607
+ export const RegisterFeatureSchema = z.preprocess(
608
+ normalizeRegisterFeatureArgs,
609
+ z.object({
610
+ feature_key: z.string().min(1, "Feature key is required"),
611
+ feature: z.string().optional(),
612
+ domain: z.string().min(1, "Domain name is required"),
613
+ domain_name: z.string().optional(),
614
+ description: z.string().optional(),
615
+ allowed_touchpoints: z.array(z.string()).default([]),
616
+ reuse_symbols: z.array(z.string()).optional(),
617
+ input_contract: z.record(z.unknown()).optional(),
618
+ output_contract: z.record(z.unknown()).optional(),
619
+ workspace_path: z.string().optional(),
620
+ })
621
+ );
622
+ export type RegisterFeatureArgs = z.infer<typeof RegisterFeatureSchema>;
623
+
624
+ function normalizeTopologyArgs(raw: unknown): unknown {
625
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
626
+ const obj = withAliases(raw, {
627
+ workspace: "workspace_path",
628
+ workspacePath: "workspace_path",
629
+ project_root: "workspace_path",
630
+ projectRoot: "workspace_path",
631
+ }) as Record<string, unknown>;
632
+ if (obj.resolve_node !== undefined && obj.resolveNode === undefined) {
633
+ obj.resolveNode = obj.resolve_node;
634
+ }
635
+ if (obj.resolve_layer !== undefined && obj.resolve === undefined) {
636
+ obj.resolve = obj.resolve_layer;
637
+ }
638
+ if (obj.resolveLayer !== undefined && obj.resolve === undefined) {
639
+ obj.resolve = obj.resolveLayer;
640
+ }
641
+ if (typeof obj.resolveNode === "object" && obj.resolveNode !== null) {
642
+ obj.resolveNode = withAliases(obj.resolveNode, {
643
+ canonicalTag: "canonical_tag",
644
+ domainOrIp: "domain_or_ip",
645
+ connectedTo: "connected_to",
646
+ nodeId: "id",
647
+ node_id: "id",
648
+ nodeName: "name",
649
+ node_name: "name",
650
+ });
651
+ }
652
+ return obj;
653
+ }
654
+
655
+ export const GetEnvironmentTopologySchema = z.preprocess(
656
+ (args) => normalizeTopologyArgs(args),
657
+ z.object({
658
+ resolve: z
659
+ .object({
660
+ layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
661
+ platform: z.string().min(1, "platform name cannot be empty"),
662
+ constraints: z.array(z.string()).optional(),
663
+ })
664
+ .optional(),
665
+ resolveNode: z
666
+ .object({
667
+ id: z.string().min(1, "Node id cannot be empty"),
668
+ layer: z.enum(["edge", "gateway", "host", "runtime", "storage", "telemetry"]),
669
+ name: z.string().min(1, "Node name cannot be empty"),
670
+ platform: z.string().min(1, "Platform cannot be empty"),
671
+ canonical_tag: z
672
+ .enum([
673
+ "shared_hosting",
674
+ "vps",
675
+ "docker",
676
+ "serverless",
677
+ "kubernetes",
678
+ "managed_db",
679
+ "cdn_edge",
680
+ "paas",
681
+ "static_cdn",
682
+ "unspecified",
683
+ ])
684
+ .optional(),
685
+ domain_or_ip: z.string().optional(),
686
+ role: z.string().optional(),
687
+ constraints: z.array(z.string()).optional(),
688
+ connected_to: z.array(z.string()).optional(),
689
+ })
690
+ .optional(),
691
+ refresh: z.boolean().optional().default(false),
692
+ workspace_path: z.string().optional(),
693
+ })
694
+ );
695
+ export type GetEnvironmentTopologyArgs = z.infer<typeof GetEnvironmentTopologySchema>;