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.
- package/LICENSE +21 -21
- package/README.md +105 -102
- package/bin/septum.ts +4 -4
- package/package.json +63 -63
- 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 +48 -90
- package/src/cli/commands/sync.ts +31 -31
- package/src/cli/commands/topology.ts +119 -0
- package/src/cli/index.ts +210 -199
- package/src/cli/templates/hooks.ts +75 -75
- package/src/cli/templates/rules.ts +38 -38
- package/src/cli/templates/skill.ts +69 -69
- package/src/core/boundary/evaluator.ts +232 -210
- package/src/core/config/loader.ts +97 -97
- package/src/core/config/schema.ts +39 -39
- package/src/core/database/client.ts +44 -44
- package/src/core/database/repositories/dependency.repository.ts +366 -349
- package/src/core/database/repositories/domain.repository.ts +218 -198
- package/src/core/database/repositories/environment.repository.ts +207 -0
- package/src/core/database/repositories/file.repository.ts +116 -106
- package/src/core/database/repositories/meta.repository.ts +33 -33
- package/src/core/database/repositories/symbol.repository.ts +443 -424
- package/src/core/database/repository.ts +264 -255
- package/src/core/database/schema.ts +34 -1
- package/src/core/discovery/topology-detector.ts +486 -486
- package/src/core/ingestion/hasher.ts +5 -5
- package/src/core/ingestion/pipeline.ts +584 -535
- package/src/core/parser/behavioral-analyzer.ts +119 -0
- package/src/core/parser/boundary-tracker.ts +288 -128
- package/src/core/parser/extractors/base.ts +62 -61
- package/src/core/parser/extractors/frontend.ts +194 -194
- package/src/core/parser/extractors/go.ts +229 -176
- package/src/core/parser/extractors/laravel-semantic.ts +1039 -585
- 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 +153 -149
- package/src/core/parser/extractors/python.ts +228 -226
- package/src/core/parser/extractors/registry.ts +70 -70
- package/src/core/parser/extractors/semantic-extractor.interface.ts +21 -21
- package/src/core/parser/extractors/typescript.ts +181 -177
- package/src/core/parser/schema/providers/laravel-migration-provider.ts +215 -0
- package/src/core/parser/schema/providers/prisma-schema-provider.ts +89 -0
- package/src/core/parser/schema/schema-provider-registry.ts +43 -0
- package/src/core/parser/schema/schema-provider.interface.ts +33 -0
- package/src/core/parser/tree-sitter.ts +41 -41
- package/src/core/resolver/call-graph-tracer.ts +666 -668
- package/src/core/resolver/module-resolver.ts +408 -342
- package/src/core/resolver/symbol-locator.ts +543 -543
- package/src/core/resolver/vertical-slice-tracer.ts +301 -252
- package/src/core/session/session-manager.ts +56 -56
- package/src/core/telemetry/telemetry.ts +86 -86
- package/src/core/topology/environment-detector.ts +860 -0
- package/src/core/topology/types.ts +83 -0
- package/src/index.ts +21 -18
- package/src/mcp/schemas.ts +578 -238
- package/src/mcp/server.ts +875 -656
- package/src/mcp/tools/check-boundary.ts +171 -152
- package/src/mcp/tools/get-domain-catalog.ts +145 -121
- package/src/mcp/tools/get-environment-topology.ts +141 -0
- package/src/mcp/tools/get-feature-context.ts +212 -116
- package/src/mcp/tools/get-symbol-hotspots.ts +60 -49
- package/src/mcp/tools/get-symbol-impact.ts +28 -26
- package/src/mcp/tools/get-symbol.ts +65 -63
- package/src/mcp/tools/locate-symbol.ts +171 -190
- package/src/mcp/tools/register-domain.ts +68 -64
- package/src/mcp/tools/register-feature.ts +100 -0
- package/src/mcp/tools/trace-vertical-slice.ts +74 -103
- package/src/types/index.ts +335 -299
|
@@ -1,63 +1,65 @@
|
|
|
1
|
-
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
-
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
-
import { SymbolLocator } from "../../core/resolver/symbol-locator.ts";
|
|
4
|
-
import type { GetSymbolArgs } from "../../types/index.ts";
|
|
5
|
-
|
|
6
|
-
export function handleGetSymbol(
|
|
7
|
-
repo: SeptumRepository,
|
|
8
|
-
_config: ValidatedSeptumConfig,
|
|
9
|
-
args: GetSymbolArgs
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
+
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
+
import { SymbolLocator } from "../../core/resolver/symbol-locator.ts";
|
|
4
|
+
import type { GetSymbolArgs } from "../../types/index.ts";
|
|
5
|
+
|
|
6
|
+
export function handleGetSymbol(
|
|
7
|
+
repo: SeptumRepository,
|
|
8
|
+
_config: ValidatedSeptumConfig,
|
|
9
|
+
args: GetSymbolArgs,
|
|
10
|
+
_workspaceRoot?: string
|
|
11
|
+
) {
|
|
12
|
+
const symbol = (args.symbol || (args as unknown as Record<string, unknown>).query || "").toString().trim();
|
|
13
|
+
if (!symbol) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Missing required argument: 'symbol' (e.g. 'OrderController::cancelOrder', 'OrderService', or 'cancelOrder')"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const details = repo.getSymbolDetails(symbol, {
|
|
20
|
+
domain: args.domain,
|
|
21
|
+
includeDependencies: args.include_dependencies,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (details) {
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text" as const,
|
|
29
|
+
text: JSON.stringify(details, null, 2),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fallback to fuzzy SymbolLocator to provide actionable hints
|
|
36
|
+
const locator = new SymbolLocator(repo);
|
|
37
|
+
const fallback = locator.locate(symbol, args.domain);
|
|
38
|
+
|
|
39
|
+
const payload = {
|
|
40
|
+
symbol,
|
|
41
|
+
found: false,
|
|
42
|
+
message: `Symbol '${symbol}' was not found in the deterministic catalog.`,
|
|
43
|
+
suggestions: fallback.suggestions.map((s) => ({
|
|
44
|
+
name: s.name,
|
|
45
|
+
similarity_score: Math.round(s.similarity_score * 100) / 100,
|
|
46
|
+
exact_range: {
|
|
47
|
+
start_line: s.line_start,
|
|
48
|
+
end_line: s.line_end,
|
|
49
|
+
total_lines: s.line_end - s.line_start + 1,
|
|
50
|
+
},
|
|
51
|
+
signature: s.signature,
|
|
52
|
+
file_path: s.file_path,
|
|
53
|
+
})),
|
|
54
|
+
sibling_methods: fallback.sibling_methods,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text" as const,
|
|
61
|
+
text: JSON.stringify(payload, null, 2),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -1,190 +1,171 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
-
import { isAbsolute, join, basename } from "node:path";
|
|
3
|
-
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
4
|
-
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
5
|
-
import type { IngestionPipeline } from "../../core/ingestion/pipeline.ts";
|
|
6
|
-
import {
|
|
7
|
-
SymbolLocator,
|
|
8
|
-
type SymbolLocationResult,
|
|
9
|
-
} from "../../core/resolver/symbol-locator.ts";
|
|
10
|
-
|
|
11
|
-
export interface LocateSymbolArgs {
|
|
12
|
-
query
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (lowerName.includes(lowerTerm) || lowerTerm.includes(lowerName.replace(/\.[^.]+$/, ""))) {
|
|
173
|
-
matched.push(full);
|
|
174
|
-
if (matched.length >= 5) return;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch {
|
|
179
|
-
// Ignore
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
for (const sDir of searchDirs) {
|
|
184
|
-
scan(sDir);
|
|
185
|
-
if (matched.length >= 5) break;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return matched;
|
|
189
|
-
}
|
|
190
|
-
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, basename } from "node:path";
|
|
3
|
+
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
4
|
+
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
5
|
+
import type { IngestionPipeline } from "../../core/ingestion/pipeline.ts";
|
|
6
|
+
import {
|
|
7
|
+
SymbolLocator,
|
|
8
|
+
type SymbolLocationResult,
|
|
9
|
+
} from "../../core/resolver/symbol-locator.ts";
|
|
10
|
+
|
|
11
|
+
export interface LocateSymbolArgs {
|
|
12
|
+
query?: string;
|
|
13
|
+
symbol?: string;
|
|
14
|
+
domain?: string;
|
|
15
|
+
workspace_path?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function handleLocateSymbol(
|
|
19
|
+
repo: SeptumRepository,
|
|
20
|
+
config: ValidatedSeptumConfig,
|
|
21
|
+
args: LocateSymbolArgs,
|
|
22
|
+
pipeline?: IngestionPipeline,
|
|
23
|
+
workspaceRoot?: string
|
|
24
|
+
) {
|
|
25
|
+
const query = (args.query || args.symbol || "").toString().trim();
|
|
26
|
+
if (!query) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Missing required argument: 'query' (e.g. 'OrderController::calculateTotal' or error log snippet)"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const root = workspaceRoot || process.cwd();
|
|
33
|
+
|
|
34
|
+
// 1. Guarded Lazy Ingestion on Empty Catalog / Cold Start
|
|
35
|
+
if (repo.countFiles() === 0 && pipeline) {
|
|
36
|
+
try {
|
|
37
|
+
await pipeline.run(config);
|
|
38
|
+
} catch {
|
|
39
|
+
// Ingestion fallback
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (repo.countFiles() === 0) {
|
|
43
|
+
const diagnostic = {
|
|
44
|
+
status: "NOT_INDEXED",
|
|
45
|
+
found: false,
|
|
46
|
+
query,
|
|
47
|
+
message: `Workspace '${root}' belum terindeks dalam Septum catalog.`,
|
|
48
|
+
suggested_action: {
|
|
49
|
+
tool: "septum_register_domain",
|
|
50
|
+
params: { name: "core", root: "src", ingest_now: true },
|
|
51
|
+
},
|
|
52
|
+
agent_guidance:
|
|
53
|
+
"Panggil tool 'septum_register_domain(name: \"core\", root: \"src\", ingest_now: true)' untuk mendaftarkan dan mengindeks codebase ini, atau jalankan 'septum init' di terminal workspace.",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: JSON.stringify(diagnostic, null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
metadata: diagnostic,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const locator = new SymbolLocator(repo);
|
|
69
|
+
let result: SymbolLocationResult = locator.locate(query, args.domain);
|
|
70
|
+
|
|
71
|
+
// 2. JIT Delta Sync: Check if located file has changed on disk since last ingest
|
|
72
|
+
if (result.found && result.file_path && pipeline) {
|
|
73
|
+
const fullPath = isAbsolute(result.file_path)
|
|
74
|
+
? result.file_path
|
|
75
|
+
: join(root, result.file_path);
|
|
76
|
+
try {
|
|
77
|
+
const reindexed = await pipeline.ingestFile(fullPath, config);
|
|
78
|
+
if (reindexed) {
|
|
79
|
+
result = locator.locate(query, args.domain);
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// Silently fall back to existing result
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 3. Fallback on Cache Miss: Search for candidate source files matching the query terms
|
|
87
|
+
if (!result.found && pipeline) {
|
|
88
|
+
try {
|
|
89
|
+
const cleanTerm = query.replace(/[^a-zA-Z0-9_]/g, " ").trim().split(/\s+/)[0] || "";
|
|
90
|
+
if (cleanTerm.length >= 3) {
|
|
91
|
+
const candidateFiles = findCandidateFiles(root, cleanTerm);
|
|
92
|
+
let anyReindexed = false;
|
|
93
|
+
for (const candidate of candidateFiles) {
|
|
94
|
+
const reindexed = await pipeline.ingestFile(candidate, config);
|
|
95
|
+
if (reindexed) anyReindexed = true;
|
|
96
|
+
}
|
|
97
|
+
if (anyReindexed) {
|
|
98
|
+
result = locator.locate(query, args.domain);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// Ignore candidate scan errors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const summary = result.found
|
|
107
|
+
? `[FOUND] ${result.exact_symbol?.name || query} in ${result.file_path} (lines ${result.exact_symbol?.line_start}-${result.exact_symbol?.line_end})`
|
|
108
|
+
: `[NOT FOUND] Symbol '${query}' not found. ${result.suggestions.length > 0 ? `Top suggestions: ${result.suggestions.map((s) => s.name).slice(0, 3).join(", ")}` : "No close suggestions."}`;
|
|
109
|
+
|
|
110
|
+
const payload = {
|
|
111
|
+
status: result.found ? "success" : "not_found",
|
|
112
|
+
found: result.found,
|
|
113
|
+
query,
|
|
114
|
+
file_path: result.file_path || null,
|
|
115
|
+
container_name: result.container_name || null,
|
|
116
|
+
exact_symbol: result.exact_symbol || null,
|
|
117
|
+
sibling_methods: result.sibling_methods || [],
|
|
118
|
+
suggestions: result.suggestions || [],
|
|
119
|
+
summary,
|
|
120
|
+
message: result.message || summary,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text" as const,
|
|
127
|
+
text: JSON.stringify(payload, null, 2),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
metadata: payload,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Searches for potential candidate files whose names resemble parts of the query term.
|
|
136
|
+
*/
|
|
137
|
+
function findCandidateFiles(root: string, term: string): string[] {
|
|
138
|
+
const matched: string[] = [];
|
|
139
|
+
const lowerTerm = term.toLowerCase().replace(/_/g, "");
|
|
140
|
+
const searchDirs = ["src", "app", "lib"].map((d) => join(root, d)).filter((d) => existsSync(d));
|
|
141
|
+
|
|
142
|
+
function scan(dir: string, depth: number = 0) {
|
|
143
|
+
if (depth > 4) return;
|
|
144
|
+
try {
|
|
145
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
146
|
+
for (const e of entries) {
|
|
147
|
+
if (e.name.startsWith(".") || e.name === "node_modules" || e.name === "dist") continue;
|
|
148
|
+
const full = join(dir, e.name);
|
|
149
|
+
if (e.isDirectory()) {
|
|
150
|
+
scan(full, depth + 1);
|
|
151
|
+
} else if (e.isFile() && /\.(ts|tsx|js|jsx|php|py|go)$/i.test(e.name)) {
|
|
152
|
+
const lowerName = e.name.toLowerCase().replace(/[-_.]/g, "");
|
|
153
|
+
if (lowerName.includes(lowerTerm) || lowerTerm.includes(lowerName.replace(/\.[^.]+$/, ""))) {
|
|
154
|
+
matched.push(full);
|
|
155
|
+
if (matched.length >= 5) return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
// Ignore
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const sDir of searchDirs) {
|
|
165
|
+
scan(sDir);
|
|
166
|
+
if (matched.length >= 5) break;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return matched;
|
|
170
|
+
}
|
|
171
|
+
|
|
@@ -1,64 +1,68 @@
|
|
|
1
|
-
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
-
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
-
import { IngestionPipeline, type IngestionMetrics } from "../../core/ingestion/pipeline.ts";
|
|
4
|
-
import type { DomainConfig } from "../../types/index.ts";
|
|
5
|
-
|
|
6
|
-
export interface RegisterDomainArgs {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
+
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
+
import { IngestionPipeline, type IngestionMetrics } from "../../core/ingestion/pipeline.ts";
|
|
4
|
+
import type { DomainConfig } from "../../types/index.ts";
|
|
5
|
+
|
|
6
|
+
export interface RegisterDomainArgs {
|
|
7
|
+
domain?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
root: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
allowed_dependencies?: string[];
|
|
12
|
+
forbidden_dependencies?: string[];
|
|
13
|
+
archetypes?: Record<string, string>;
|
|
14
|
+
ingest_now?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function handleRegisterDomain(
|
|
18
|
+
repo: SeptumRepository,
|
|
19
|
+
config: ValidatedSeptumConfig,
|
|
20
|
+
args: RegisterDomainArgs,
|
|
21
|
+
workspaceRoot: string = process.cwd()
|
|
22
|
+
) {
|
|
23
|
+
const domainName = (args.domain || args.name || "").trim();
|
|
24
|
+
if (!domainName || !args.root) {
|
|
25
|
+
throw new Error("Missing required arguments: 'domain' (or 'name') and 'root' are required.");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const domainConfig = {
|
|
29
|
+
root: args.root,
|
|
30
|
+
description: args.description || `Domain: ${domainName}`,
|
|
31
|
+
allowed_dependencies: args.allowed_dependencies || [],
|
|
32
|
+
forbidden_dependencies: args.forbidden_dependencies || [],
|
|
33
|
+
archetypes: args.archetypes || {},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const domainId = repo.domains.upsertDomain(domainName, domainConfig);
|
|
37
|
+
|
|
38
|
+
config.domains[domainName] = domainConfig;
|
|
39
|
+
|
|
40
|
+
let ingestionMetrics: IngestionMetrics | null = null;
|
|
41
|
+
if (args.ingest_now) {
|
|
42
|
+
const pipeline = new IngestionPipeline(repo, workspaceRoot);
|
|
43
|
+
ingestionMetrics = await pipeline.run(config);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text" as const,
|
|
50
|
+
text: JSON.stringify(
|
|
51
|
+
{
|
|
52
|
+
status: "success",
|
|
53
|
+
message: `Domain '${domainName}' successfully registered in SQLite SSOT.${
|
|
54
|
+
args.ingest_now ? " Initial ingestion completed." : ""
|
|
55
|
+
}`,
|
|
56
|
+
domain_id: domainId,
|
|
57
|
+
domain: domainName,
|
|
58
|
+
name: domainName,
|
|
59
|
+
config: domainConfig,
|
|
60
|
+
...(ingestionMetrics ? { ingestion_metrics: ingestionMetrics } : {}),
|
|
61
|
+
},
|
|
62
|
+
null,
|
|
63
|
+
2
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|