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
@@ -1,211 +1,211 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { handleIngestCommand } from "./ingest.ts";
4
- import {
5
- SEPTUM_HOOKS_CONFIG,
6
- SEPTUM_POST_WRITE_SCRIPT,
7
- SEPTUM_PRE_WRITE_SCRIPT,
8
- } from "../templates/hooks.ts";
9
- import { SEPTUM_RULES_TEMPLATE } from "../templates/rules.ts";
10
- import { SEPTUM_MAP_SKILL_TEMPLATE } from "../templates/skill.ts";
11
- import { TopologyDetector } from "../../core/discovery/topology-detector.ts";
12
- import { SeptumDatabase } from "../../core/database/client.ts";
13
- import { SeptumRepository } from "../../core/database/repository.ts";
14
-
15
- export async function handleInitCommand(force: boolean = false): Promise<void> {
16
- console.log("[Septum] Initializing Septum bounded-context environment (Zero-Config)...\n");
17
-
18
- const cwd = process.cwd();
19
- const dbPath = ".septum/septum.db";
20
-
21
- // 1. Auto-discover project topology and persist to SQLite SSOT
22
- console.log(" • Analyzing codebase topology and modular boundaries...");
23
- const db = new SeptumDatabase(dbPath);
24
- const repo = new SeptumRepository(db.raw);
25
- const domains = TopologyDetector.discoverAndPersist(repo, cwd);
26
- const domainNames = Object.keys(domains);
27
- console.log(` ✓ Discovered and registered ${domainNames.length} domains directly in SQLite (${dbPath}):`);
28
- for (const name of domainNames) {
29
- console.log(` - ${name}: root -> '${domains[name].root}'`);
30
- }
31
- db.close();
32
-
33
- // 2. Ensure .gitignore excludes local SQLite cache
34
- ensureGitIgnore(cwd);
35
-
36
- // 3. Install Git Pre-Commit Hook
37
- installGitHook(cwd);
38
-
39
- // 4. Scaffold Agent Customizations (.agents/skills, .agents/hooks, .agents/rules)
40
- scaffoldAgentCustomizations(cwd);
41
-
42
- // 5. Run initial codebase ingestion
43
- console.log("\n[Septum] Running initial ingestion to build deterministic structural catalog...");
44
- try {
45
- await handleIngestCommand();
46
- console.log("\n✓ Septum successfully initialized and ready for production use!");
47
- console.log(" • Pre-commit hook is active (runs 'septum check --staged' before each commit).");
48
- console.log(" • AI Agent skill '/septum-map' and boundary hooks installed in '.agents/'.");
49
- console.log(" • MCP server is ready for AI coding agents via 'septum serve'.\n");
50
- } catch (err: unknown) {
51
- const message = err instanceof Error ? err.message : String(err);
52
- console.warn(`\n[Septum Warning] Initial ingestion encountered an error: ${message}`);
53
- console.warn("Run 'septum ingest' manually to retry.");
54
- }
55
- }
56
-
57
- function ensureGitIgnore(cwd: string): void {
58
- const gitignorePath = path.join(cwd, ".gitignore");
59
- const entriesToEnsure = [".septum/*.db*", ".septum/"];
60
-
61
- if (fs.existsSync(gitignorePath)) {
62
- let content = fs.readFileSync(gitignorePath, "utf-8");
63
- const missing = entriesToEnsure.filter((entry) => !content.includes(entry));
64
-
65
- if (missing.length > 0) {
66
- const addition = `\n# Septum local database and cache\n${missing.join("\n")}\n`;
67
- fs.appendFileSync(gitignorePath, addition, "utf-8");
68
- console.log(" ✓ Updated .gitignore to exclude local SQLite cache (.septum/).");
69
- } else {
70
- console.log(" ✓ .gitignore already configured for Septum.");
71
- }
72
- } else {
73
- fs.writeFileSync(
74
- gitignorePath,
75
- `# Septum local database and cache\n${entriesToEnsure.join("\n")}\n`,
76
- "utf-8"
77
- );
78
- console.log(" ✓ Created .gitignore excluding local SQLite cache (.septum/).");
79
- }
80
- }
81
-
82
- function installGitHook(cwd: string): void {
83
- const gitDir = path.join(cwd, ".git");
84
- if (!fs.existsSync(gitDir)) {
85
- console.log(" ⚠ No .git directory found. Skipping pre-commit hook installation.");
86
- return;
87
- }
88
-
89
- const huskyDir = path.join(cwd, ".husky");
90
- if (fs.existsSync(huskyDir)) {
91
- const huskyPreCommit = path.join(huskyDir, "pre-commit");
92
- const hookCommand = "[ -f ./bin/septum.ts ] && bun run ./bin/septum.ts check --staged || command -v septum >/dev/null 2>&1 && septum check --staged || [ -f ./node_modules/.bin/septum ] && ./node_modules/.bin/septum check --staged || bunx septum check --staged";
93
- if (fs.existsSync(huskyPreCommit)) {
94
- const existing = fs.readFileSync(huskyPreCommit, "utf-8");
95
- if (!existing.includes("septum check")) {
96
- fs.appendFileSync(huskyPreCommit, `\n${hookCommand}\n`, "utf-8");
97
- }
98
- } else {
99
- fs.writeFileSync(huskyPreCommit, `#!/usr/bin/env sh\n${hookCommand}\n`, "utf-8");
100
- try {
101
- fs.chmodSync(huskyPreCommit, 0o755);
102
- } catch (err: unknown) {
103
- const message = err instanceof Error ? err.message : String(err);
104
- console.warn(` Notice: Could not set executable permission on '${huskyPreCommit}': ${message}`);
105
- }
106
- }
107
- console.log(" ✓ Installed pre-commit hook in .husky/pre-commit.");
108
- return;
109
- }
110
-
111
- const hooksDir = path.join(gitDir, "hooks");
112
- if (!fs.existsSync(hooksDir)) {
113
- fs.mkdirSync(hooksDir, { recursive: true });
114
- }
115
-
116
- const preCommitHook = path.join(hooksDir, "pre-commit");
117
- const hookScript = `#!/usr/bin/env sh
118
- # Septum deterministic boundary pre-commit hook
119
- echo "[Septum Hook] Auditing staged changes against domain boundaries..."
120
-
121
- if command -v bun >/dev/null 2>&1 && [ -f "./bin/septum.ts" ]; then
122
- bun run ./bin/septum.ts check --staged
123
- elif command -v septum >/dev/null 2>&1; then
124
- septum check --staged
125
- elif [ -f "./node_modules/.bin/septum" ]; then
126
- ./node_modules/.bin/septum check --staged
127
- elif command -v bunx >/dev/null 2>&1; then
128
- bunx septum check --staged
129
- else
130
- echo "[Septum Hook] Notice: Septum executable not found. Skipping boundary check."
131
- fi
132
- `;
133
-
134
- fs.writeFileSync(preCommitHook, hookScript, "utf-8");
135
- try {
136
- fs.chmodSync(preCommitHook, 0o755);
137
- } catch (err: unknown) {
138
- const message = err instanceof Error ? err.message : String(err);
139
- console.warn(` Notice: Could not set executable permission on '${preCommitHook}': ${message}`);
140
- }
141
- console.log(" ✓ Installed executable git pre-commit hook at '.git/hooks/pre-commit'.");
142
- }
143
-
144
- function scaffoldAgentCustomizations(cwd: string): void {
145
- const agentsDir = path.join(cwd, ".agents");
146
-
147
- // 1. Skill: .agents/skills/septum-map/SKILL.md
148
- const skillDir = path.join(agentsDir, "skills", "septum-map");
149
- if (!fs.existsSync(skillDir)) {
150
- fs.mkdirSync(skillDir, { recursive: true });
151
- }
152
- const skillPath = path.join(skillDir, "SKILL.md");
153
- fs.writeFileSync(skillPath, SEPTUM_MAP_SKILL_TEMPLATE, "utf-8");
154
- console.log(" ✓ Deployed AI Agent skill: '.agents/skills/septum-map/SKILL.md'.");
155
-
156
- // 2. Rule: .agents/rules/septum-boundary.md
157
- const rulesDir = path.join(agentsDir, "rules");
158
- if (!fs.existsSync(rulesDir)) {
159
- fs.mkdirSync(rulesDir, { recursive: true });
160
- }
161
- const rulePath = path.join(rulesDir, "septum-boundary.md");
162
- fs.writeFileSync(rulePath, SEPTUM_RULES_TEMPLATE, "utf-8");
163
- console.log(" ✓ Deployed AI Agent boundary rules: '.agents/rules/septum-boundary.md'.");
164
-
165
- // 3. Hook Script: .agents/hooks/septum-pre-write.sh
166
- const hooksDir = path.join(agentsDir, "hooks");
167
- if (!fs.existsSync(hooksDir)) {
168
- fs.mkdirSync(hooksDir, { recursive: true });
169
- }
170
- const preWriteHook = path.join(hooksDir, "septum-pre-write.sh");
171
- fs.writeFileSync(preWriteHook, SEPTUM_PRE_WRITE_SCRIPT, "utf-8");
172
- try {
173
- fs.chmodSync(preWriteHook, 0o755);
174
- } catch (err: unknown) {
175
- const message = err instanceof Error ? err.message : String(err);
176
- console.warn(` Notice: Could not set executable permission on '${preWriteHook}': ${message}`);
177
- }
178
- console.log(" ✓ Deployed pre-write hook script: '.agents/hooks/septum-pre-write.sh'.");
179
-
180
- // 3b. Post-Write Hook Script: .agents/hooks/septum-post-write.sh
181
- const postWriteHook = path.join(hooksDir, "septum-post-write.sh");
182
- fs.writeFileSync(postWriteHook, SEPTUM_POST_WRITE_SCRIPT, "utf-8");
183
- try {
184
- fs.chmodSync(postWriteHook, 0o755);
185
- } catch (err: unknown) {
186
- const message = err instanceof Error ? err.message : String(err);
187
- console.warn(` Notice: Could not set executable permission on '${postWriteHook}': ${message}`);
188
- }
189
- console.log(" ✓ Deployed post-write hook script: '.agents/hooks/septum-post-write.sh'.");
190
-
191
- // 4. Hooks Config: .agents/hooks.json
192
- const hooksJsonPath = path.join(agentsDir, "hooks.json");
193
- let existingHooks: Record<string, unknown> = {};
194
-
195
- if (fs.existsSync(hooksJsonPath)) {
196
- try {
197
- const raw = fs.readFileSync(hooksJsonPath, "utf-8");
198
- existingHooks = JSON.parse(raw);
199
- } catch {
200
- // Malformed JSON fallback
201
- }
202
- }
203
-
204
- const mergedHooks = {
205
- ...existingHooks,
206
- ...SEPTUM_HOOKS_CONFIG,
207
- };
208
-
209
- fs.writeFileSync(hooksJsonPath, JSON.stringify(mergedHooks, null, 2) + "\n", "utf-8");
210
- console.log(" ✓ Configured AI Agent lifecycle hooks: '.agents/hooks.json'.");
211
- }
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { handleIngestCommand } from "./ingest.ts";
4
+ import {
5
+ SEPTUM_HOOKS_CONFIG,
6
+ SEPTUM_POST_WRITE_SCRIPT,
7
+ SEPTUM_PRE_WRITE_SCRIPT,
8
+ } from "../templates/hooks.ts";
9
+ import { SEPTUM_RULES_TEMPLATE } from "../templates/rules.ts";
10
+ import { SEPTUM_MAP_SKILL_TEMPLATE } from "../templates/skill.ts";
11
+ import { TopologyDetector } from "../../core/discovery/topology-detector.ts";
12
+ import { SeptumDatabase } from "../../core/database/client.ts";
13
+ import { SeptumRepository } from "../../core/database/repository.ts";
14
+
15
+ export async function handleInitCommand(force: boolean = false): Promise<void> {
16
+ console.log("[Septum] Initializing Septum bounded-context environment (Zero-Config)...\n");
17
+
18
+ const cwd = process.cwd();
19
+ const dbPath = ".septum/septum.db";
20
+
21
+ // 1. Auto-discover project topology and persist to SQLite SSOT
22
+ console.log(" • Analyzing codebase topology and modular boundaries...");
23
+ const db = new SeptumDatabase(dbPath);
24
+ const repo = new SeptumRepository(db.raw);
25
+ const domains = TopologyDetector.discoverAndPersist(repo, cwd);
26
+ const domainNames = Object.keys(domains);
27
+ console.log(` ✓ Discovered and registered ${domainNames.length} domains directly in SQLite (${dbPath}):`);
28
+ for (const name of domainNames) {
29
+ console.log(` - ${name}: root -> '${domains[name].root}'`);
30
+ }
31
+ db.close();
32
+
33
+ // 2. Ensure .gitignore excludes local SQLite cache
34
+ ensureGitIgnore(cwd);
35
+
36
+ // 3. Install Git Pre-Commit Hook
37
+ installGitHook(cwd);
38
+
39
+ // 4. Scaffold Agent Customizations (.agents/skills, .agents/hooks, .agents/rules)
40
+ scaffoldAgentCustomizations(cwd);
41
+
42
+ // 5. Run initial codebase ingestion
43
+ console.log("\n[Septum] Running initial ingestion to build deterministic structural catalog...");
44
+ try {
45
+ await handleIngestCommand();
46
+ console.log("\n✓ Septum successfully initialized and ready for production use!");
47
+ console.log(" • Pre-commit hook is active (runs 'septum check --staged' before each commit).");
48
+ console.log(" • AI Agent skill '/septum-map' and boundary hooks installed in '.agents/'.");
49
+ console.log(" • MCP server is ready for AI coding agents via 'septum serve'.\n");
50
+ } catch (err: unknown) {
51
+ const message = err instanceof Error ? err.message : String(err);
52
+ console.warn(`\n[Septum Warning] Initial ingestion encountered an error: ${message}`);
53
+ console.warn("Run 'septum ingest' manually to retry.");
54
+ }
55
+ }
56
+
57
+ function ensureGitIgnore(cwd: string): void {
58
+ const gitignorePath = path.join(cwd, ".gitignore");
59
+ const entriesToEnsure = [".septum/*.db*", ".septum/"];
60
+
61
+ if (fs.existsSync(gitignorePath)) {
62
+ let content = fs.readFileSync(gitignorePath, "utf-8");
63
+ const missing = entriesToEnsure.filter((entry) => !content.includes(entry));
64
+
65
+ if (missing.length > 0) {
66
+ const addition = `\n# Septum local database and cache\n${missing.join("\n")}\n`;
67
+ fs.appendFileSync(gitignorePath, addition, "utf-8");
68
+ console.log(" ✓ Updated .gitignore to exclude local SQLite cache (.septum/).");
69
+ } else {
70
+ console.log(" ✓ .gitignore already configured for Septum.");
71
+ }
72
+ } else {
73
+ fs.writeFileSync(
74
+ gitignorePath,
75
+ `# Septum local database and cache\n${entriesToEnsure.join("\n")}\n`,
76
+ "utf-8"
77
+ );
78
+ console.log(" ✓ Created .gitignore excluding local SQLite cache (.septum/).");
79
+ }
80
+ }
81
+
82
+ function installGitHook(cwd: string): void {
83
+ const gitDir = path.join(cwd, ".git");
84
+ if (!fs.existsSync(gitDir)) {
85
+ console.log(" ⚠ No .git directory found. Skipping pre-commit hook installation.");
86
+ return;
87
+ }
88
+
89
+ const huskyDir = path.join(cwd, ".husky");
90
+ if (fs.existsSync(huskyDir)) {
91
+ const huskyPreCommit = path.join(huskyDir, "pre-commit");
92
+ const hookCommand = "[ -f ./bin/septum.ts ] && bun run ./bin/septum.ts check --staged || command -v septum >/dev/null 2>&1 && septum check --staged || [ -f ./node_modules/.bin/septum ] && ./node_modules/.bin/septum check --staged || bunx septum check --staged";
93
+ if (fs.existsSync(huskyPreCommit)) {
94
+ const existing = fs.readFileSync(huskyPreCommit, "utf-8");
95
+ if (!existing.includes("septum check")) {
96
+ fs.appendFileSync(huskyPreCommit, `\n${hookCommand}\n`, "utf-8");
97
+ }
98
+ } else {
99
+ fs.writeFileSync(huskyPreCommit, `#!/usr/bin/env sh\n${hookCommand}\n`, "utf-8");
100
+ try {
101
+ fs.chmodSync(huskyPreCommit, 0o755);
102
+ } catch (err: unknown) {
103
+ const message = err instanceof Error ? err.message : String(err);
104
+ console.warn(` Notice: Could not set executable permission on '${huskyPreCommit}': ${message}`);
105
+ }
106
+ }
107
+ console.log(" ✓ Installed pre-commit hook in .husky/pre-commit.");
108
+ return;
109
+ }
110
+
111
+ const hooksDir = path.join(gitDir, "hooks");
112
+ if (!fs.existsSync(hooksDir)) {
113
+ fs.mkdirSync(hooksDir, { recursive: true });
114
+ }
115
+
116
+ const preCommitHook = path.join(hooksDir, "pre-commit");
117
+ const hookScript = `#!/usr/bin/env sh
118
+ # Septum deterministic boundary pre-commit hook
119
+ echo "[Septum Hook] Auditing staged changes against domain boundaries..."
120
+
121
+ if command -v bun >/dev/null 2>&1 && [ -f "./bin/septum.ts" ]; then
122
+ bun run ./bin/septum.ts check --staged
123
+ elif command -v septum >/dev/null 2>&1; then
124
+ septum check --staged
125
+ elif [ -f "./node_modules/.bin/septum" ]; then
126
+ ./node_modules/.bin/septum check --staged
127
+ elif command -v bunx >/dev/null 2>&1; then
128
+ bunx septum check --staged
129
+ else
130
+ echo "[Septum Hook] Notice: Septum executable not found. Skipping boundary check."
131
+ fi
132
+ `;
133
+
134
+ fs.writeFileSync(preCommitHook, hookScript, "utf-8");
135
+ try {
136
+ fs.chmodSync(preCommitHook, 0o755);
137
+ } catch (err: unknown) {
138
+ const message = err instanceof Error ? err.message : String(err);
139
+ console.warn(` Notice: Could not set executable permission on '${preCommitHook}': ${message}`);
140
+ }
141
+ console.log(" ✓ Installed executable git pre-commit hook at '.git/hooks/pre-commit'.");
142
+ }
143
+
144
+ function scaffoldAgentCustomizations(cwd: string): void {
145
+ const agentsDir = path.join(cwd, ".agents");
146
+
147
+ // 1. Skill: .agents/skills/septum-map/SKILL.md
148
+ const skillDir = path.join(agentsDir, "skills", "septum-map");
149
+ if (!fs.existsSync(skillDir)) {
150
+ fs.mkdirSync(skillDir, { recursive: true });
151
+ }
152
+ const skillPath = path.join(skillDir, "SKILL.md");
153
+ fs.writeFileSync(skillPath, SEPTUM_MAP_SKILL_TEMPLATE, "utf-8");
154
+ console.log(" ✓ Deployed AI Agent skill: '.agents/skills/septum-map/SKILL.md'.");
155
+
156
+ // 2. Rule: .agents/rules/septum-boundary.md
157
+ const rulesDir = path.join(agentsDir, "rules");
158
+ if (!fs.existsSync(rulesDir)) {
159
+ fs.mkdirSync(rulesDir, { recursive: true });
160
+ }
161
+ const rulePath = path.join(rulesDir, "septum-boundary.md");
162
+ fs.writeFileSync(rulePath, SEPTUM_RULES_TEMPLATE, "utf-8");
163
+ console.log(" ✓ Deployed AI Agent boundary rules: '.agents/rules/septum-boundary.md'.");
164
+
165
+ // 3. Hook Script: .agents/hooks/septum-pre-write.sh
166
+ const hooksDir = path.join(agentsDir, "hooks");
167
+ if (!fs.existsSync(hooksDir)) {
168
+ fs.mkdirSync(hooksDir, { recursive: true });
169
+ }
170
+ const preWriteHook = path.join(hooksDir, "septum-pre-write.sh");
171
+ fs.writeFileSync(preWriteHook, SEPTUM_PRE_WRITE_SCRIPT, "utf-8");
172
+ try {
173
+ fs.chmodSync(preWriteHook, 0o755);
174
+ } catch (err: unknown) {
175
+ const message = err instanceof Error ? err.message : String(err);
176
+ console.warn(` Notice: Could not set executable permission on '${preWriteHook}': ${message}`);
177
+ }
178
+ console.log(" ✓ Deployed pre-write hook script: '.agents/hooks/septum-pre-write.sh'.");
179
+
180
+ // 3b. Post-Write Hook Script: .agents/hooks/septum-post-write.sh
181
+ const postWriteHook = path.join(hooksDir, "septum-post-write.sh");
182
+ fs.writeFileSync(postWriteHook, SEPTUM_POST_WRITE_SCRIPT, "utf-8");
183
+ try {
184
+ fs.chmodSync(postWriteHook, 0o755);
185
+ } catch (err: unknown) {
186
+ const message = err instanceof Error ? err.message : String(err);
187
+ console.warn(` Notice: Could not set executable permission on '${postWriteHook}': ${message}`);
188
+ }
189
+ console.log(" ✓ Deployed post-write hook script: '.agents/hooks/septum-post-write.sh'.");
190
+
191
+ // 4. Hooks Config: .agents/hooks.json
192
+ const hooksJsonPath = path.join(agentsDir, "hooks.json");
193
+ let existingHooks: Record<string, unknown> = {};
194
+
195
+ if (fs.existsSync(hooksJsonPath)) {
196
+ try {
197
+ const raw = fs.readFileSync(hooksJsonPath, "utf-8");
198
+ existingHooks = JSON.parse(raw);
199
+ } catch {
200
+ // Malformed JSON fallback
201
+ }
202
+ }
203
+
204
+ const mergedHooks = {
205
+ ...existingHooks,
206
+ ...SEPTUM_HOOKS_CONFIG,
207
+ };
208
+
209
+ fs.writeFileSync(hooksJsonPath, JSON.stringify(mergedHooks, null, 2) + "\n", "utf-8");
210
+ console.log(" ✓ Configured AI Agent lifecycle hooks: '.agents/hooks.json'.");
211
+ }
@@ -1,61 +1,61 @@
1
- import { ConfigLoader } from "../../core/config/loader.ts";
2
- import { SeptumDatabase } from "../../core/database/client.ts";
3
- import { SeptumRepository } from "../../core/database/repository.ts";
4
- import { SymbolLocator } from "../../core/resolver/symbol-locator.ts";
5
-
6
- export async function handleLocateCommand(
7
- query: string,
8
- options?: { domain?: string; json?: boolean }
9
- ): Promise<void> {
10
- const config = ConfigLoader.load();
11
- const db = new SeptumDatabase(config.settings.db_path);
12
- const repo = new SeptumRepository(db.raw);
13
-
14
- try {
15
- const locator = new SymbolLocator(repo);
16
- const result = locator.locate(query, options?.domain);
17
-
18
- if (options?.json) {
19
- console.log(JSON.stringify(result, null, 2));
20
- return;
21
- }
22
-
23
- if (result.found) {
24
- console.log(`\n✅ Symbol Found:`);
25
- console.log(` File: ${result.file_path}`);
26
- if (result.exact_symbol) {
27
- console.log(` Symbol: ${result.exact_symbol.name} [${result.exact_symbol.kind}]`);
28
- console.log(` Signature: ${result.exact_symbol.signature}`);
29
- console.log(` Lines: ${result.exact_symbol.line_start} - ${result.exact_symbol.line_end}`);
30
- }
31
- if (result.sibling_methods && result.sibling_methods.length > 0) {
32
- console.log(`\n Available Methods in Container:`);
33
- console.log(` ${result.sibling_methods.join(", ")}`);
34
- }
35
- } else {
36
- console.log(`\n❌ Diagnostic Result:`);
37
- if (result.file_path) {
38
- console.log(` File: ${result.file_path}`);
39
- console.log(` Container: ${result.container_name}`);
40
- }
41
- console.log(` Message: ${result.message}`);
42
-
43
- if (result.sibling_methods && result.sibling_methods.length > 0) {
44
- console.log(`\n Available Methods:`);
45
- console.log(` ${result.sibling_methods.join(", ")}`);
46
- }
47
-
48
- if (result.suggestions.length > 0) {
49
- console.log(`\n Suggestions (Ranked by Match):`);
50
- for (const sug of result.suggestions) {
51
- const matchPercent = Math.round(sug.similarity_score * 100);
52
- console.log(
53
- ` • ${sug.name} (${matchPercent}% match) [line ${sug.line_start}] -> ${sug.signature}`
54
- );
55
- }
56
- }
57
- }
58
- } finally {
59
- db.close();
60
- }
61
- }
1
+ import { ConfigLoader } from "../../core/config/loader.ts";
2
+ import { SeptumDatabase } from "../../core/database/client.ts";
3
+ import { SeptumRepository } from "../../core/database/repository.ts";
4
+ import { SymbolLocator } from "../../core/resolver/symbol-locator.ts";
5
+
6
+ export async function handleLocateCommand(
7
+ query: string,
8
+ options?: { domain?: string; json?: boolean }
9
+ ): Promise<void> {
10
+ const config = ConfigLoader.load();
11
+ const db = new SeptumDatabase(config.settings.db_path);
12
+ const repo = new SeptumRepository(db.raw);
13
+
14
+ try {
15
+ const locator = new SymbolLocator(repo);
16
+ const result = locator.locate(query, options?.domain);
17
+
18
+ if (options?.json) {
19
+ console.log(JSON.stringify(result, null, 2));
20
+ return;
21
+ }
22
+
23
+ if (result.found) {
24
+ console.log(`\n✅ Symbol Found:`);
25
+ console.log(` File: ${result.file_path}`);
26
+ if (result.exact_symbol) {
27
+ console.log(` Symbol: ${result.exact_symbol.name} [${result.exact_symbol.kind}]`);
28
+ console.log(` Signature: ${result.exact_symbol.signature}`);
29
+ console.log(` Lines: ${result.exact_symbol.line_start} - ${result.exact_symbol.line_end}`);
30
+ }
31
+ if (result.sibling_methods && result.sibling_methods.length > 0) {
32
+ console.log(`\n Available Methods in Container:`);
33
+ console.log(` ${result.sibling_methods.join(", ")}`);
34
+ }
35
+ } else {
36
+ console.log(`\n❌ Diagnostic Result:`);
37
+ if (result.file_path) {
38
+ console.log(` File: ${result.file_path}`);
39
+ console.log(` Container: ${result.container_name}`);
40
+ }
41
+ console.log(` Message: ${result.message}`);
42
+
43
+ if (result.sibling_methods && result.sibling_methods.length > 0) {
44
+ console.log(`\n Available Methods:`);
45
+ console.log(` ${result.sibling_methods.join(", ")}`);
46
+ }
47
+
48
+ if (result.suggestions.length > 0) {
49
+ console.log(`\n Suggestions (Ranked by Match):`);
50
+ for (const sug of result.suggestions) {
51
+ const matchPercent = Math.round(sug.similarity_score * 100);
52
+ console.log(
53
+ ` • ${sug.name} (${matchPercent}% match) [line ${sug.line_start}] -> ${sug.signature}`
54
+ );
55
+ }
56
+ }
57
+ }
58
+ } finally {
59
+ db.close();
60
+ }
61
+ }