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
@@ -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: string;
8
- }
9
-
10
- export function handleTraceVerticalSlice(
11
- repo: SeptumRepository,
12
- config: ValidatedSeptumConfig,
13
- args: TraceVerticalSliceArgs
14
- ) {
15
- if (!args.query) {
16
- throw new Error(
17
- "Missing required argument: 'query' (e.g. 'POST /orders/{id}/status', 'OrderController@updateStatus', or task intent 'Ubah status order di dashboard')"
18
- );
19
- }
20
-
21
- const workspaceRoot = (config.settings as Record<string, unknown>)?.workspace_root as string || process.cwd();
22
- const tracer = new VerticalSliceTracer(repo, workspaceRoot);
23
- const result: VerticalSliceTraceResponse = tracer.trace(args.query);
24
-
25
- let formattedText = "";
26
-
27
- if (result.found && result.slice) {
28
- const s = result.slice;
29
- formattedText = `=== VERTICAL SLICE TRACE ===\n`;
30
- formattedText += `Query: ${result.query}\n`;
31
- formattedText += `Status: RESOLVED\n\n`;
32
-
33
- formattedText += `• Route:\n`;
34
- formattedText += ` Method: ${s.route.method}\n`;
35
- formattedText += ` URI: ${s.route.uri}\n`;
36
- if (s.route.name) {
37
- formattedText += ` Name: ${s.route.name}\n`;
38
- }
39
-
40
- if (s.request) {
41
- formattedText += `\n• Request / Validation:\n`;
42
- formattedText += ` Class: ${s.request.class} (${s.request.file || "inline"})\n`;
43
- const ruleKeys = Object.keys(s.request.rules);
44
- if (ruleKeys.length > 0) {
45
- formattedText += ` Rules:\n`;
46
- for (const k of ruleKeys) {
47
- formattedText += ` - ${k} => ${s.request.rules[k]}\n`;
48
- }
49
- }
50
- }
51
-
52
- formattedText += `\n• Controller Action:\n`;
53
- formattedText += ` Class: ${s.controller.class}@${s.controller.action}\n`;
54
- formattedText += ` File: ${s.controller.file || "unknown"}:${s.controller.line}\n`;
55
-
56
- if (s.model) {
57
- formattedText += `\n• Model:\n`;
58
- formattedText += ` Class: ${s.model.class} (${s.model.file || "unknown"})\n`;
59
- if (s.model.fillable && s.model.fillable.length > 0) {
60
- formattedText += ` Fillable: [${s.model.fillable.join(", ")}]\n`;
61
- }
62
- if (s.model.casts && Object.keys(s.model.casts).length > 0) {
63
- formattedText += ` Casts: ${JSON.stringify(s.model.casts)}\n`;
64
- }
65
- }
66
-
67
- if (s.frontend) {
68
- formattedText += `\n• Frontend Target:\n`;
69
- formattedText += ` Page: ${s.frontend.target}\n`;
70
- if (s.frontend.props.length > 0) {
71
- formattedText += ` Props: [${s.frontend.props.join(", ")}]\n`;
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
+ }