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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
4
|
+
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
5
|
+
import { SessionManager } from "../../core/session/session-manager.ts";
|
|
6
|
+
import type { FeatureConfig, RegisterFeatureArgs } from "../../types/index.ts";
|
|
7
|
+
|
|
8
|
+
export async function handleRegisterFeature(
|
|
9
|
+
_repo: SeptumRepository,
|
|
10
|
+
config: ValidatedSeptumConfig,
|
|
11
|
+
args: RegisterFeatureArgs,
|
|
12
|
+
workspaceRoot: string = process.cwd()
|
|
13
|
+
) {
|
|
14
|
+
const featureKey = (args.feature_key || (args as unknown as Record<string, unknown>).feature || "").toString().trim();
|
|
15
|
+
const domain = (args.domain || (args as unknown as Record<string, unknown>).domain_name || "").toString().trim();
|
|
16
|
+
|
|
17
|
+
if (!featureKey || !domain) {
|
|
18
|
+
throw new Error("Missing required arguments: 'feature_key' and 'domain' are required.");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const rawTouchpoints = args.allowed_touchpoints ?? [];
|
|
22
|
+
const allowedTouchpoints = Array.isArray(rawTouchpoints)
|
|
23
|
+
? rawTouchpoints
|
|
24
|
+
: typeof rawTouchpoints === "string"
|
|
25
|
+
? [rawTouchpoints]
|
|
26
|
+
: [];
|
|
27
|
+
|
|
28
|
+
const rawReuse = args.reuse_symbols;
|
|
29
|
+
const reuseSymbols: string[] = Array.isArray(rawReuse)
|
|
30
|
+
? rawReuse
|
|
31
|
+
: typeof rawReuse === "string"
|
|
32
|
+
? [rawReuse]
|
|
33
|
+
: [];
|
|
34
|
+
|
|
35
|
+
const featureConfig = {
|
|
36
|
+
domain,
|
|
37
|
+
description: args.description || `Feature: ${featureKey}`,
|
|
38
|
+
allowed_touchpoints: allowedTouchpoints,
|
|
39
|
+
reuse_symbols: reuseSymbols,
|
|
40
|
+
input_contract: args.input_contract || {},
|
|
41
|
+
output_contract: args.output_contract || {},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (!config.features) {
|
|
45
|
+
config.features = {};
|
|
46
|
+
}
|
|
47
|
+
config.features[featureKey] = featureConfig;
|
|
48
|
+
|
|
49
|
+
// Record Active Feature Session Lease (.septum/session.json)
|
|
50
|
+
SessionManager.saveActiveSession(workspaceRoot, {
|
|
51
|
+
feature_key: featureKey,
|
|
52
|
+
domain,
|
|
53
|
+
touchpoints: featureConfig.allowed_touchpoints,
|
|
54
|
+
locked_at: new Date().toISOString(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let persisted = false;
|
|
58
|
+
if (args.persist_to_config) {
|
|
59
|
+
try {
|
|
60
|
+
const configPath = path.resolve(workspaceRoot, "septum.config.json");
|
|
61
|
+
let diskConfig: Record<string, unknown> = {};
|
|
62
|
+
if (fs.existsSync(configPath)) {
|
|
63
|
+
diskConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
64
|
+
}
|
|
65
|
+
if (!diskConfig.features || typeof diskConfig.features !== "object") {
|
|
66
|
+
diskConfig.features = {};
|
|
67
|
+
}
|
|
68
|
+
(diskConfig.features as Record<string, unknown>)[featureKey] = featureConfig;
|
|
69
|
+
fs.writeFileSync(configPath, JSON.stringify(diskConfig, null, 2), "utf-8");
|
|
70
|
+
persisted = true;
|
|
71
|
+
} catch (err: unknown) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
73
|
+
console.warn(`[Septum Warning] Failed to persist feature to septum.config.json: ${msg}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text" as const,
|
|
81
|
+
text: JSON.stringify(
|
|
82
|
+
{
|
|
83
|
+
status: "success",
|
|
84
|
+
message: `Feature '${featureKey}' successfully registered and session lease established.${
|
|
85
|
+
persisted ? " Persisted to septum.config.json." : ""
|
|
86
|
+
}`,
|
|
87
|
+
feature_key: featureKey,
|
|
88
|
+
feature: featureKey,
|
|
89
|
+
domain,
|
|
90
|
+
touchpoints: featureConfig.allowed_touchpoints,
|
|
91
|
+
reuse_symbols: featureConfig.reuse_symbols,
|
|
92
|
+
persisted_to_disk: persisted,
|
|
93
|
+
},
|
|
94
|
+
null,
|
|
95
|
+
2
|
|
96
|
+
),
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -1,103 +1,74 @@
|
|
|
1
|
-
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
-
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
-
import { VerticalSliceTracer } from "../../core/resolver/vertical-slice-tracer.ts";
|
|
4
|
-
import type { VerticalSliceTraceResponse } from "../../types/index.ts";
|
|
5
|
-
|
|
6
|
-
export interface TraceVerticalSliceArgs {
|
|
7
|
-
query
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (result.alternatives && result.alternatives.length > 0) {
|
|
76
|
-
formattedText += `\n• Other Matching Slices:\n`;
|
|
77
|
-
for (const alt of result.alternatives) {
|
|
78
|
-
formattedText += ` - ${alt.method} ${alt.uri} -> ${alt.controller}@${alt.action}\n`;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
formattedText = `=== VERTICAL SLICE NOT FOUND ===\n`;
|
|
83
|
-
formattedText += `Query: ${result.query}\n`;
|
|
84
|
-
formattedText += `Message: ${result.message}\n`;
|
|
85
|
-
|
|
86
|
-
if (result.alternatives && result.alternatives.length > 0) {
|
|
87
|
-
formattedText += `\nAvailable Slices:\n`;
|
|
88
|
-
for (const alt of result.alternatives) {
|
|
89
|
-
formattedText += ` - ${alt.method} ${alt.uri} -> ${alt.controller}@${alt.action}\n`;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
content: [
|
|
96
|
-
{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: formattedText.trim(),
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
metadata: result,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
1
|
+
import type { ValidatedSeptumConfig } from "../../core/config/schema.ts";
|
|
2
|
+
import type { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
|
+
import { VerticalSliceTracer } from "../../core/resolver/vertical-slice-tracer.ts";
|
|
4
|
+
import type { VerticalSliceTraceResponse } from "../../types/index.ts";
|
|
5
|
+
|
|
6
|
+
export interface TraceVerticalSliceArgs {
|
|
7
|
+
query?: string;
|
|
8
|
+
symbol?: string;
|
|
9
|
+
route?: string;
|
|
10
|
+
max_depth?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function handleTraceVerticalSlice(
|
|
14
|
+
repo: SeptumRepository,
|
|
15
|
+
config: ValidatedSeptumConfig,
|
|
16
|
+
args: TraceVerticalSliceArgs,
|
|
17
|
+
workspaceRoot?: string
|
|
18
|
+
) {
|
|
19
|
+
const query = (
|
|
20
|
+
args.query ||
|
|
21
|
+
args.symbol ||
|
|
22
|
+
args.route ||
|
|
23
|
+
""
|
|
24
|
+
).toString().trim();
|
|
25
|
+
|
|
26
|
+
if (!query) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Missing required argument: 'query' (e.g. 'POST /orders/{id}/status', 'OrderController@updateStatus', or task intent 'Ubah status order di dashboard')"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const root =
|
|
33
|
+
workspaceRoot ||
|
|
34
|
+
((config.settings as Record<string, unknown>)?.workspace_root as string) ||
|
|
35
|
+
process.cwd();
|
|
36
|
+
const tracer = new VerticalSliceTracer(repo, root);
|
|
37
|
+
const result: VerticalSliceTraceResponse = tracer.trace(query);
|
|
38
|
+
const chain = result.chain || result.slice || [];
|
|
39
|
+
|
|
40
|
+
let summary = "";
|
|
41
|
+
if (result.found && chain.length > 0) {
|
|
42
|
+
summary = chain
|
|
43
|
+
.map((node) => `[${(node.stage || "STAGE").toUpperCase()}] ${node.symbol}`)
|
|
44
|
+
.join(" ➔ ");
|
|
45
|
+
} else if (result.found && result.entrypoint) {
|
|
46
|
+
summary = `[ENTRYPOINT] ${result.entrypoint.method} ${result.entrypoint.uri} ➔ ${result.entrypoint.controller}@${result.entrypoint.action}`;
|
|
47
|
+
} else {
|
|
48
|
+
summary = `[NOT FOUND] No vertical slice matching '${query}'`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const payload = {
|
|
52
|
+
status: result.found ? "success" : "not_found",
|
|
53
|
+
query,
|
|
54
|
+
found: result.found,
|
|
55
|
+
confidence: result.confidence || (result.found ? "exact" : undefined),
|
|
56
|
+
is_exact_match: result.is_exact_match,
|
|
57
|
+
architecture_style: result.architecture_style,
|
|
58
|
+
entrypoint: result.entrypoint || null,
|
|
59
|
+
chain,
|
|
60
|
+
alternatives: result.alternatives || [],
|
|
61
|
+
summary,
|
|
62
|
+
message: result.message || summary,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text" as const,
|
|
69
|
+
text: JSON.stringify(payload, null, 2),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
metadata: payload,
|
|
73
|
+
};
|
|
74
|
+
}
|