@noorm/marie-cli 0.1.18 → 0.1.25
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/README.md +7 -15
- package/SENTINEL.md +4 -7
- package/dist/cli-new/components/App.js +16 -63
- package/dist/cli-new/components/App.js.map +1 -1
- package/dist/cli-new/components/ApprovalDialog.js +2 -1
- package/dist/cli-new/components/ApprovalDialog.js.map +1 -1
- package/dist/cli-new/components/Banner.js +4 -3
- package/dist/cli-new/components/Banner.js.map +1 -1
- package/dist/cli-new/components/ChatArea.js +6 -7
- package/dist/cli-new/components/ChatArea.js.map +1 -1
- package/dist/cli-new/components/Header.js +13 -7
- package/dist/cli-new/components/Header.js.map +1 -1
- package/dist/cli-new/components/InputArea.js +73 -12
- package/dist/cli-new/components/InputArea.js.map +1 -1
- package/dist/cli-new/components/MessageBubble.js +26 -18
- package/dist/cli-new/components/MessageBubble.js.map +1 -1
- package/dist/cli-new/components/SessionSwitcher.js +4 -7
- package/dist/cli-new/components/SessionSwitcher.js.map +1 -1
- package/dist/cli-new/components/SetupWizard.js +80 -257
- package/dist/cli-new/components/SetupWizard.js.map +1 -1
- package/dist/cli-new/components/ToolCallDisplay.js +20 -5
- package/dist/cli-new/components/ToolCallDisplay.js.map +1 -1
- package/dist/cli-new/components/WizardSteps.js +22 -0
- package/dist/cli-new/components/WizardSteps.js.map +1 -0
- package/dist/cli-new/constants/SetupConstants.js +42 -0
- package/dist/cli-new/constants/SetupConstants.js.map +1 -0
- package/dist/cli-new/hooks/useGit.js +19 -62
- package/dist/cli-new/hooks/useGit.js.map +1 -1
- package/dist/cli-new/hooks/useMarie.js +26 -18
- package/dist/cli-new/hooks/useMarie.js.map +1 -1
- package/dist/cli-new/hooks/useSessions.js +1 -1
- package/dist/cli-new/hooks/useSessions.js.map +1 -1
- package/dist/cli-new/hooks/useSetupWizard.js +88 -0
- package/dist/cli-new/hooks/useSetupWizard.js.map +1 -0
- package/dist/cli-new/hooks/useUpdateCheck.js +4 -3
- package/dist/cli-new/hooks/useUpdateCheck.js.map +1 -1
- package/dist/cli-new/index.js +2 -4
- package/dist/cli-new/index.js.map +1 -1
- package/dist/cli-new/services/CommandService.js +104 -0
- package/dist/cli-new/services/CommandService.js.map +1 -0
- package/dist/cli-new/services/GitService.js +91 -0
- package/dist/cli-new/services/GitService.js.map +1 -0
- package/dist/cli-new/services/MarieService.js +77 -0
- package/dist/cli-new/services/MarieService.js.map +1 -0
- package/dist/cli-new/services/auth-server.js +128 -0
- package/dist/cli-new/services/auth-server.js.map +1 -0
- package/dist/cli-new/utils/version.js +24 -0
- package/dist/cli-new/utils/version.js.map +1 -0
- package/dist/monolith/adapters/CliMarieAdapter.js +12 -11
- package/dist/monolith/adapters/CliMarieAdapter.js.map +1 -1
- package/dist/monolith/cli/CliFileSystemPort.js +17 -3
- package/dist/monolith/cli/CliFileSystemPort.js.map +1 -1
- package/dist/monolith/cli/MarieToolDefinitionsCLI.js +39 -31
- package/dist/monolith/cli/MarieToolDefinitionsCLI.js.map +1 -1
- package/dist/monolith/cli/index.js +5 -20
- package/dist/monolith/cli/index.js.map +1 -1
- package/dist/monolith/cli/services/JoyAutomationServiceCLI.js +15 -62
- package/dist/monolith/cli/services/JoyAutomationServiceCLI.js.map +1 -1
- package/dist/monolith/cli/storage.js +142 -72
- package/dist/monolith/cli/storage.js.map +1 -1
- package/dist/monolith/domain/joy/RitualService.js +44 -46
- package/dist/monolith/domain/joy/RitualService.js.map +1 -1
- package/dist/monolith/domain/marie/MarieCortex.js +148 -0
- package/dist/monolith/domain/marie/MarieCortex.js.map +1 -0
- package/dist/monolith/domain/marie/PersonalityRenderer.js +97 -0
- package/dist/monolith/domain/marie/PersonalityRenderer.js.map +1 -0
- package/dist/monolith/infrastructure/Configuration.js +68 -0
- package/dist/monolith/infrastructure/Configuration.js.map +1 -0
- package/dist/monolith/infrastructure/CoreInfrastructure.js +204 -0
- package/dist/monolith/infrastructure/CoreInfrastructure.js.map +1 -0
- package/dist/monolith/infrastructure/ai/agents/MarieAscendant.js +3 -3
- package/dist/monolith/infrastructure/ai/agents/MarieAscendant.js.map +1 -1
- package/dist/monolith/infrastructure/ai/context/ContextArchiveService.js +6 -27
- package/dist/monolith/infrastructure/ai/context/ContextArchiveService.js.map +1 -1
- package/dist/monolith/infrastructure/ai/context/ContextManager.js +142 -131
- package/dist/monolith/infrastructure/ai/context/ContextManager.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieEngine.js +115 -1077
- package/dist/monolith/infrastructure/ai/core/MarieEngine.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieEventDispatcher.js +1 -37
- package/dist/monolith/infrastructure/ai/core/MarieEventDispatcher.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieLockManager.js +6 -1
- package/dist/monolith/infrastructure/ai/core/MarieLockManager.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieProgressTracker.js +172 -221
- package/dist/monolith/infrastructure/ai/core/MarieProgressTracker.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieSanitizer.js +292 -141
- package/dist/monolith/infrastructure/ai/core/MarieSanitizer.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieToolProcessor.js +331 -614
- package/dist/monolith/infrastructure/ai/core/MarieToolProcessor.js.map +1 -1
- package/dist/monolith/infrastructure/ai/core/MarieVitality.js +238 -0
- package/dist/monolith/infrastructure/ai/core/MarieVitality.js.map +1 -0
- package/dist/monolith/infrastructure/ai/core/SessionLogService.js +9 -2
- package/dist/monolith/infrastructure/ai/core/SessionLogService.js.map +1 -1
- package/dist/monolith/infrastructure/ai/providers/AIProvider.js +402 -1
- package/dist/monolith/infrastructure/ai/providers/AIProvider.js.map +1 -1
- package/dist/monolith/infrastructure/ai/providers/DreamBeesProvider.js +114 -0
- package/dist/monolith/infrastructure/ai/providers/DreamBeesProvider.js.map +1 -0
- package/dist/monolith/infrastructure/ai/providers/OpenRouterProvider.js +426 -392
- package/dist/monolith/infrastructure/ai/providers/OpenRouterProvider.js.map +1 -1
- package/dist/monolith/infrastructure/ai/providers/OpenRouterStreamParser.js +235 -241
- package/dist/monolith/infrastructure/ai/providers/OpenRouterStreamParser.js.map +1 -1
- package/dist/monolith/infrastructure/ai/workerAi.js +185 -0
- package/dist/monolith/infrastructure/ai/workerAi.js.map +1 -0
- package/dist/monolith/infrastructure/config/ConfigService.js +216 -503
- package/dist/monolith/infrastructure/config/ConfigService.js.map +1 -1
- package/dist/monolith/infrastructure/joy/CognitiveRituals.js +4 -165
- package/dist/monolith/infrastructure/joy/CognitiveRituals.js.map +1 -1
- package/dist/monolith/infrastructure/joy/JoyTools.js +14 -47
- package/dist/monolith/infrastructure/joy/JoyTools.js.map +1 -1
- package/dist/monolith/infrastructure/persistence/MarieMindAutonomics.js +4 -0
- package/dist/monolith/infrastructure/persistence/MarieMindAutonomics.js.map +1 -0
- package/dist/monolith/infrastructure/persistence/MarieMindEngine.js +11 -0
- package/dist/monolith/infrastructure/persistence/MarieMindEngine.js.map +1 -0
- package/dist/monolith/infrastructure/persistence/NoormmeAutonomics.js +123 -106
- package/dist/monolith/infrastructure/persistence/NoormmeAutonomics.js.map +1 -1
- package/dist/monolith/infrastructure/persistence/NoormmeEngine.js +508 -63
- package/dist/monolith/infrastructure/persistence/NoormmeEngine.js.map +1 -1
- package/dist/monolith/infrastructure/persistence/NoormmeSchema.js +68 -39
- package/dist/monolith/infrastructure/persistence/NoormmeSchema.js.map +1 -1
- package/dist/monolith/infrastructure/persistence/NoormmeSeeder.js +80 -67
- package/dist/monolith/infrastructure/persistence/NoormmeSeeder.js.map +1 -1
- package/dist/monolith/infrastructure/persistence/NoormmeTools.js +122 -75
- package/dist/monolith/infrastructure/persistence/NoormmeTools.js.map +1 -1
- package/dist/monolith/infrastructure/services/MarieMemoryStore.js +133 -134
- package/dist/monolith/infrastructure/services/MarieMemoryStore.js.map +1 -1
- package/dist/monolith/infrastructure/tools/MarieToolDefinitions.js +6 -30
- package/dist/monolith/infrastructure/tools/MarieToolDefinitions.js.map +1 -1
- package/dist/monolith/infrastructure/tools/PureStreamParser.js +68 -80
- package/dist/monolith/infrastructure/tools/PureStreamParser.js.map +1 -1
- package/dist/monolith/infrastructure/tools/SharedToolDefinitions.js +12 -11
- package/dist/monolith/infrastructure/tools/SharedToolDefinitions.js.map +1 -1
- package/dist/monolith/infrastructure/tools/SovereignTools.js +326 -0
- package/dist/monolith/infrastructure/tools/SovereignTools.js.map +1 -0
- package/dist/monolith/infrastructure/tools/ToolRegistry.js +45 -26
- package/dist/monolith/infrastructure/tools/ToolRegistry.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/AnalysisTools.js +39 -153
- package/dist/monolith/infrastructure/tools/definitions/AnalysisTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/AutomationTools.js +31 -46
- package/dist/monolith/infrastructure/tools/definitions/AutomationTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/ContextTools.js +41 -13
- package/dist/monolith/infrastructure/tools/definitions/ContextTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/CoreTools.js +10 -14
- package/dist/monolith/infrastructure/tools/definitions/CoreTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/DiagnosticTools.js +39 -70
- package/dist/monolith/infrastructure/tools/definitions/DiagnosticTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/NavigationTools.js +30 -181
- package/dist/monolith/infrastructure/tools/definitions/NavigationTools.js.map +1 -1
- package/dist/monolith/infrastructure/tools/definitions/PlanningTools.js +12 -9
- package/dist/monolith/infrastructure/tools/definitions/PlanningTools.js.map +1 -1
- package/dist/monolith/plumbing/Plumbing.js +238 -0
- package/dist/monolith/plumbing/Plumbing.js.map +1 -0
- package/dist/monolith/plumbing/PlumbingAnalysis.js +109 -0
- package/dist/monolith/plumbing/PlumbingAnalysis.js.map +1 -0
- package/dist/monolith/plumbing/PlumbingSystem.js +169 -0
- package/dist/monolith/plumbing/PlumbingSystem.js.map +1 -0
- package/dist/monolith/plumbing/analysis/ComplexityService.js +30 -34
- package/dist/monolith/plumbing/analysis/ComplexityService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/DependencyService.js +55 -44
- package/dist/monolith/plumbing/analysis/DependencyService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/DiscoveryService.js +40 -42
- package/dist/monolith/plumbing/analysis/DiscoveryService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/JoyMapService.js +52 -56
- package/dist/monolith/plumbing/analysis/JoyMapService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/LintService.js +118 -118
- package/dist/monolith/plumbing/analysis/LintService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/MarieSentinelService.js +278 -268
- package/dist/monolith/plumbing/analysis/MarieSentinelService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/QualityGuardrailService.js +116 -114
- package/dist/monolith/plumbing/analysis/QualityGuardrailService.js.map +1 -1
- package/dist/monolith/plumbing/analysis/SurgicalMender.js +57 -59
- package/dist/monolith/plumbing/analysis/SurgicalMender.js.map +1 -1
- package/dist/monolith/plumbing/analysis/TestService.js +89 -89
- package/dist/monolith/plumbing/analysis/TestService.js.map +1 -1
- package/dist/monolith/plumbing/filesystem/FileService.js +123 -195
- package/dist/monolith/plumbing/filesystem/FileService.js.map +1 -1
- package/dist/monolith/plumbing/filesystem/PathResolver.js +7 -8
- package/dist/monolith/plumbing/filesystem/PathResolver.js.map +1 -1
- package/dist/monolith/plumbing/git/GitService.js +4 -4
- package/dist/monolith/plumbing/git/GitService.js.map +1 -1
- package/dist/monolith/plumbing/lsp/SymbolService.js +5 -34
- package/dist/monolith/plumbing/lsp/SymbolService.js.map +1 -1
- package/dist/monolith/plumbing/terminal/ProcessRegistry.js +20 -22
- package/dist/monolith/plumbing/terminal/ProcessRegistry.js.map +1 -1
- package/dist/monolith/plumbing/terminal/TerminalService.js +127 -141
- package/dist/monolith/plumbing/terminal/TerminalService.js.map +1 -1
- package/dist/monolith/plumbing/utils/EnvironmentUtils.js +3 -23
- package/dist/monolith/plumbing/utils/EnvironmentUtils.js.map +1 -1
- package/dist/monolith/plumbing/utils/JsonUtils.js +252 -311
- package/dist/monolith/plumbing/utils/JsonUtils.js.map +1 -1
- package/dist/monolith/plumbing/utils/PlumbingCore.js +549 -0
- package/dist/monolith/plumbing/utils/PlumbingCore.js.map +1 -0
- package/dist/monolith/plumbing/utils/PrefixTree.js +61 -114
- package/dist/monolith/plumbing/utils/PrefixTree.js.map +1 -1
- package/dist/monolith/plumbing/utils/StreamTagDetector.js +89 -127
- package/dist/monolith/plumbing/utils/StreamTagDetector.js.map +1 -1
- package/dist/monolith/plumbing/utils/StringUtils.js +87 -89
- package/dist/monolith/plumbing/utils/StringUtils.js.map +1 -1
- package/dist/monolith/runtime/MarieRuntime.js +76 -499
- package/dist/monolith/runtime/MarieRuntime.js.map +1 -1
- package/dist/monolith/runtime/RuntimeAdapterBase.js +1 -1
- package/dist/monolith/runtime/RuntimeAdapterBase.js.map +1 -1
- package/dist/monolith/runtime/providerFactory.js +1 -7
- package/dist/monolith/runtime/providerFactory.js.map +1 -1
- package/dist/monolith/services/HealthService.js +29 -32
- package/dist/monolith/services/HealthService.js.map +1 -1
- package/dist/monolith/services/JoyAutomationService.js +58 -95
- package/dist/monolith/services/JoyAutomationService.js.map +1 -1
- package/dist/monolith/services/MarieAutomationService.js +59 -0
- package/dist/monolith/services/MarieAutomationService.js.map +1 -0
- package/dist/monolith/services/MarieGhostService.js +46 -173
- package/dist/monolith/services/MarieGhostService.js.map +1 -1
- package/dist/monolith/services/MarieServices.js +102 -0
- package/dist/monolith/services/MarieServices.js.map +1 -0
- package/dist/monolith/services/MarieTypes.js +2 -0
- package/dist/monolith/services/MarieTypes.js.map +1 -0
- package/dist/monolith/services/UpdateService.js +47 -49
- package/dist/monolith/services/UpdateService.js.map +1 -1
- package/dist/prompts.js +11 -5
- package/dist/prompts.js.map +1 -1
- package/dist/test_prefix_tree.js +9 -9
- package/dist/test_prefix_tree.js.map +1 -1
- package/package.json +18 -89
- package/run_test.js +5 -0
- package/.marie_visual_verify_1771225696548/progress_bar_check.txt +0 -1
- package/dist/extension.cjs +0 -1155
- package/dist/extension.js +0 -474
- package/dist/extension.js.map +0 -1
- package/dist/monolith/adapters/VscodeMarieAdapter.js +0 -81
- package/dist/monolith/adapters/VscodeMarieAdapter.js.map +0 -1
- package/dist/monolith/infrastructure/ai/core/GhostPort.js +0 -2
- package/dist/monolith/infrastructure/ai/core/GhostPort.js.map +0 -1
- package/dist/monolith/infrastructure/ai/core/VscodeFileSystemPort.js +0 -33
- package/dist/monolith/infrastructure/ai/core/VscodeFileSystemPort.js.map +0 -1
- package/dist/monolith/infrastructure/ai/providers/AnthropicProvider.js +0 -154
- package/dist/monolith/infrastructure/ai/providers/AnthropicProvider.js.map +0 -1
- package/dist/monolith/infrastructure/ai/providers/CerebrasProvider.js +0 -214
- package/dist/monolith/infrastructure/ai/providers/CerebrasProvider.js.map +0 -1
- package/dist/monolith/plumbing/ui/DecorationService.js +0 -54
- package/dist/monolith/plumbing/ui/DecorationService.js.map +0 -1
- package/dist/monolith/services/JoyLogService.js +0 -48
- package/dist/monolith/services/JoyLogService.js.map +0 -1
- package/dist/monolith/services/JoyService.js +0 -209
- package/dist/monolith/services/JoyService.js.map +0 -1
- package/dist/monolith/services/MarieSCMProvider.js +0 -41
- package/dist/monolith/services/MarieSCMProvider.js.map +0 -1
- package/dist/webview-ui/main.css +0 -1
- package/dist/webview-ui/main.js +0 -108
- package/lint_output.txt +0 -705
- package/lint_output_v2.txt +0 -711
- package/test-mind-p6.sqlite +0 -0
- package/test-mind-p6.sqlite-shm +0 -0
- package/test-mind-p6.sqlite-wal +0 -0
|
@@ -1,287 +1,298 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as crypto from "crypto";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { runLint } from "./LintService.js";
|
|
5
|
+
import { analyzeComplexity } from "./ComplexityService.js";
|
|
6
|
+
import { getAutonomyMode } from "../../infrastructure/config/ConfigService.js";
|
|
7
|
+
import { safeStringify } from "../../infrastructure/ai/core/MarieSanitizer.js";
|
|
7
8
|
/**
|
|
8
9
|
* MARIE SENTINEL v3.1: THE GROUNDED GUARDIAN
|
|
9
10
|
* Improved accuracy in resolution, duplication, and graph fidelity.
|
|
10
11
|
*/
|
|
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
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
12
|
+
const ZONES = {
|
|
13
|
+
DOMAIN: "domain",
|
|
14
|
+
INFRASTRUCTURE: "infrastructure",
|
|
15
|
+
PLUMBING: "plumbing",
|
|
16
|
+
};
|
|
17
|
+
const STATE_FILE = ".marie/sentinel_state.json";
|
|
18
|
+
export async function audit(workingDir, specificFile) {
|
|
19
|
+
const allFiles = await getAllFiles(workingDir);
|
|
20
|
+
const targetFiles = specificFile ? [specificFile] : allFiles;
|
|
21
|
+
const zoneViolations = [];
|
|
22
|
+
const circularDependencies = [];
|
|
23
|
+
const leakyAbstractions = [];
|
|
24
|
+
const duplication = [];
|
|
25
|
+
const navigationDrifts = [];
|
|
26
|
+
const toxicFiles = [];
|
|
27
|
+
// 1. Precise Dependency Graph & Semantic Content Maps
|
|
28
|
+
const dependencyGraph = new Map();
|
|
29
|
+
const semanticHashMap = new Map(); // Hash -> FilePath
|
|
30
|
+
// INCREMENTAL SENTINEL: Load state to check mtimes
|
|
31
|
+
const state = await loadState(workingDir);
|
|
32
|
+
const lastMtimes = state.fileHashes || {};
|
|
33
|
+
const newMtimes = {};
|
|
34
|
+
// FAST PATH: Check ascension mode
|
|
35
|
+
const isAscension = getAutonomyMode() === "ascension";
|
|
36
|
+
// CHUNKING: Prevent EMFILE or OOM by processing files in batches
|
|
37
|
+
const BATCH_SIZE = 50;
|
|
38
|
+
for (let i = 0; i < allFiles.length; i += BATCH_SIZE) {
|
|
39
|
+
const batch = allFiles.slice(i, i + BATCH_SIZE);
|
|
40
|
+
await Promise.all(batch.map(async (file) => {
|
|
41
|
+
try {
|
|
42
|
+
const stats = await fs.stat(file);
|
|
43
|
+
const mtime = stats.mtimeMs;
|
|
44
|
+
const relativePath = path.relative(workingDir, file);
|
|
45
|
+
newMtimes[relativePath] = mtime;
|
|
46
|
+
const hasChanged = lastMtimes[relativePath] !== mtime;
|
|
47
|
+
const content = await fs.readFile(file, "utf8");
|
|
48
|
+
// A. Semantic Duplication
|
|
49
|
+
if (!isAscension) {
|
|
50
|
+
const semanticHash = computeSemanticHash(content);
|
|
51
|
+
if (semanticHashMap.has(semanticHash)) {
|
|
52
|
+
const original = semanticHashMap.get(semanticHash);
|
|
53
|
+
if (original !== relativePath) {
|
|
54
|
+
duplication.push(`[Semantic Duplicate] ${relativePath} matches ${original}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
semanticHashMap.set(semanticHash, relativePath);
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
// B. Robust Import Extraction & Resolution
|
|
62
|
+
const rawImports = extractImports(content);
|
|
63
|
+
const resolvedImports = await Promise.all(rawImports.map((imp) => resolveImportDeep(imp, file, workingDir)));
|
|
64
|
+
const validImports = resolvedImports.filter(Boolean);
|
|
65
|
+
dependencyGraph.set(relativePath, validImports);
|
|
66
|
+
// C. Target Analysis
|
|
67
|
+
if (targetFiles.includes(file) && hasChanged) {
|
|
68
|
+
await analyzeFile(file, workingDir, content, validImports, zoneViolations, leakyAbstractions, navigationDrifts, toxicFiles);
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const resolvedImports = await Promise.all(rawImports.map((i) => this.resolveImportDeep(i, file, workingDir)));
|
|
59
|
-
const validImports = resolvedImports.filter(Boolean);
|
|
60
|
-
dependencyGraph.set(relativePath, validImports);
|
|
61
|
-
// C. Target Analysis
|
|
62
|
-
if (targetFiles.includes(file) && hasChanged) {
|
|
63
|
-
await this.analyzeFile(file, workingDir, content, validImports, zoneViolations, leakyAbstractions, navigationDrifts, toxicFiles);
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.warn(`[Sentinel] Failed to analyze file ${file}:`, e);
|
|
64
73
|
}
|
|
65
74
|
}));
|
|
66
|
-
// 2. Cycle Detection (Global)
|
|
67
|
-
// FAST PATH: Skip expensive cycle detection in Ascension mode
|
|
68
|
-
if (!isAscension) {
|
|
69
|
-
const cycles = this.detectCycles(dependencyGraph);
|
|
70
|
-
circularDependencies.push(...cycles);
|
|
71
|
-
}
|
|
72
|
-
// 3. Score Normalization & Ratchet
|
|
73
|
-
const lintErrors = await LintService.runLint(workingDir);
|
|
74
|
-
const entropyScore = zoneViolations.length * 8 + // Weighted higher
|
|
75
|
-
lintErrors.length * 1 +
|
|
76
|
-
circularDependencies.length * 15 + // Structural rot is expensive
|
|
77
|
-
toxicFiles.length * 6 +
|
|
78
|
-
leakyAbstractions.length * 5 +
|
|
79
|
-
duplication.length * 10 +
|
|
80
|
-
navigationDrifts.length * 12; // Ecclesiastical Navigation is mandatory
|
|
81
|
-
const entropyDelta = entropyScore - state.lastEntropy;
|
|
82
|
-
await this.saveState(workingDir, {
|
|
83
|
-
lastEntropy: entropyScore,
|
|
84
|
-
history: [
|
|
85
|
-
...state.history,
|
|
86
|
-
{ date: new Date().toISOString(), entropy: entropyScore },
|
|
87
|
-
].slice(-20),
|
|
88
|
-
fileHashes: newMtimes,
|
|
89
|
-
});
|
|
90
|
-
let stability = "Stable";
|
|
91
|
-
if (entropyScore > 30)
|
|
92
|
-
stability = "Toxic";
|
|
93
|
-
else if (entropyScore > 15)
|
|
94
|
-
stability = "Fragile";
|
|
95
|
-
else if (entropyScore > 7)
|
|
96
|
-
stability = "Fluid";
|
|
97
|
-
const report = {
|
|
98
|
-
timestamp: new Date().toISOString(),
|
|
99
|
-
zoneViolations,
|
|
100
|
-
circularDependencies,
|
|
101
|
-
leakyAbstractions,
|
|
102
|
-
duplication,
|
|
103
|
-
entropyScore,
|
|
104
|
-
entropyDelta,
|
|
105
|
-
stability,
|
|
106
|
-
hotspots: Array.from(new Set([...zoneViolations.map((v) => v.split(" ")[1]), ...toxicFiles])).slice(0, 5),
|
|
107
|
-
quarantineCandidates: toxicFiles,
|
|
108
|
-
navigationDrifts,
|
|
109
|
-
graphDefinition: this.generateMermaidGraph(dependencyGraph, zoneViolations),
|
|
110
|
-
passed: entropyScore < 20 && entropyDelta <= 0,
|
|
111
|
-
};
|
|
112
|
-
await this.writeToSentinelLog(workingDir, report);
|
|
113
|
-
return report;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Computes a "Semantic Hash" by tokenizing the code and stripping noise.
|
|
117
|
-
* This catches duplication even if variables are renamed (shallowly).
|
|
118
|
-
*/
|
|
119
|
-
static computeSemanticHash(content) {
|
|
120
|
-
const tokens = content
|
|
121
|
-
.replace(/\/\/.*$/gm, "") // Strip line comments
|
|
122
|
-
.replace(/\/\*[\s\S]*?\*\//g, "") // Strip block comments
|
|
123
|
-
.replace(/\s+/g, " ") // Normalize whitespace
|
|
124
|
-
.replace(/\b(const|let|var)\s+\w+\b/g, "VAR") // Normalize variable declarations
|
|
125
|
-
.replace(/\bfunction\s+\w+\b/g, "FUNC") // Normalize function names
|
|
126
|
-
.trim();
|
|
127
|
-
return crypto.createHash("sha256").update(tokens).digest("hex");
|
|
128
75
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
path.resolve(baseDir, `${imp}.ts`),
|
|
136
|
-
path.resolve(baseDir, `${imp}.tsx`),
|
|
137
|
-
path.resolve(baseDir, `${imp}.js`),
|
|
138
|
-
path.resolve(baseDir, imp, "index.ts"),
|
|
139
|
-
path.resolve(baseDir, imp, "index.js"),
|
|
140
|
-
];
|
|
141
|
-
for (const p of candidatePaths) {
|
|
142
|
-
try {
|
|
143
|
-
const stats = await fs.stat(p);
|
|
144
|
-
if (stats.isFile()) {
|
|
145
|
-
return path.relative(workingDir, p);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
76
|
+
// 2. Cycle Detection (Global)
|
|
77
|
+
// FAST PATH: Skip expensive cycle detection in Ascension mode
|
|
78
|
+
if (!isAscension) {
|
|
79
|
+
const cycles = detectCycles(dependencyGraph);
|
|
80
|
+
for (const cycle of cycles) {
|
|
81
|
+
circularDependencies.push(cycle);
|
|
151
82
|
}
|
|
152
|
-
return null;
|
|
153
83
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
84
|
+
// 3. Score Normalization & Ratchet
|
|
85
|
+
const lintErrors = await runLint(workingDir);
|
|
86
|
+
const entropyScore = zoneViolations.length * 8 + // Weighted higher
|
|
87
|
+
lintErrors.length * 1 +
|
|
88
|
+
circularDependencies.length * 15 + // Structural rot is expensive
|
|
89
|
+
toxicFiles.length * 6 +
|
|
90
|
+
leakyAbstractions.length * 5 +
|
|
91
|
+
duplication.length * 10 +
|
|
92
|
+
navigationDrifts.length * 12; // Ecclesiastical Navigation is mandatory
|
|
93
|
+
const entropyDelta = entropyScore - state.lastEntropy;
|
|
94
|
+
await saveState(workingDir, {
|
|
95
|
+
lastEntropy: entropyScore,
|
|
96
|
+
history: [
|
|
97
|
+
...state.history,
|
|
98
|
+
{ date: new Date().toISOString(), entropy: entropyScore },
|
|
99
|
+
].slice(-20),
|
|
100
|
+
fileHashes: newMtimes,
|
|
101
|
+
});
|
|
102
|
+
let stability = "Stable";
|
|
103
|
+
if (entropyScore > 30)
|
|
104
|
+
stability = "Toxic";
|
|
105
|
+
else if (entropyScore > 15)
|
|
106
|
+
stability = "Fragile";
|
|
107
|
+
else if (entropyScore > 7)
|
|
108
|
+
stability = "Fluid";
|
|
109
|
+
const report = {
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
zoneViolations,
|
|
112
|
+
circularDependencies,
|
|
113
|
+
leakyAbstractions,
|
|
114
|
+
duplication,
|
|
115
|
+
entropyScore,
|
|
116
|
+
entropyDelta,
|
|
117
|
+
stability,
|
|
118
|
+
hotspots: Array.from(new Set([...zoneViolations.map((v) => v.split(" ")[1]), ...toxicFiles])).slice(0, 5),
|
|
119
|
+
quarantineCandidates: toxicFiles,
|
|
120
|
+
navigationDrifts,
|
|
121
|
+
graphDefinition: generateMermaidGraph(dependencyGraph, zoneViolations),
|
|
122
|
+
passed: entropyScore < 20 && entropyDelta <= 0,
|
|
123
|
+
};
|
|
124
|
+
await writeToSentinelLog(workingDir, report);
|
|
125
|
+
return report;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Computes a "Semantic Hash" by tokenizing the code and stripping noise.
|
|
129
|
+
* This catches duplication even if variables are renamed (shallowly).
|
|
130
|
+
*/
|
|
131
|
+
export function computeSemanticHash(content) {
|
|
132
|
+
const tokens = content
|
|
133
|
+
.replace(/\/\/.*$/gm, "") // Strip line comments
|
|
134
|
+
.replace(/\/\*[\s\S]*?\*\//g, "") // Strip block comments
|
|
135
|
+
.replace(/\s+/g, " ") // Normalize whitespace
|
|
136
|
+
.replace(/\b(const|let|var)\s+\w+\b/g, "VAR") // Normalize variable declarations
|
|
137
|
+
.replace(/\bfunction\s+\w+\b/g, "FUNC") // Normalize function names
|
|
138
|
+
.trim();
|
|
139
|
+
return crypto.createHash("sha256").update(tokens).digest("hex");
|
|
140
|
+
}
|
|
141
|
+
export async function resolveImportDeep(imp, sourceFile, workingDir) {
|
|
142
|
+
if (!imp.startsWith("."))
|
|
143
|
+
return null; // Ignore external for now
|
|
144
|
+
const baseDir = path.dirname(sourceFile);
|
|
145
|
+
const candidatePaths = [
|
|
146
|
+
path.resolve(baseDir, imp),
|
|
147
|
+
path.resolve(baseDir, `${imp}.ts`),
|
|
148
|
+
path.resolve(baseDir, `${imp}.tsx`),
|
|
149
|
+
path.resolve(baseDir, `${imp}.js`),
|
|
150
|
+
path.resolve(baseDir, imp, "index.ts"),
|
|
151
|
+
path.resolve(baseDir, imp, "index.js"),
|
|
152
|
+
];
|
|
153
|
+
for (const p of candidatePaths) {
|
|
154
|
+
try {
|
|
155
|
+
const stats = await fs.stat(p);
|
|
156
|
+
if (stats.isFile()) {
|
|
157
|
+
return path.relative(workingDir, p);
|
|
170
158
|
}
|
|
171
159
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const systemKeywords = /\b(fs|path|os|vscode|express|React|HTMLElement|Buffer|process|window|document)\b/;
|
|
175
|
-
if (systemKeywords.test(content)) {
|
|
176
|
-
leakyAbstractions.push(`[System Leak] ${relativePath} references non-domain types.`);
|
|
177
|
-
}
|
|
160
|
+
catch {
|
|
161
|
+
continue;
|
|
178
162
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
export async function analyzeFile(file, workingDir, content, resolvedImports, zoneViolations, leakyAbstractions, navigationDrifts, toxicFiles) {
|
|
167
|
+
const relativePath = path.relative(workingDir, file);
|
|
168
|
+
const zone = getZone(relativePath);
|
|
169
|
+
if (!zone)
|
|
170
|
+
return;
|
|
171
|
+
// A. Real Zone Verification (based on RESOLVED paths)
|
|
172
|
+
for (const impPath of resolvedImports) {
|
|
173
|
+
const impZone = getZone(impPath);
|
|
174
|
+
if (!impZone)
|
|
175
|
+
continue;
|
|
176
|
+
if (zone === ZONES.DOMAIN && impZone !== ZONES.DOMAIN) {
|
|
177
|
+
zoneViolations.push(`[Purity Breach] ${relativePath} -> ${impPath} (${impZone})`);
|
|
183
178
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const flatRouteRegex = /path=['"]\/(blog|docs|legal|support)\/:slug['"]/g;
|
|
187
|
-
if (flatRouteRegex.test(content)) {
|
|
188
|
-
navigationDrifts.push(`[Ecclesiastical Violation] ${relativePath} uses flat routes. Use hierarchical schema (e.g., /blog/:year/:month/:slug).`);
|
|
189
|
-
}
|
|
190
|
-
// Secondary check for Breadcrumb presence in content views
|
|
191
|
-
if (content.includes("ArticleView") && !content.includes("Breadcrumbs")) {
|
|
192
|
-
navigationDrifts.push(`[Liturgical Violation] ${relativePath} is a content view missing mandatory Breadcrumbs.`);
|
|
193
|
-
}
|
|
179
|
+
if (zone === ZONES.INFRASTRUCTURE && impZone === ZONES.PLUMBING) {
|
|
180
|
+
zoneViolations.push(`[Architecture Leak] ${relativePath} -> ${impPath} (Infrastructure cannot use Plumbing)`);
|
|
194
181
|
}
|
|
195
182
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
return this.ZONES.INFRASTRUCTURE;
|
|
202
|
-
if (filePath.includes("src/plumbing") || filePath.includes("/plumbing/"))
|
|
203
|
-
return this.ZONES.PLUMBING;
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
static extractImports(content) {
|
|
207
|
-
const imports = [];
|
|
208
|
-
const importRegex = /from\s+['"](.*?)['"]/g;
|
|
209
|
-
const requireRegex = /require\(['"](.*?)['"]\)/g;
|
|
210
|
-
let match;
|
|
211
|
-
while ((match = importRegex.exec(content)) !== null)
|
|
212
|
-
imports.push(match[1]);
|
|
213
|
-
while ((match = requireRegex.exec(content)) !== null)
|
|
214
|
-
imports.push(match[1]);
|
|
215
|
-
return Array.from(new Set(imports));
|
|
216
|
-
}
|
|
217
|
-
static detectCycles(graph) {
|
|
218
|
-
const cycles = [];
|
|
219
|
-
const visited = new Set();
|
|
220
|
-
const stack = new Set();
|
|
221
|
-
const dfs = (node, path) => {
|
|
222
|
-
visited.add(node);
|
|
223
|
-
stack.add(node);
|
|
224
|
-
for (const neighbor of graph.get(node) || []) {
|
|
225
|
-
if (stack.has(neighbor)) {
|
|
226
|
-
cycles.push(`🔄 ${path.join(" -> ")} -> ${neighbor}`);
|
|
227
|
-
}
|
|
228
|
-
else if (!visited.has(neighbor)) {
|
|
229
|
-
dfs(neighbor, [...path, neighbor]);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
stack.delete(node);
|
|
233
|
-
};
|
|
234
|
-
for (const node of graph.keys()) {
|
|
235
|
-
if (!visited.has(node))
|
|
236
|
-
dfs(node, [node]);
|
|
183
|
+
// B. Purity Scan (System Types in Domain)
|
|
184
|
+
if (zone === ZONES.DOMAIN) {
|
|
185
|
+
const systemKeywords = /\b(fs|path|os|express|React|HTMLElement|Buffer|process|window|document)\b/;
|
|
186
|
+
if (systemKeywords.test(content)) {
|
|
187
|
+
leakyAbstractions.push(`[System Leak] ${relativePath} references non-domain types.`);
|
|
237
188
|
}
|
|
238
|
-
return cycles;
|
|
239
189
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const id = node.replace(/[^a-zA-Z0-9]/g, "_");
|
|
245
|
-
const name = path.basename(node);
|
|
246
|
-
graph.get(node)?.forEach((dep) => {
|
|
247
|
-
const depId = dep.replace(/[^a-zA-Z0-9]/g, "_");
|
|
248
|
-
const isViolated = violations.some((v) => v.includes(node) && v.includes(dep));
|
|
249
|
-
mermaid += ` ${id}[${name}] --> ${depId}[${path.basename(dep)}];\n`;
|
|
250
|
-
if (isViolated)
|
|
251
|
-
mermaid += ` style ${id} fill:#f96,stroke:#333,stroke-width:2px\n`;
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
return mermaid;
|
|
190
|
+
// C. Complexity Guard
|
|
191
|
+
const metrics = await analyzeComplexity(file);
|
|
192
|
+
if (metrics.cyclomaticComplexity > 20 || metrics.clutterLevel === "Toxic") {
|
|
193
|
+
toxicFiles.push(relativePath);
|
|
255
194
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
195
|
+
// D. Ecclesiastical Navigation Check (Webapp/UI Files)
|
|
196
|
+
if (relativePath.includes(".tsx") || content.includes("react-router-dom")) {
|
|
197
|
+
const flatRouteRegex = /path=['"]\/(blog|docs|legal|support)\/:slug['"]/g;
|
|
198
|
+
if (flatRouteRegex.test(content)) {
|
|
199
|
+
navigationDrifts.push(`[Ecclesiastical Violation] ${relativePath} uses flat routes. Use hierarchical schema (e.g., /blog/:year/:month/:slug).`);
|
|
260
200
|
}
|
|
261
|
-
|
|
262
|
-
|
|
201
|
+
// Secondary check for Breadcrumb presence in content views
|
|
202
|
+
if (content.includes("ArticleView") && !content.includes("Breadcrumbs")) {
|
|
203
|
+
navigationDrifts.push(`[Liturgical Violation] ${relativePath} is a content view missing mandatory Breadcrumbs.`);
|
|
263
204
|
}
|
|
264
205
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
206
|
+
}
|
|
207
|
+
export function getZone(filePath) {
|
|
208
|
+
if (filePath.includes("src/domain") || filePath.includes("/domain/"))
|
|
209
|
+
return ZONES.DOMAIN;
|
|
210
|
+
if (filePath.includes("src/infrastructure") ||
|
|
211
|
+
filePath.includes("/infrastructure/"))
|
|
212
|
+
return ZONES.INFRASTRUCTURE;
|
|
213
|
+
if (filePath.includes("src/plumbing") || filePath.includes("/plumbing/"))
|
|
214
|
+
return ZONES.PLUMBING;
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
export function extractImports(content) {
|
|
218
|
+
const imports = [];
|
|
219
|
+
const importRegex = /from\s+['"](.*?)['"]/g;
|
|
220
|
+
const requireRegex = /require\(['"](.*?)['"]\)/g;
|
|
221
|
+
let match;
|
|
222
|
+
while ((match = importRegex.exec(content)) !== null)
|
|
223
|
+
imports.push(match[1]);
|
|
224
|
+
while ((match = requireRegex.exec(content)) !== null)
|
|
225
|
+
imports.push(match[1]);
|
|
226
|
+
return Array.from(new Set(imports));
|
|
227
|
+
}
|
|
228
|
+
export function detectCycles(graph) {
|
|
229
|
+
const cycles = [];
|
|
230
|
+
const visited = new Set();
|
|
231
|
+
const stack = new Set();
|
|
232
|
+
const dfs = (node, path) => {
|
|
233
|
+
visited.add(node);
|
|
234
|
+
stack.add(node);
|
|
235
|
+
for (const neighbor of graph.get(node) || []) {
|
|
236
|
+
if (stack.has(neighbor)) {
|
|
237
|
+
cycles.push(`🔄 ${path.join(" -> ")} -> ${neighbor}`);
|
|
238
|
+
}
|
|
239
|
+
else if (!visited.has(neighbor)) {
|
|
240
|
+
dfs(neighbor, [...path, neighbor]);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
stack.delete(node);
|
|
244
|
+
};
|
|
245
|
+
for (const node of graph.keys()) {
|
|
246
|
+
if (!visited.has(node))
|
|
247
|
+
dfs(node, [node]);
|
|
269
248
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
249
|
+
return cycles;
|
|
250
|
+
}
|
|
251
|
+
export function generateMermaidGraph(graph, violations) {
|
|
252
|
+
let mermaid = "graph TD;\n";
|
|
253
|
+
const nodes = Array.from(graph.keys()).slice(0, 40);
|
|
254
|
+
nodes.forEach((node) => {
|
|
255
|
+
const id = node.replace(/[^a-zA-Z0-9]/g, "_");
|
|
256
|
+
const name = path.basename(node);
|
|
257
|
+
graph.get(node)?.forEach((dep) => {
|
|
258
|
+
const depId = dep.replace(/[^a-zA-Z0-9]/g, "_");
|
|
259
|
+
const isViolated = violations.some((v) => v.includes(node) && v.includes(dep));
|
|
260
|
+
mermaid += ` ${id}[${name}] --> ${depId}[${path.basename(dep)}];\n`;
|
|
261
|
+
if (isViolated)
|
|
262
|
+
mermaid += ` style ${id} fill:#f96,stroke:#333,stroke-width:2px\n`;
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
return mermaid;
|
|
266
|
+
}
|
|
267
|
+
export async function loadState(workingDir) {
|
|
268
|
+
try {
|
|
269
|
+
const content = await fs.readFile(path.join(workingDir, STATE_FILE), "utf8");
|
|
270
|
+
return JSON.parse(content);
|
|
281
271
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
272
|
+
catch {
|
|
273
|
+
return { lastEntropy: 0, history: [], fileHashes: {} };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
export async function saveState(workingDir, state) {
|
|
277
|
+
const dir = path.join(workingDir, ".marie");
|
|
278
|
+
await fs.mkdir(dir, { recursive: true });
|
|
279
|
+
await fs.writeFile(path.join(workingDir, STATE_FILE), safeStringify(state, 2));
|
|
280
|
+
}
|
|
281
|
+
export async function getAllFiles(dir) {
|
|
282
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
283
|
+
const files = await Promise.all(entries.map((entry) => {
|
|
284
|
+
const res = path.resolve(dir, entry.name);
|
|
285
|
+
if (res.includes("node_modules") ||
|
|
286
|
+
res.includes(".git") ||
|
|
287
|
+
res.includes("dist"))
|
|
288
|
+
return [];
|
|
289
|
+
return entry.isDirectory() ? getAllFiles(res) : res;
|
|
290
|
+
}));
|
|
291
|
+
return files.flat().filter((f) => /\.(ts|tsx)$/.test(f));
|
|
292
|
+
}
|
|
293
|
+
export async function writeToSentinelLog(workingDir, report) {
|
|
294
|
+
const logPath = path.join(workingDir, "SENTINEL.md");
|
|
295
|
+
const summary = `
|
|
285
296
|
# 🛡️ Sentinel Report: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}
|
|
286
297
|
|
|
287
298
|
**Stability**: ${report.stability}
|
|
@@ -302,26 +313,25 @@ ${report.graphDefinition}
|
|
|
302
313
|
|
|
303
314
|
## 📜 High-Priority Alerts
|
|
304
315
|
${report.zoneViolations
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
316
|
+
.slice(0, 5)
|
|
317
|
+
.map((v) => `- ❌ ${v}`)
|
|
318
|
+
.join("\n")}
|
|
308
319
|
${report.circularDependencies
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
320
|
+
.slice(0, 3)
|
|
321
|
+
.map((c) => `- 🔄 ${c}`)
|
|
322
|
+
.join("\n")}
|
|
312
323
|
${report.duplication
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
324
|
+
.slice(0, 3)
|
|
325
|
+
.map((d) => `- 👯 ${d}`)
|
|
326
|
+
.join("\n")}
|
|
316
327
|
${report.navigationDrifts
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
328
|
+
.slice(0, 5)
|
|
329
|
+
.map((n) => `- 🧭 ${n}`)
|
|
330
|
+
.join("\n")}
|
|
320
331
|
|
|
321
332
|
---
|
|
322
333
|
*Marie Sentinel v3.1 — Grounded Architectural Guardian*
|
|
323
334
|
`;
|
|
324
|
-
|
|
325
|
-
}
|
|
335
|
+
await fs.writeFile(logPath, summary);
|
|
326
336
|
}
|
|
327
337
|
//# sourceMappingURL=MarieSentinelService.js.map
|