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.
- package/LICENSE +21 -21
- package/README.md +105 -105
- 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 +216 -188
- package/src/cli/commands/ingest.ts +29 -29
- package/src/cli/commands/init.ts +213 -211
- package/src/cli/commands/locate.ts +61 -61
- package/src/cli/commands/query.ts +104 -64
- package/src/cli/commands/serve.ts +5 -5
- package/src/cli/commands/slice.ts +50 -48
- package/src/cli/commands/sync.ts +31 -31
- package/src/cli/index.ts +220 -210
- package/src/cli/templates/hooks.ts +75 -75
- package/src/cli/templates/rules.ts +50 -38
- package/src/cli/templates/skill.ts +65 -69
- package/src/core/boundary/evaluator.ts +429 -210
- package/src/core/config/loader.ts +117 -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 +552 -349
- package/src/core/database/repositories/domain.repository.ts +275 -198
- package/src/core/database/repositories/file.repository.ts +117 -106
- package/src/core/database/repositories/meta.repository.ts +33 -33
- package/src/core/database/repositories/symbol.repository.ts +482 -443
- package/src/core/database/repository.ts +281 -260
- package/src/core/database/schema.ts +154 -153
- package/src/core/discovery/topology-detector.ts +486 -486
- package/src/core/ingestion/hasher.ts +5 -5
- package/src/core/ingestion/pipeline.ts +606 -535
- package/src/core/parser/boundary-tracker.ts +433 -288
- package/src/core/parser/extractors/base.ts +62 -62
- package/src/core/parser/extractors/blade.ts +158 -158
- package/src/core/parser/extractors/frontend.ts +147 -194
- package/src/core/parser/extractors/go.ts +408 -180
- package/src/core/parser/extractors/laravel-semantic.ts +1045 -901
- package/src/core/parser/extractors/mcp-cli-semantic.ts +328 -199
- package/src/core/parser/extractors/nestjs-semantic.ts +134 -119
- package/src/core/parser/extractors/php.ts +415 -153
- package/src/core/parser/extractors/python.ts +460 -228
- 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 +668 -181
- package/src/core/parser/tree-sitter.ts +41 -41
- package/src/core/parser/wasm-loader.ts +271 -0
- package/src/core/resolver/call-graph-tracer.ts +698 -668
- package/src/core/resolver/module-resolver.ts +479 -342
- package/src/core/resolver/path-resolver.ts +147 -147
- package/src/core/resolver/symbol-locator.ts +543 -543
- package/src/core/resolver/vertical-slice-tracer.ts +323 -285
- package/src/core/session/session-manager.ts +56 -56
- package/src/core/telemetry/telemetry.ts +86 -86
- package/src/core/topology/environment-detector.ts +109 -6
- package/src/index.ts +21 -21
- package/src/mcp/schemas.ts +695 -286
- package/src/mcp/server.ts +927 -716
- package/src/mcp/tools/check-boundary.ts +307 -152
- package/src/mcp/tools/get-domain-catalog.ts +155 -121
- package/src/mcp/tools/get-environment-topology.ts +3 -6
- package/src/mcp/tools/get-feature-context.ts +227 -116
- package/src/mcp/tools/get-symbol-hotspots.ts +54 -59
- 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 +169 -190
- package/src/mcp/tools/register-domain.ts +59 -64
- package/src/mcp/tools/register-feature.ts +91 -0
- package/src/mcp/tools/trace-vertical-slice.ts +74 -48
- package/src/types/index.ts +366 -315
- package/src/version.ts +3 -3
|
@@ -1,106 +1,117 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
import type { ArchetypeKind, DomainRecord, FileRecord } from "../../../types/index.ts";
|
|
3
|
-
|
|
4
|
-
export class FileRepository {
|
|
5
|
-
constructor(private db: Database) {}
|
|
6
|
-
|
|
7
|
-
public getFileByPath(path: string): FileRecord | null {
|
|
8
|
-
return this.db
|
|
9
|
-
.query<FileRecord, [string]>("SELECT * FROM files WHERE path = ?")
|
|
10
|
-
.get(path);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public getFileById(id: number): FileRecord | null {
|
|
14
|
-
return this.db
|
|
15
|
-
.query<FileRecord, [number]>("SELECT * FROM files WHERE id = ?")
|
|
16
|
-
.get(id);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public getFilesByDomain(domainId: number): FileRecord[] {
|
|
20
|
-
return this.db
|
|
21
|
-
.query<FileRecord, [number]>("SELECT * FROM files WHERE domain_id = ? ORDER BY path ASC")
|
|
22
|
-
.all(domainId);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public
|
|
26
|
-
|
|
27
|
-
.query<
|
|
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
|
-
}
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { ArchetypeKind, DomainRecord, FileRecord } from "../../../types/index.ts";
|
|
3
|
+
|
|
4
|
+
export class FileRepository {
|
|
5
|
+
constructor(private db: Database) {}
|
|
6
|
+
|
|
7
|
+
public getFileByPath(path: string): FileRecord | null {
|
|
8
|
+
return this.db
|
|
9
|
+
.query<FileRecord, [string]>("SELECT * FROM files WHERE path = ?")
|
|
10
|
+
.get(path);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public getFileById(id: number): FileRecord | null {
|
|
14
|
+
return this.db
|
|
15
|
+
.query<FileRecord, [number]>("SELECT * FROM files WHERE id = ?")
|
|
16
|
+
.get(id);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public getFilesByDomain(domainId: number): FileRecord[] {
|
|
20
|
+
return this.db
|
|
21
|
+
.query<FileRecord, [number]>("SELECT * FROM files WHERE domain_id = ? ORDER BY path ASC")
|
|
22
|
+
.all(domainId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public getAllFiles(): FileRecord[] {
|
|
26
|
+
return this.db
|
|
27
|
+
.query<FileRecord, []>("SELECT * FROM files ORDER BY path ASC")
|
|
28
|
+
.all();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public countFiles(): number {
|
|
32
|
+
const row = this.db
|
|
33
|
+
.query<{ count: number }, []>("SELECT COUNT(*) as count FROM files")
|
|
34
|
+
.get();
|
|
35
|
+
return row?.count ?? 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public upsertFile(
|
|
39
|
+
domainId: number,
|
|
40
|
+
path: string,
|
|
41
|
+
archetype: ArchetypeKind,
|
|
42
|
+
contentHash: string,
|
|
43
|
+
lineCount: number,
|
|
44
|
+
mtimeMs: number = 0,
|
|
45
|
+
sizeBytes: number = 0
|
|
46
|
+
): number {
|
|
47
|
+
const row = this.db
|
|
48
|
+
.query<{ id: number }, [number, string, string, string, number, number, number]>(
|
|
49
|
+
`INSERT INTO files (domain_id, path, archetype, content_hash, line_count, mtime_ms, size_bytes)
|
|
50
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
51
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
52
|
+
domain_id = excluded.domain_id,
|
|
53
|
+
archetype = excluded.archetype,
|
|
54
|
+
content_hash = excluded.content_hash,
|
|
55
|
+
line_count = excluded.line_count,
|
|
56
|
+
mtime_ms = excluded.mtime_ms,
|
|
57
|
+
size_bytes = excluded.size_bytes,
|
|
58
|
+
last_scanned_at = CURRENT_TIMESTAMP
|
|
59
|
+
RETURNING id`
|
|
60
|
+
)
|
|
61
|
+
.get(domainId, path, archetype, contentHash, lineCount, mtimeMs, sizeBytes);
|
|
62
|
+
return row ? Number(row.id) : 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public updateFileMetadataOnly(id: number, mtimeMs: number, sizeBytes: number): void {
|
|
66
|
+
this.db
|
|
67
|
+
.query(
|
|
68
|
+
`UPDATE files
|
|
69
|
+
SET mtime_ms = ?, size_bytes = ?, last_scanned_at = CURRENT_TIMESTAMP
|
|
70
|
+
WHERE id = ?`
|
|
71
|
+
)
|
|
72
|
+
.run(mtimeMs, sizeBytes, id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public deleteFilesNotInPaths(domainId: number, validPaths: string[]): number {
|
|
76
|
+
if (validPaths.length === 0) {
|
|
77
|
+
const res = this.db.query("DELETE FROM files WHERE domain_id = ?").run(domainId);
|
|
78
|
+
return Number(res.changes);
|
|
79
|
+
}
|
|
80
|
+
const existingFiles = this.getFilesByDomain(domainId);
|
|
81
|
+
const validSet = new Set(validPaths);
|
|
82
|
+
const idsToDelete = existingFiles.filter((f) => !validSet.has(f.path)).map((f) => f.id);
|
|
83
|
+
if (idsToDelete.length === 0) return 0;
|
|
84
|
+
|
|
85
|
+
const chunkSize = 200;
|
|
86
|
+
let totalDeleted = 0;
|
|
87
|
+
for (let i = 0; i < idsToDelete.length; i += chunkSize) {
|
|
88
|
+
const chunk = idsToDelete.slice(i, i + chunkSize);
|
|
89
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
90
|
+
const res = this.db.query(`DELETE FROM files WHERE id IN (${placeholders})`).run(...chunk);
|
|
91
|
+
totalDeleted += Number(res.changes);
|
|
92
|
+
}
|
|
93
|
+
return totalDeleted;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public deleteFile(path: string): void {
|
|
97
|
+
this.db.query("DELETE FROM files WHERE path = ?").run(path);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public deleteFileByPath(path: string): void {
|
|
101
|
+
this.deleteFile(path);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public getFileWithDomain(
|
|
105
|
+
filePath: string
|
|
106
|
+
): { file: FileRecord; domain: DomainRecord } | null {
|
|
107
|
+
const file = this.getFileByPath(filePath);
|
|
108
|
+
if (!file) return null;
|
|
109
|
+
|
|
110
|
+
const domain = this.db
|
|
111
|
+
.query<DomainRecord, [number]>("SELECT * FROM domains WHERE id = ?")
|
|
112
|
+
.get(file.domain_id);
|
|
113
|
+
if (!domain) return null;
|
|
114
|
+
|
|
115
|
+
return { file, domain };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
|
|
3
|
-
export class MetaRepository {
|
|
4
|
-
constructor(private db: Database) {}
|
|
5
|
-
|
|
6
|
-
public getMeta(key: string): string | null {
|
|
7
|
-
const row = this.db
|
|
8
|
-
.query<{ value: string }, [string]>("SELECT value FROM repo_meta WHERE key = ?")
|
|
9
|
-
.get(key);
|
|
10
|
-
return row ? row.value : null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public setMeta(key: string, value: string): void {
|
|
14
|
-
this.db
|
|
15
|
-
.query(
|
|
16
|
-
`INSERT INTO repo_meta (key, value, updated_at)
|
|
17
|
-
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
18
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP`
|
|
19
|
-
)
|
|
20
|
-
.run(key, value);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public getAllMeta(): Record<string, string> {
|
|
24
|
-
const rows = this.db
|
|
25
|
-
.query<{ key: string; value: string }, []>("SELECT key, value FROM repo_meta")
|
|
26
|
-
.all();
|
|
27
|
-
const result: Record<string, string> = {};
|
|
28
|
-
for (const r of rows) {
|
|
29
|
-
result[r.key] = r.value;
|
|
30
|
-
}
|
|
31
|
-
return result;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
|
|
3
|
+
export class MetaRepository {
|
|
4
|
+
constructor(private db: Database) {}
|
|
5
|
+
|
|
6
|
+
public getMeta(key: string): string | null {
|
|
7
|
+
const row = this.db
|
|
8
|
+
.query<{ value: string }, [string]>("SELECT value FROM repo_meta WHERE key = ?")
|
|
9
|
+
.get(key);
|
|
10
|
+
return row ? row.value : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public setMeta(key: string, value: string): void {
|
|
14
|
+
this.db
|
|
15
|
+
.query(
|
|
16
|
+
`INSERT INTO repo_meta (key, value, updated_at)
|
|
17
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
18
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP`
|
|
19
|
+
)
|
|
20
|
+
.run(key, value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public getAllMeta(): Record<string, string> {
|
|
24
|
+
const rows = this.db
|
|
25
|
+
.query<{ key: string; value: string }, []>("SELECT key, value FROM repo_meta")
|
|
26
|
+
.all();
|
|
27
|
+
const result: Record<string, string> = {};
|
|
28
|
+
for (const r of rows) {
|
|
29
|
+
result[r.key] = r.value;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
}
|