@shahmilsaari/memory-core 1.0.22 → 1.0.26

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.
@@ -1,22 +1,27 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ detectProject,
4
+ embed,
5
+ inferProjectArchitectures,
6
+ startWatch
7
+ } from "./chunk-3XTHE74V.js";
8
+ import {
9
+ getChatProviderLabel
10
+ } from "./chunk-PQBWHAZN.js";
2
11
  import {
3
12
  Config,
4
13
  closePool,
5
14
  deleteMemory,
6
- detectProject,
7
- embed,
8
- getChatProviderLabel,
9
15
  getPool,
10
- inferProjectArchitectures,
11
16
  listMemories,
12
17
  saveMemory,
13
- startWatch,
14
18
  updateMemory
15
- } from "./chunk-UZDALJVQ.js";
19
+ } from "./chunk-M7NKSXFS.js";
20
+ import "./chunk-ZZBQEXEO.js";
16
21
 
17
22
  // src/dashboard-server.ts
18
23
  import { createHash } from "crypto";
19
- import { createReadStream, existsSync, readFileSync, watch } from "fs";
24
+ import { createReadStream, existsSync, mkdirSync, readFileSync, watch, writeFileSync } from "fs";
20
25
  import { createServer } from "http";
21
26
  import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
22
27
  import { fileURLToPath } from "url";
@@ -47,6 +52,63 @@ var snapshotBroadcastInFlight = false;
47
52
  var snapshotBroadcastQueued = false;
48
53
  var snapshotBroadcastForceRefresh = false;
49
54
  var projectRoot = process.cwd();
55
+ var archState = {
56
+ pendingRules: 0,
57
+ archRuleCount: 0,
58
+ layers: [],
59
+ rules: [],
60
+ lastCheckResult: null
61
+ };
62
+ var HISTORY_MAX = 200;
63
+ function historyPath() {
64
+ return join(projectRoot, ".archmind", "violation-history.json");
65
+ }
66
+ function readViolationHistory() {
67
+ const path = historyPath();
68
+ if (!existsSync(path)) return [];
69
+ try {
70
+ return JSON.parse(readFileSync(path, "utf-8"));
71
+ } catch {
72
+ return [];
73
+ }
74
+ }
75
+ function appendViolationHistory(file, violations, timestamp) {
76
+ try {
77
+ const archmindDir = join(projectRoot, ".archmind");
78
+ mkdirSync(archmindDir, { recursive: true });
79
+ const history = readViolationHistory();
80
+ history.unshift({ timestamp, file, violations });
81
+ if (history.length > HISTORY_MAX) history.length = HISTORY_MAX;
82
+ writeFileSync(historyPath(), JSON.stringify(history, null, 2), "utf-8");
83
+ } catch {
84
+ }
85
+ }
86
+ function readArchState() {
87
+ const archmindDir = join(projectRoot, ".archmind");
88
+ const queuePath = join(archmindDir, "approval-queue.json");
89
+ const rulesPath = join(archmindDir, "rules.json");
90
+ const layersPath = join(archmindDir, "layers.json");
91
+ try {
92
+ const queue = readJsonFile(queuePath, {});
93
+ archState.pendingRules = Array.isArray(queue.items) ? queue.items.length : 0;
94
+ } catch {
95
+ archState.pendingRules = 0;
96
+ }
97
+ try {
98
+ const config = readJsonFile(rulesPath, {});
99
+ archState.rules = Array.isArray(config.rules) ? config.rules : [];
100
+ archState.archRuleCount = archState.rules.length;
101
+ } catch {
102
+ archState.rules = [];
103
+ archState.archRuleCount = 0;
104
+ }
105
+ try {
106
+ const config = readJsonFile(layersPath, {});
107
+ archState.layers = Array.isArray(config.layers) ? config.layers : [];
108
+ } catch {
109
+ archState.layers = [];
110
+ }
111
+ }
50
112
  function resolveDashboardProjectRoot(inputPath) {
51
113
  const candidate = (inputPath ?? process.cwd()).trim();
52
114
  if (candidate.endsWith(".memory-core.json")) {
@@ -85,7 +147,7 @@ function resolveDeclaredArchitectures(config, detectedFramework) {
85
147
  const declaredArchitectures = [...new Set([
86
148
  backendArchitecture,
87
149
  frontendFramework
88
- ].filter((value) => Boolean(value)))];
150
+ ].filter((value) => Boolean(value) && value !== "custom"))];
89
151
  if (declaredArchitectures.length > 0) {
90
152
  return { backendArchitecture, frontendFramework, declaredArchitectures };
91
153
  }
