sentinel-verify 1.0.0 → 1.0.1
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/CHANGELOG.md +6 -0
- package/README.md +16 -2
- package/dist/mcp/projectRoot.js +68 -0
- package/dist/mcp/projectRoot.js.map +1 -0
- package/dist/mcp/server.js +53 -12
- package/dist/mcp/server.js.map +1 -1
- package/dist/storage/db.js +9 -1
- package/dist/storage/db.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Sentinel are documented here.
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **MCP server crashed with `ENOENT: mkdir '/.sentinel'` when the client launched it from an unexpected working directory** (e.g. `/`). The project root is now resolved defensively: `SENTINEL_PROJECT_ROOT` env var → plausible launch directory → the MCP client's declared workspace roots (`roots` capability) → a clear, actionable tool error. As a last line of defense, Sentinel now refuses to create `.sentinel/` at a filesystem root no matter how it is invoked.
|
|
9
|
+
- Tool enable/disable from `.sentinelrc` is now also enforced per call (previously only at server startup), so it holds even when the config couldn't be read until the root was resolved.
|
|
10
|
+
|
|
5
11
|
## 1.0.0 — 2026-07-10 (Phase 9 — first public release)
|
|
6
12
|
|
|
7
13
|
### Added
|
package/README.md
CHANGED
|
@@ -13,12 +13,26 @@ npm install -g sentinel-verify
|
|
|
13
13
|
cd your-project
|
|
14
14
|
sentinel init # creates the local .sentinel/ data folder
|
|
15
15
|
|
|
16
|
-
# Connect your agent (example: Claude Code)
|
|
17
|
-
claude mcp add sentinel -- sentinel mcp
|
|
16
|
+
# Connect your agent (example: Claude Code — run this inside your project)
|
|
17
|
+
claude mcp add sentinel -e SENTINEL_PROJECT_ROOT="$(pwd)" -- sentinel mcp
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
That's it. Your agent now has four verification tools it can call after making changes. In agent mode Sentinel needs **no API key and no local model** — see the two modes below.
|
|
21
21
|
|
|
22
|
+
**How Sentinel finds your project:** some MCP clients launch servers from an unexpected working directory (even `/`), so Sentinel resolves the project root defensively, in this order: the `SENTINEL_PROJECT_ROOT` environment variable → a plausible launch directory → the workspace roots your MCP client declares (the protocol's `roots` capability). If none of those work, tool calls fail with a clear message instead of writing anywhere wrong. For clients configured via JSON (Cursor, Claude Desktop, …), set the env var explicitly:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"sentinel": {
|
|
28
|
+
"command": "sentinel",
|
|
29
|
+
"args": ["mcp"],
|
|
30
|
+
"env": { "SENTINEL_PROJECT_ROOT": "/absolute/path/to/your-project" }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
22
36
|
Optional extras:
|
|
23
37
|
|
|
24
38
|
```bash
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Purpose: resolves the project root for the MCP server — never trusts a bogus working directory (like "/"), supports SENTINEL_PROJECT_ROOT and the MCP roots capability.
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { parse, resolve } from "node:path";
|
|
5
|
+
export const ROOT_HELP = 'Fix it one of two ways: (1) set SENTINEL_PROJECT_ROOT to your project\'s absolute path in the ' +
|
|
6
|
+
'MCP server config (the "env" field), or (2) configure your MCP client to launch "sentinel mcp" ' +
|
|
7
|
+
"with its working directory set to your project.";
|
|
8
|
+
function isUsableDirectory(path) {
|
|
9
|
+
try {
|
|
10
|
+
return statSync(path).isDirectory();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** "/" on POSIX, "C:\\" on Windows — never a legitimate project directory. */
|
|
17
|
+
export function isFilesystemRoot(path) {
|
|
18
|
+
const resolved = resolve(path);
|
|
19
|
+
return resolved === parse(resolved).root;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolution that needs no client connection: the SENTINEL_PROJECT_ROOT
|
|
23
|
+
* environment variable (explicit user intent — always wins), then the
|
|
24
|
+
* process working directory if it looks legitimate. Returns null when
|
|
25
|
+
* neither is usable; the caller can then ask the MCP client for its roots.
|
|
26
|
+
* Throws when SENTINEL_PROJECT_ROOT is set but wrong — explicit config that
|
|
27
|
+
* is broken must fail loudly, not fall through.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveStartupRoot() {
|
|
30
|
+
const envRoot = process.env.SENTINEL_PROJECT_ROOT;
|
|
31
|
+
if (envRoot) {
|
|
32
|
+
const resolved = resolve(envRoot);
|
|
33
|
+
if (!isUsableDirectory(resolved) || isFilesystemRoot(resolved)) {
|
|
34
|
+
throw new Error(`SENTINEL_PROJECT_ROOT is set to "${envRoot}", which is not a usable project directory. ${ROOT_HELP}`);
|
|
35
|
+
}
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
if (!isFilesystemRoot(cwd) && isUsableDirectory(cwd)) {
|
|
40
|
+
return cwd;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Asks the connected MCP client for its workspace roots (the protocol's
|
|
46
|
+
* standard way to communicate the project directory). Returns the first
|
|
47
|
+
* usable file:// root, or null when the client doesn't support roots or
|
|
48
|
+
* has none. Never throws — this is a fallback, not a requirement.
|
|
49
|
+
*/
|
|
50
|
+
export async function resolveRootViaClient(server) {
|
|
51
|
+
try {
|
|
52
|
+
if (!server.server.getClientCapabilities()?.roots)
|
|
53
|
+
return null;
|
|
54
|
+
const { roots } = await server.server.listRoots();
|
|
55
|
+
for (const root of roots ?? []) {
|
|
56
|
+
if (typeof root.uri === "string" && root.uri.startsWith("file://")) {
|
|
57
|
+
const path = fileURLToPath(root.uri);
|
|
58
|
+
if (isUsableDirectory(path) && !isFilesystemRoot(path))
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Client advertised roots but the request failed — treat as unavailable.
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=projectRoot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectRoot.js","sourceRoot":"","sources":["../../src/mcp/projectRoot.ts"],"names":[],"mappings":"AAAA,0KAA0K;AAE1K,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG3C,MAAM,CAAC,MAAM,SAAS,GACpB,gGAAgG;IAChG,iGAAiG;IACjG,iDAAiD,CAAC;AAEpD,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,QAAQ,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAClD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CACb,oCAAoC,OAAO,+CAA+C,SAAS,EAAE,CACtG,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAiB;IAC1D,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC;QAC/D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QAClD,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAC/B,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnE,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACrC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YACtE,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { gatherSecurityEvidence, SECURITY_TOOL_NAME } from "../verify/security.j
|
|
|
9
9
|
import { gatherStyleEvidence, STYLE_TOOL_NAME } from "../verify/style.js";
|
|
10
10
|
import { loadLocalEnv } from "../config/env.js";
|
|
11
11
|
import { loadSentinelrc, isToolEnabled } from "../config/sentinelrc.js";
|
|
12
|
+
import { resolveStartupRoot, resolveRootViaClient, ROOT_HELP } from "./projectRoot.js";
|
|
12
13
|
export const SERVER_NAME = "sentinel";
|
|
13
14
|
const diffInput = z
|
|
14
15
|
.string()
|
|
@@ -25,11 +26,22 @@ const findingSchema = z.object({
|
|
|
25
26
|
reason: z.string(),
|
|
26
27
|
source: z.literal("static"),
|
|
27
28
|
});
|
|
28
|
-
/**
|
|
29
|
-
function
|
|
29
|
+
/**
|
|
30
|
+
* Wraps an evidence function into an MCP handler: resolves the project root
|
|
31
|
+
* first (never trusting a bogus launch directory), re-checks the tool toggle
|
|
32
|
+
* against that root's .sentinelrc, and reports failures as tool errors.
|
|
33
|
+
*/
|
|
34
|
+
function evidenceHandler(toolName, requireProjectRoot, gather) {
|
|
30
35
|
return async (args) => {
|
|
31
36
|
try {
|
|
32
|
-
const
|
|
37
|
+
const projectRoot = await requireProjectRoot();
|
|
38
|
+
if (!isToolEnabled(loadSentinelrc(projectRoot), toolName)) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: `${toolName} is disabled in this project's .sentinelrc.` }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const evidence = gather(projectRoot, args);
|
|
33
45
|
return {
|
|
34
46
|
content: [{ type: "text", text: JSON.stringify(evidence, null, 2) }],
|
|
35
47
|
// The cast is safe: every evidence payload is a plain JSON object.
|
|
@@ -56,15 +68,41 @@ function evidenceHandler(toolName, gather) {
|
|
|
56
68
|
* Any human-facing logging from this process must go to stderr.
|
|
57
69
|
*/
|
|
58
70
|
export async function startMcpServer() {
|
|
59
|
-
//
|
|
60
|
-
|
|
71
|
+
// Some MCP clients launch servers from an unexpected directory (even "/"),
|
|
72
|
+
// so the project root is resolved defensively: SENTINEL_PROJECT_ROOT wins,
|
|
73
|
+
// then a plausible working directory, then — lazily, once the client is
|
|
74
|
+
// connected — the MCP roots capability. Throws here only when the env var
|
|
75
|
+
// is set but broken (explicit config must fail loudly).
|
|
76
|
+
const startupRoot = resolveStartupRoot();
|
|
77
|
+
if (startupRoot !== null) {
|
|
78
|
+
loadLocalEnv(startupRoot);
|
|
79
|
+
}
|
|
61
80
|
// Fail fast on a malformed .sentinelrc — a broken config must never be
|
|
62
|
-
// silently ignored. Tools disabled there are not registered at all.
|
|
63
|
-
|
|
81
|
+
// silently ignored. Tools disabled there are not registered at all. When
|
|
82
|
+
// the root is unknown at startup, all tools register and the per-call
|
|
83
|
+
// .sentinelrc check in evidenceHandler takes over.
|
|
84
|
+
const config = startupRoot !== null ? loadSentinelrc(startupRoot) : null;
|
|
64
85
|
const server = new McpServer({
|
|
65
86
|
name: SERVER_NAME,
|
|
66
87
|
version: SENTINEL_VERSION,
|
|
67
88
|
});
|
|
89
|
+
// Memoized, single-flight root resolution shared by all tool handlers.
|
|
90
|
+
let resolvedRoot = startupRoot;
|
|
91
|
+
let pendingResolve = null;
|
|
92
|
+
const requireProjectRoot = async () => {
|
|
93
|
+
if (resolvedRoot !== null)
|
|
94
|
+
return resolvedRoot;
|
|
95
|
+
pendingResolve ??= resolveRootViaClient(server);
|
|
96
|
+
const fromClient = await pendingResolve;
|
|
97
|
+
if (fromClient !== null) {
|
|
98
|
+
resolvedRoot = fromClient;
|
|
99
|
+
loadLocalEnv(fromClient);
|
|
100
|
+
console.error(`[sentinel] project root resolved via MCP roots: ${fromClient}`);
|
|
101
|
+
return fromClient;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(`Could not determine the project root. Sentinel was launched from "${process.cwd()}" ` +
|
|
104
|
+
`(not a usable project directory) and the MCP client did not provide workspace roots. ${ROOT_HELP}`);
|
|
105
|
+
};
|
|
68
106
|
const enabledTools = [];
|
|
69
107
|
if (isToolEnabled(config, TOOL_NAME)) {
|
|
70
108
|
enabledTools.push(TOOL_NAME);
|
|
@@ -84,7 +122,7 @@ export async function startMcpServer() {
|
|
|
84
122
|
diffFiles: z.array(z.string()),
|
|
85
123
|
addedLineCount: z.number(),
|
|
86
124
|
},
|
|
87
|
-
}, evidenceHandler(TOOL_NAME, ({ task, diff }) => gatherTaskMatchEvidence(
|
|
125
|
+
}, evidenceHandler(TOOL_NAME, requireProjectRoot, (root, { task, diff }) => gatherTaskMatchEvidence(root, task, diff)));
|
|
88
126
|
}
|
|
89
127
|
if (isToolEnabled(config, HALLUCINATION_TOOL_NAME)) {
|
|
90
128
|
enabledTools.push(HALLUCINATION_TOOL_NAME);
|
|
@@ -100,7 +138,7 @@ export async function startMcpServer() {
|
|
|
100
138
|
staticFindings: z.array(findingSchema),
|
|
101
139
|
declaredDependencies: z.array(z.string()).nullable(),
|
|
102
140
|
},
|
|
103
|
-
}, evidenceHandler(HALLUCINATION_TOOL_NAME, ({ diff }) => gatherHallucinationEvidence(
|
|
141
|
+
}, evidenceHandler(HALLUCINATION_TOOL_NAME, requireProjectRoot, (root, { diff }) => gatherHallucinationEvidence(root, diff)));
|
|
104
142
|
}
|
|
105
143
|
if (isToolEnabled(config, SECURITY_TOOL_NAME)) {
|
|
106
144
|
enabledTools.push(SECURITY_TOOL_NAME);
|
|
@@ -115,7 +153,7 @@ export async function startMcpServer() {
|
|
|
115
153
|
...evidenceBaseSchema,
|
|
116
154
|
staticFindings: z.array(findingSchema),
|
|
117
155
|
},
|
|
118
|
-
}, evidenceHandler(SECURITY_TOOL_NAME, ({ diff }) => gatherSecurityEvidence(
|
|
156
|
+
}, evidenceHandler(SECURITY_TOOL_NAME, requireProjectRoot, (root, { diff }) => gatherSecurityEvidence(root, diff)));
|
|
119
157
|
}
|
|
120
158
|
if (isToolEnabled(config, STYLE_TOOL_NAME)) {
|
|
121
159
|
enabledTools.push(STYLE_TOOL_NAME);
|
|
@@ -131,11 +169,14 @@ export async function startMcpServer() {
|
|
|
131
169
|
profile: z.string(),
|
|
132
170
|
staticFindings: z.array(findingSchema),
|
|
133
171
|
},
|
|
134
|
-
}, evidenceHandler(STYLE_TOOL_NAME, ({ diff }) => gatherStyleEvidence(
|
|
172
|
+
}, evidenceHandler(STYLE_TOOL_NAME, requireProjectRoot, (root, { diff }) => gatherStyleEvidence(root, diff)));
|
|
135
173
|
}
|
|
136
174
|
const transport = new StdioServerTransport();
|
|
137
175
|
await server.connect(transport);
|
|
138
176
|
console.error(`[sentinel] MCP server running on stdio (${SERVER_NAME} v${SENTINEL_VERSION}, agent-native evidence mode). ` +
|
|
139
|
-
`Tools: ${enabledTools.join(", ") || "(all disabled by .sentinelrc)"}`
|
|
177
|
+
`Tools: ${enabledTools.join(", ") || "(all disabled by .sentinelrc)"}. ` +
|
|
178
|
+
(startupRoot !== null
|
|
179
|
+
? `Project root: ${startupRoot}`
|
|
180
|
+
: "Project root unknown at startup — will ask the MCP client for its workspace roots on first tool call."));
|
|
140
181
|
}
|
|
141
182
|
//# sourceMappingURL=server.js.map
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,6JAA6J;AAE7J,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/db.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Purpose: opens (and migrates) Sentinel's local SQLite database at .sentinel/sentinel.db — all persistence is local, nothing leaves the machine.
|
|
2
2
|
import { DatabaseSync } from "node:sqlite";
|
|
3
3
|
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { join, parse, resolve } from "node:path";
|
|
5
5
|
export const SENTINEL_DIR = ".sentinel";
|
|
6
6
|
export const DB_FILENAME = "sentinel.db";
|
|
7
7
|
/**
|
|
@@ -33,6 +33,14 @@ export const CURRENT_SCHEMA_VERSION = MIGRATIONS.length;
|
|
|
33
33
|
* Returns the absolute path to the .sentinel/ folder.
|
|
34
34
|
*/
|
|
35
35
|
export function ensureSentinelDir(projectDir) {
|
|
36
|
+
// Last line of defense: a filesystem root ("/", "C:\") is never a real
|
|
37
|
+
// project — writing there means the caller misresolved its directory.
|
|
38
|
+
const resolved = resolve(projectDir);
|
|
39
|
+
if (resolved === parse(resolved).root) {
|
|
40
|
+
throw new Error(`Refusing to create ${SENTINEL_DIR}/ in the filesystem root ("${resolved}") — ` +
|
|
41
|
+
`the project directory was not resolved correctly. Run Sentinel from your project folder, ` +
|
|
42
|
+
`or set SENTINEL_PROJECT_ROOT when configuring the MCP server.`);
|
|
43
|
+
}
|
|
36
44
|
const dir = join(projectDir, SENTINEL_DIR);
|
|
37
45
|
mkdirSync(dir, { recursive: true });
|
|
38
46
|
// Self-ignoring folder (same trick as .terraform/): keeps local data out of git
|
package/dist/storage/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/storage/db.ts"],"names":[],"mappings":"AAAA,kJAAkJ;AAElJ,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/storage/db.ts"],"names":[],"mappings":"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"}
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sentinel-verify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A fully local, open-source verification layer for AI coding agents. Plugs into any MCP-compatible agent and lets it self-check its work before a human sees it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|