@@ -145,8 +207,7 @@ function parseEnvFile(raw) {
145
207
  return values;
146
208
  }
147
209
  function getRuntimeEnvPath() {
148
- const memoryEnv = join(projectRoot, ".memory-core.env");
149
- return existsSync(memoryEnv) ? memoryEnv : join(projectRoot, ".env");
210
+ return join(projectRoot, ".memory-core.env");
150
211
  }
151
212
  function reloadRuntimeEnv() {
152
213
  const envPath = getRuntimeEnvPath();
@@ -179,9 +240,20 @@ function buildStatsPayload() {
179
240
  files,
180
241
  topRules: topEntries(rules),
181
242
  topFiles: topEntries(files),
182
- recentViolations: stats.recentViolations ?? []
243
+ recentViolations: stats.recentViolations ?? [],
244
+ tokens: stats.tokens ?? { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },
245
+ bypasses: stats.bypasses ?? { total: 0, withReason: 0, withoutReason: 0 }
183
246
  };
184
247
  }
248
+ function broadcastStats() {
249
+ const payload = JSON.stringify({ type: "stats", stats: buildStatsPayload(), timestamp: (/* @__PURE__ */ new Date()).toISOString() });
250
+ for (const client of clients) {
251
+ try {
252
+ client.write(payload);
253
+ } catch {
254
+ }
255
+ }
256
+ }
185
257
  function redactDatabaseUrl(url) {
186
258
  if (!url) return "(not set)";
187
259
  return url.replace(/:\/\/([^:@/]+)(?::[^@/]+)?@/, "://$1:***@");
@@ -407,6 +479,7 @@ async function getSnapshotBase(forceRefresh = false) {
407
479
  }
408
480
  async function getSnapshot(options = {}) {
409
481
  const base = await getSnapshotBase(options.forceBaseRefresh ?? false);
482
+ readArchState();
410
483
  return {
411
484
  config: base.config,
412
485
  runtime: base.runtime,
@@ -416,7 +489,9 @@ async function getSnapshot(options = {}) {
416
489
  watcher: watcherStatus,
417
490
  memories: base.memories,
418
491
  memoryCount: base.memoryCount,
419
- dbError: base.dbError
492
+ dbError: base.dbError,
493
+ arch: { ...archState },
494
+ violationHistory: readViolationHistory().slice(0, 50)
420
495
  };
421
496
  }
422
497
  function sendJson(res, status, payload) {
@@ -528,6 +603,53 @@ async function handleApi(req, res, url) {
528
603
  scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
529
604
  return;
530
605
  }
606
+ if (req.method === "POST" && url.pathname === "/api/config/model") {
607
+ const body = await readBody(req);
608
+ const provider = typeof body.provider === "string" && body.provider.trim() ? body.provider.trim() : null;
609
+ const model = typeof body.model === "string" && body.model.trim() ? body.model.trim() : null;
610
+ if (!provider && !model) {
611
+ sendJson(res, 400, { error: "provider or model is required" });
612
+ return;
613
+ }
614
+ const envPath = join(projectRoot, ".memory-core.env");
615
+ const existing = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
616
+ let lines = existing.split("\n").filter((l) => l.trim());
617
+ if (provider) {
618
+ lines = lines.filter((l) => !l.startsWith("CHAT_PROVIDER="));
619
+ lines.push(`CHAT_PROVIDER=${provider}`);
620
+ }
621
+ if (model) {
622
+ lines = lines.filter((l) => !l.startsWith("CHAT_MODEL=") && !l.startsWith("OLLAMA_CHAT_MODEL="));
623
+ lines.push(`CHAT_MODEL=${model}`);
624
+ }
625
+ writeFileSync(envPath, lines.join("\n") + "\n", "utf-8");
626
+ reloadRuntimeEnv();
627
+ invalidateSnapshotBase();
628
+ scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
629
+ sendJson(res, 200, { ok: true });
630
+ return;
631
+ }
632
+ if (req.method === "POST" && url.pathname === "/api/actions/sync") {
633
+ const { spawn } = await import("child_process");
634
+ const proc = spawn("memory-core", ["sync", "--no-confirm"], { cwd: projectRoot, detached: true, stdio: "ignore" });
635
+ proc.unref();
636
+ sendJson(res, 200, { ok: true, message: "Sync started" });
637
+ return;
638
+ }
639
+ if (req.method === "POST" && url.pathname === "/api/actions/check-all") {
640
+ const { spawn } = await import("child_process");
641
+ const proc = spawn("memory-core", ["check", "--all"], { cwd: projectRoot, detached: true, stdio: "ignore" });
642
+ proc.unref();
643
+ sendJson(res, 200, { ok: true, message: "Full scan started" });
644
+ return;
645
+ }
646
+ if (req.method === "POST" && url.pathname === "/api/actions/tune") {
647
+ const { spawn } = await import("child_process");
648
+ const proc = spawn("memory-core", ["tune"], { cwd: projectRoot, detached: true, stdio: "ignore" });
649
+ proc.unref();
650
+ sendJson(res, 200, { ok: true, message: "Tune started" });
651
+ return;
652
+ }
531
653
  sendJson(res, 404, { error: "Not found" });
532
654
  } catch (err) {
533
655
  sendJson(res, 500, { error: err.message });
@@ -549,8 +671,14 @@ function contentType(path) {
549
671
  return "application/octet-stream";
550
672
  }
551
673
  }
674
+ function resolveDashboardDir() {
675
+ const distDir = join(projectRoot, "dist", "dashboard");
676
+ if (existsSync(join(distDir, "index.html"))) return distDir;
677
+ const srcDir = join(fileURLToPath(new URL(".", import.meta.url)), "dashboard");
678
+ return srcDir;
679
+ }
552
680
  function serveStatic(req, res, url) {
553
- const dashboardDir = join(fileURLToPath(new URL(".", import.meta.url)), "dashboard");
681
+ const dashboardDir = resolveDashboardDir();
554
682
  const requested = url.pathname === "/" ? "/index.html" : url.pathname;
555
683
  const filePath = normalize(join(dashboardDir, requested));
556
684
  if (!filePath.startsWith(dashboardDir) || relative(dashboardDir, filePath).startsWith("..")) {
@@ -692,6 +820,7 @@ function handleWatchEvent(event) {
692
820
  lastSeen: event.timestamp,
693
821
  violations: event.violations
694
822
  });
823
+ appendViolationHistory(event.file, event.violations, event.timestamp);
695
824
  }
696
825
  if (event.type === "error") {
697
826
  watcherStatus.error = event.message;
@@ -726,18 +855,25 @@ function acceptWebSocket(req, socket) {
726
855
  function startConfigWatch() {
727
856
  let timer;
728
857
  const watchedFiles = [".env", ".memory-core.env", ".memory-core.json", ".memory-core-stats.json"];
858
+ const archmindFiles = ["approval-queue.json", "rules.json"];
859
+ const archmindDir = join(projectRoot, ".archmind");
729
860
  const watchedPaths = /* @__PURE__ */ new Set();
730
861
  const watchers = [];
731
862
  const reload = (filePath) => {
732
863
  if (timer) clearTimeout(timer);
733
864
  timer = setTimeout(async () => {
865
+ const fileName = filePath.split("/").pop() ?? "";
866
+ if (fileName === ".memory-core-stats.json") {
867
+ broadcastStats();
868
+ return;
869
+ }
734
870
  reloadRuntimeEnv();
735
871
  await closePool();
736
872
  invalidateSnapshotBase();
737
873
  recentEvents.push({
738
874
  type: "error",
739
875
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
740
- message: `Configuration reloaded from ${filePath.split("/").pop()}`
876
+ message: `Configuration reloaded from ${fileName}`
741
877
  });
742
878
  if (recentEvents.length > 100) recentEvents.shift();
743
879
  scheduleSnapshotBroadcast({ forceBaseRefresh: true });
@@ -759,6 +895,16 @@ function startConfigWatch() {
759
895
  }
760
896
  reload(filePath);
761
897
  }));
898
+ if (existsSync(archmindDir)) {
899
+ watchers.push(watch(archmindDir, (_eventType, filename) => {
900
+ if (typeof filename !== "string" || !archmindFiles.includes(filename)) return;
901
+ if (timer) clearTimeout(timer);
902
+ timer = setTimeout(() => {
903
+ readArchState();
904
+ scheduleSnapshotBroadcast({ forceBaseRefresh: false });
905
+ }, 150);
906
+ }));
907
+ }
762
908
  return () => {
763
909
  if (timer) clearTimeout(timer);
764
910
  for (const watcher of watchers) watcher.close();
@@ -815,6 +961,7 @@ async function startDashboard(options = {}) {
815
961
  }
816
962
  export {
817
963
  mapFrameworkToArchitecture,
964
+ readViolationHistory,
818
965
  resolveDashboardProjectRoot,
819
966
  resolveDeclaredArchitectures,
820
967
  startDashboard
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ closePool,
4
+ deleteMemories,
5
+ deleteMemory,
6
+ getMemory,
7
+ getPool,
8
+ hashMemoryContent,
9
+ listMemories,
10
+ runMigrations,
11
+ saveMemory,
12
+ searchMemories,
13
+ updateMemory,
14
+ upsertMemory
15
+ } from "./chunk-M7NKSXFS.js";
16
+ export {
17
+ closePool,
18
+ deleteMemories,
19
+ deleteMemory,
20
+ getMemory,
21
+ getPool,
22
+ hashMemoryContent,
23
+ listMemories,
24
+ runMigrations,
25
+ saveMemory,
26
+ searchMemories,
27
+ updateMemory,
28
+ upsertMemory
29
+ };
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ callChatModel
4
+ } from "./chunk-PQBWHAZN.js";
5
+
6
+ // src/models/deepseek-critique.ts
7
+ function loadDeepSeekConfig(_configDir) {
8
+ return {};
9
+ }
10
+ var DeepSeekCritique = class {
11
+ constructor(_config) {
12
+ }
13
+ async critique(packet, primaryDecision) {
14
+ try {
15
+ const result = await callChatModel([
16
+ {
17
+ role: "system",
18
+ content: "You are an expert architecture reviewer. Validate or override model decisions based only on the provided evidence. Return ONLY valid JSON."
19
+ },
20
+ {
21
+ role: "user",
22
+ content: buildCritiquePrompt(packet, primaryDecision)
23
+ }
24
+ ]);
25
+ return parseDecision(result.content, primaryDecision);
26
+ } catch {
27
+ return {
28
+ decision: primaryDecision.decision,
29
+ confidence: primaryDecision.confidence,
30
+ reasoning: "Chat model unavailable for critique \u2014 using primary decision",
31
+ override: false
32
+ };
33
+ }
34
+ }
35
+ };
36
+ function buildCritiquePrompt(packet, primary) {
37
+ return `Validate this architecture review decision.
38
+
39
+ PRIMARY DECISION:
40
+ - Decision: ${primary.decision}
41
+ - Confidence: ${primary.confidence}
42
+ - Reasoning: ${primary.reasoning}
43
+ - Violations cited: ${primary.violations.join(", ") || "none"}
44
+
45
+ EVIDENCE SUMMARY:
46
+ - Changed files: ${packet.changedFiles.map((f) => `${f.file} (${f.layer})`).join(", ") || "none"}
47
+ - Violations: ${packet.violations.map((v) => `${v.rule.name}: ${v.fromFile} \u2192 ${v.toFile}`).join(", ") || "none"}
48
+ - Graph violations: ${packet.graphViolations.map((v) => v.ruleName).join(", ") || "none"}
49
+ - Matched rules: ${packet.matchedRules.map((r) => r.name).join(", ") || "none"}
50
+
51
+ TASK:
52
+ 1. Is the primary decision supported by the evidence?
53
+ 2. Were any violations missed?
54
+ 3. Should the decision be overridden?
55
+
56
+ Return ONLY this JSON, no other text:
57
+ {
58
+ "decision": "allow",
59
+ "confidence": 0.9,
60
+ "reasoning": "",
61
+ "override": false
62
+ }`;
63
+ }
64
+ function parseDecision(raw, fallback) {
65
+ try {
66
+ const match = raw.match(/\{[\s\S]*\}/);
67
+ if (!match) throw new Error("no JSON");
68
+ const parsed = JSON.parse(match[0]);
69
+ return {
70
+ decision: ["allow", "warn", "block"].includes(parsed.decision) ? parsed.decision : fallback.decision,
71
+ confidence: typeof parsed.confidence === "number" ? Math.max(0, Math.min(1, parsed.confidence)) : fallback.confidence,
72
+ reasoning: parsed.reasoning ?? "",
73
+ override: parsed.override ?? false
74
+ };
75
+ } catch {
76
+ return { decision: fallback.decision, confidence: fallback.confidence, reasoning: "Parse error", override: false };
77
+ }
78
+ }
79
+ export {
80
+ DeepSeekCritique,
81
+ loadDeepSeekConfig
82
+ };
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/models/deterministic-validator.ts
4
+ var DeterministicValidator = class {
5
+ validate(packet) {
6
+ if (packet.violations.some((v) => v.rule.severity === "critical")) return "block";
7
+ if (packet.graphViolations.length > 0) return "block";
8
+ const newInRestricted = packet.changedFiles.filter(
9
+ (f) => f.isNew && ["infrastructure", "config"].includes(f.layer)
10
+ );
11
+ if (newInRestricted.length > 0) return "warn";
12
+ if (packet.violations.some((v) => v.rule.severity === "medium")) return "warn";
13
+ return "allow";
14
+ }
15
+ };
16
+ export {
17
+ DeterministicValidator
18
+ };
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/evidence.ts
4
+ import { dirname, resolve } from "path";
5
+ var EvidencePacketBuilder = class {
6
+ constructor(cwd = process.cwd()) {
7
+ this.cwd = cwd;
8
+ }
9
+ cwd;
10
+ async build(diff, astAnalyzer, classifier, ruleMatcher, graph) {
11
+ const changedFiles = classifier.classifyDiff(diff);
12
+ const layersAffected = [...new Set(changedFiles.map((f) => f.layer))];
13
+ const importsMap = [];
14
+ const violations = [];
15
+ for (const file of [...diff.added, ...diff.modified]) {
16
+ const astInfo = astAnalyzer.analyze(file);
17
+ importsMap.push({ file, imports: astInfo.imports });
18
+ const fromClassified = classifier.classifyFile(file);
19
+ if (!fromClassified) continue;
20
+ for (const imp of astInfo.imports) {
21
+ if (imp.isExternal) continue;
22
+ const importedFile = this.resolveImport(file, imp.module);
23
+ const toClassified = classifier.classifyFile(importedFile);
24
+ if (!toClassified) continue;
25
+ const violatingRules = ruleMatcher.findViolatingRules(fromClassified.layer, toClassified.layer);
26
+ for (const rule of violatingRules) {
27
+ violations.push({ rule, fromFile: file, toFile: importedFile, imports: [imp] });
28
+ }
29
+ }
30
+ }
31
+ const graphViolations = graph.findViolations(ruleMatcher.getAllRules());
32
+ const matchedRules = ruleMatcher.getRulesForLayers(layersAffected);
33
+ return {
34
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35
+ changedFiles,
36
+ layersAffected,
37
+ imports: importsMap,
38
+ matchedRules,
39
+ violations,
40
+ graphViolations,
41
+ diffSummary: this.generateDiffSummary(changedFiles),
42
+ metadata: {
43
+ totalFilesChanged: diff.added.length + diff.modified.length,
44
+ newFiles: diff.added.length,
45
+ layerTransitions: violations.length
46
+ }
47
+ };
48
+ }
49
+ generateDiffSummary(files) {
50
+ const layers = [...new Set(files.map((f) => f.layer))];
51
+ const newCount = files.filter((f) => f.isNew).length;
52
+ return `Modified ${files.length} files across ${layers.length} layer(s) (${newCount} new)`;
53
+ }
54
+ resolveImport(fromFile, importPath) {
55
+ if (importPath.startsWith(".")) {
56
+ const abs = resolve(this.cwd, dirname(fromFile), importPath).replace(/\\/g, "/");
57
+ const base = this.cwd.replace(/\\/g, "/").replace(/\/?$/, "/");
58
+ return abs.startsWith(base) ? abs.slice(base.length) : abs;
59
+ }
60
+ return importPath;
61
+ }
62
+ };
63
+ export {
64
+ EvidencePacketBuilder
65
+ };
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/graph.ts
4
+ import { dirname, resolve } from "path";
5
+ var DependencyGraph = class {
6
+ nodes = /* @__PURE__ */ new Map();
7
+ edges = [];
8
+ addNode(file, layer, imports) {
9
+ this.nodes.set(file, { id: file, layer, imports });
10
+ }
11
+ addEdge(from, to) {
12
+ this.edges.push({ from, to, type: "import" });
13
+ }
14
+ validateDependency(from, to, rules) {
15
+ const fromNode = this.nodes.get(from);
16
+ const toNode = this.nodes.get(to);
17
+ if (!fromNode || !toNode) return null;
18
+ for (const rule of rules) {
19
+ const fromMatches = rule.fromLayer === fromNode.layer || rule.fromLayer === "*";
20
+ const toMatches = rule.toLayer === toNode.layer || rule.toLayer === "*";
21
+ if (fromMatches && toMatches && !rule.allowed) {
22
+ return {
23
+ from,
24
+ fromLayer: fromNode.layer,
25
+ to,
26
+ toLayer: toNode.layer,
27
+ ruleName: rule.name,
28
+ path: [from, to]
29
+ };
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ findViolations(rules) {
35
+ const violations = [];
36
+ for (const edge of this.edges) {
37
+ const v = this.validateDependency(edge.from, edge.to, rules);
38
+ if (v) violations.push(v);
39
+ }
40
+ return violations;
41
+ }
42
+ findPath(from, to) {
43
+ const visited = /* @__PURE__ */ new Set();
44
+ const queue = [[from, [from]]];
45
+ while (queue.length > 0) {
46
+ const [current, path] = queue.shift();
47
+ if (current === to) return path;
48
+ if (visited.has(current)) continue;
49
+ visited.add(current);
50
+ const node = this.nodes.get(current);
51
+ if (!node) continue;
52
+ for (const neighbor of node.imports) {
53
+ if (!visited.has(neighbor)) queue.push([neighbor, [...path, neighbor]]);
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ getNodes() {
59
+ return [...this.nodes.values()];
60
+ }
61
+ getEdges() {
62
+ return this.edges;
63
+ }
64
+ };
65
+ var GraphBuilder = class {
66
+ constructor(astAnalyzer, classifier, cwd = process.cwd()) {
67
+ this.astAnalyzer = astAnalyzer;
68
+ this.classifier = classifier;
69
+ this.cwd = cwd;
70
+ }
71
+ astAnalyzer;
72
+ classifier;
73
+ cwd;
74
+ async buildFromFiles(files) {
75
+ const graph = new DependencyGraph();
76
+ for (const file of files) {
77
+ const classified = this.classifier.classifyFile(file);
78
+ if (!classified) continue;
79
+ const astInfo = this.astAnalyzer.analyze(file);
80
+ const resolvedImports = astInfo.imports.filter((imp) => !imp.isExternal).map((imp) => this.resolveImport(file, imp.module));
81
+ graph.addNode(file, classified.layer, resolvedImports);
82
+ for (const resolved of resolvedImports) {
83
+ graph.addEdge(file, resolved);
84
+ }
85
+ }
86
+ return graph;
87
+ }
88
+ resolveImport(fromFile, importPath) {
89
+ if (importPath.startsWith(".")) {
90
+ return resolve(this.cwd, dirname(fromFile), importPath).replace(/\\/g, "/");
91
+ }
92
+ return importPath;
93
+ }
94
+ };
95
+ export {
96
+ DependencyGraph,
97
+ GraphBuilder
98
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/automation/incident-capture.ts
4
+ var IncidentCaptureService = class {
5
+ draftRule(incident) {
6
+ return {
7
+ id: `auto-incident-${Date.now()}`,
8
+ name: `[INCIDENT] ${incident.what.slice(0, 60)}`,
9
+ description: `Root cause: ${incident.why}. Applies to: ${incident.where}`,
10
+ fromLayer: "*",
11
+ toLayer: "*",
12
+ allowed: false,
13
+ severity: "critical",
14
+ enforcement: "block"
15
+ };
16
+ }
17
+ };
18
+ export {
19
+ IncidentCaptureService
20
+ };