sentinel-verify 1.0.0

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 (56) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/LICENSE +21 -0
  3. package/README.md +127 -0
  4. package/dist/cli/commands/hook.js +133 -0
  5. package/dist/cli/commands/hook.js.map +1 -0
  6. package/dist/cli/commands/init.js +21 -0
  7. package/dist/cli/commands/init.js.map +1 -0
  8. package/dist/cli/commands/testHallucination.js +35 -0
  9. package/dist/cli/commands/testHallucination.js.map +1 -0
  10. package/dist/cli/commands/testSecurity.js +35 -0
  11. package/dist/cli/commands/testSecurity.js.map +1 -0
  12. package/dist/cli/commands/testStyle.js +41 -0
  13. package/dist/cli/commands/testStyle.js.map +1 -0
  14. package/dist/cli/commands/testVerify.js +36 -0
  15. package/dist/cli/commands/testVerify.js.map +1 -0
  16. package/dist/cli/diffInput.js +17 -0
  17. package/dist/cli/diffInput.js.map +1 -0
  18. package/dist/cli/index.js +93 -0
  19. package/dist/cli/index.js.map +1 -0
  20. package/dist/config/env.js +16 -0
  21. package/dist/config/env.js.map +1 -0
  22. package/dist/config/sentinelrc.js +104 -0
  23. package/dist/config/sentinelrc.js.map +1 -0
  24. package/dist/dashboard/page.js +270 -0
  25. package/dist/dashboard/page.js.map +1 -0
  26. package/dist/dashboard/server.js +65 -0
  27. package/dist/dashboard/server.js.map +1 -0
  28. package/dist/mcp/server.js +141 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/providers/anthropic.js +72 -0
  31. package/dist/providers/anthropic.js.map +1 -0
  32. package/dist/providers/index.js +37 -0
  33. package/dist/providers/index.js.map +1 -0
  34. package/dist/providers/ollama.js +102 -0
  35. package/dist/providers/ollama.js.map +1 -0
  36. package/dist/providers/types.js +3 -0
  37. package/dist/providers/types.js.map +1 -0
  38. package/dist/storage/db.js +123 -0
  39. package/dist/storage/db.js.map +1 -0
  40. package/dist/verify/combine.js +42 -0
  41. package/dist/verify/combine.js.map +1 -0
  42. package/dist/verify/diff.js +27 -0
  43. package/dist/verify/diff.js.map +1 -0
  44. package/dist/verify/evidence.js +25 -0
  45. package/dist/verify/evidence.js.map +1 -0
  46. package/dist/verify/hallucination.js +299 -0
  47. package/dist/verify/hallucination.js.map +1 -0
  48. package/dist/verify/security.js +274 -0
  49. package/dist/verify/security.js.map +1 -0
  50. package/dist/verify/style.js +489 -0
  51. package/dist/verify/style.js.map +1 -0
  52. package/dist/verify/taskMatch.js +107 -0
  53. package/dist/verify/taskMatch.js.map +1 -0
  54. package/dist/version.js +3 -0
  55. package/dist/version.js.map +1 -0
  56. package/package.json +43 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,84 @@
1
+ # Changelog
2
+
3
+ All notable changes to Sentinel are documented here.
4
+
5
+ ## 1.0.0 — 2026-07-10 (Phase 9 — first public release)
6
+
7
+ ### Added
8
+ - MIT `LICENSE` file, `prepublishOnly` build script, and packaging hygiene (`files` limits the published package to `dist/` + `CHANGELOG.md`).
9
+ - "Sentinel Verified ✅" README badge snippet for projects that gate their commits/agents with Sentinel.
10
+
11
+ ### Changed
12
+ - Package name finalized: **`sentinel-verify`** (confirmed available on npm).
13
+ - README rewritten for first-time visitors: what it is → quickstart → the two modes → features → configuration reference.
14
+ - Internal planning documents moved out of the repo root into `docs/internal/`.
15
+
16
+ ## 0.10.0 — 2026-07-10 (Phase 8 — git pre-commit hook)
17
+
18
+ ### Added
19
+ - `sentinel install-hook` / `sentinel uninstall-hook`: a marker-signed pre-commit hook that checks `git diff --cached` in standalone mode before every commit. Runs `check_security` by default; configure with the new `hook.tools` field in `.sentinelrc` (`check_hallucination` and `check_style_consistency` may be added; `verify_task_match` cannot run in a hook). Findings block the commit with a printed summary; `git commit --no-verify` bypasses once. Fail-closed: if the model layer is unavailable, the commit is blocked with setup instructions rather than silently allowed.
20
+ - Install refuses to overwrite a pre-commit hook Sentinel didn't create; uninstall refuses to delete one.
21
+
22
+ ## 0.9.0 — 2026-07-10 (Phase 7 — agent-native judgment mode)
23
+
24
+ ### Changed
25
+ - **MCP tools no longer make model calls.** The agent calling them (Claude Code, Cursor, …) is itself a capable model, so each tool now returns deterministic evidence — static findings, the style profile, declared dependencies, `.sentinelrc` context and rules — plus an instruction telling the agent how to judge. No Ollama and no API key are needed in agent mode.
26
+ - MCP-mode runs are logged as `evidence_provided` (Sentinel didn't judge); the dashboard shows these with an informational chip and counts them as flagged only when the handed-over findings list was non-empty.
27
+ - Standalone CLI mode (`sentinel test-*`) is unchanged: with no agent present, the configured provider (Ollama by default, Anthropic with a key) still does the judgment.
28
+
29
+ ## 0.8.0 — 2026-07-08 (Phase 6)
30
+
31
+ ### Added
32
+ - `sentinel dashboard` command: a local-only web dashboard (binds to 127.0.0.1, default port 4747, `--port` to change) showing total verifications and per-tool breakdown, flag rate per tool, recent history newest-first, and click-to-expand details for every stored verification. One self-contained HTML page — no CDN assets, no telemetry, no analytics; auto-refreshes every 10 seconds with a manual refresh button; Ctrl+C stops it cleanly.
33
+
34
+ ## 0.7.0 — 2026-07-08 (Phase 5)
35
+
36
+ ### Added
37
+ - Optional `.sentinelrc` (or `.sentinelrc.json`) project config, validated strictly on load (unknown keys and type errors fail with a clear message, never silently ignored):
38
+ - `context` — free-text tech-stack description fed into all four tools' model prompts.
39
+ - `style` — convention overrides (`indent`, `quotes`, `semicolons`, `naming`, `moduleStyle`) that replace inference; pinned conventions are enforced even in projects too new to profile.
40
+ - `tools` — per-tool enable/disable; disabled tools are not registered on the MCP server and their CLI commands print a skip notice.
41
+ - `rules` — plain-language project policies fed to the model layer of every tool.
42
+ - Zero-config unchanged: without the file, behavior is identical to 0.6.0.
43
+
44
+ ## 0.6.0 — 2026-07-08 (Phase 4 — completes the four core tools)
45
+
46
+ ### Added
47
+ - `check_style_consistency` MCP tool: profiles the existing codebase's conventions (indent character/width, quotes, semicolons, naming, ESM/CommonJS) from sampled files — excluding files the diff touches — then flags clear deviations. Dominant conventions (≥80%) violated repeatedly are flagged deterministically; the model adds advisory findings, deduplicated and filtered to actual style topics. Verdict: `no_issues` or `style_mismatch`; the detected profile is returned with every result.
48
+ - `sentinel test-style` CLI command (`--diff-file` or piped stdin, exit 1 on findings).
49
+
50
+ ## 0.5.0 — 2026-07-08 (Phase 3)
51
+
52
+ ### Added
53
+ - `check_security` MCP tool: flags leaked secrets (AWS/GitHub/Slack/Google/Stripe/Anthropic/OpenAI/npm keys, private key blocks, JWTs, connection strings with embedded passwords, entropy-gated hardcoded credentials) and unsafe patterns (`eval`, `new Function`, shell command injection, `shell: true`/`shell=True`, disabled TLS verification) via deterministic rules, plus advisory model judgment for context-dependent risks. Verdict: `no_issues` or `security_risk`.
54
+ - `sentinel test-security` CLI command (`--diff-file` or piped stdin, exit 1 on findings).
55
+ - Detected secrets are **masked** (first 4 characters + `…`) in all output and in the local database — Sentinel never stores or prints a whole secret.
56
+ - Shared internals: diff parsing and the static/model combiner extracted to `src/verify/diff.ts` and `src/verify/combine.ts` (no behavior change).
57
+
58
+ ## 0.4.0 — 2026-07-08 (Phase 2)
59
+
60
+ ### Added
61
+ - `check_hallucination` MCP tool: flags hallucinated code in a diff by combining a static evidence check (imported packages verified against package.json/node_modules, imported files and symbols verified against the actual filesystem — JS/TS) with model judgment (made-up methods, invented API usage). Verdict: `no_issues` or `possible_hallucination` with per-issue location, reason, and source.
62
+ - `sentinel test-hallucination` CLI command (`--diff-file` or piped stdin).
63
+ - `details` column on the `verifications` table (JSON per-issue breakdown). Schema version 1 → 2, migrated automatically.
64
+
65
+ ## 0.3.0 — 2026-07-08 (Phase 1 fix: free by default)
66
+
67
+ ### Changed
68
+ - **Ollama is now the default provider** — Sentinel works fully locally and fully free with no API key. Default local model: `qwen2.5-coder`.
69
+ - Anthropic is now opt-in: used only when `ANTHROPIC_API_KEY` is set (or `SENTINEL_PROVIDER=anthropic`). With no key set, Sentinel silently uses Ollama.
70
+ - If Ollama is unreachable and no API key is set, the error explains exactly how to fix it (install Ollama + pull the model, or set a key).
71
+
72
+ ## 0.2.0 — 2026-07-08 (Phase 1)
73
+
74
+ ### Added
75
+ - `verify_task_match` MCP tool: checks whether a code diff functionally accomplishes a stated task using the user's own LLM provider. Returns `match` / `partial_match` / `no_match` plus a reason.
76
+ - Provider layer with Anthropic support (official `@anthropic-ai/sdk`, credentials via `ANTHROPIC_API_KEY` or a git-ignored `.env`). OpenAI and Ollama slots reserved for later phases.
77
+ - `sentinel test-verify` CLI command for running the check manually (`--task`, `--diff-file` or piped stdin).
78
+ - `verifications` table in the local SQLite store (task, diff hash, verdict, reason, provider, model, timestamp). Schema version bumped 0 → 1 with automatic migration.
79
+ - `.env` loading from the project directory (existing shell variables always win).
80
+
81
+ ## 0.1.0 — 2026-07-08 (Phase 0)
82
+
83
+ ### Added
84
+ - Project scaffold: TypeScript, CLI entry point (`sentinel init`, `sentinel mcp`), MCP server skeleton over stdio, local SQLite storage via `node:sqlite` (zero native dependencies), base README.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhinav Bansal (AutomateIQ)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Sentinel
2
+
3
+ **A fully local verification layer for AI coding agents.** Sentinel plugs into any MCP-compatible agent (Claude Code, Cursor, Copilot, OpenCode, …) and lets it self-check its own work — does the diff actually do the task, does the code it references exist, did it leak a secret, does it match your codebase's style — before a human ever sees it. Everything runs on your machine: no cloud backend, no telemetry, no account.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-1a1a19)](LICENSE)
6
+
7
+ ## Quickstart
8
+
9
+ ```bash
10
+ # Requires Node.js 24+ (Sentinel uses Node's built-in SQLite — nothing to compile)
11
+ npm install -g sentinel-verify
12
+
13
+ cd your-project
14
+ sentinel init # creates the local .sentinel/ data folder
15
+
16
+ # Connect your agent (example: Claude Code)
17
+ claude mcp add sentinel -- sentinel mcp
18
+ ```
19
+
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
+
22
+ Optional extras:
23
+
24
+ ```bash
25
+ sentinel install-hook # block bad commits at git pre-commit time
26
+ sentinel dashboard # local-only web UI over your verification history
27
+ ```
28
+
29
+ ## The four checks
30
+
31
+ | Tool | Question it answers |
32
+ |---|---|
33
+ | `verify_task_match` | Does the diff **functionally** accomplish the stated task — not just superficially? |
34
+ | `check_hallucination` | Do the packages, files, and symbols the diff references actually **exist**? (verified against `package.json`, `node_modules`, and the real filesystem) |
35
+ | `check_security` | Any leaked secrets (AWS/GitHub/Slack/Stripe/… keys, private keys, connection strings — detected and **masked**), `eval`, shell injection, disabled TLS, hardcoded credentials? |
36
+ | `check_style_consistency` | Does the diff write code the way **this** project already does? (indentation, quotes, semicolons, naming, module style — measured from your real files) |
37
+
38
+ Every check combines **deterministic evidence** (filesystem checks, pattern rules, style measurement — authoritative) with **model judgment** (for what rules can't prove). Every result is stored locally in `.sentinel/sentinel.db`.
39
+
40
+ ## Two modes, one important idea
41
+
42
+ **Agent mode (MCP)** — when an AI agent calls Sentinel's tools, Sentinel does **not** run a model of its own. The calling agent is already a powerful, already-paid-for model; a second, weaker judge adds cost and noise. Instead each tool returns structured **evidence** — the deterministic findings, the measured style profile, your project rules — plus an instruction telling the agent how to judge. Requires nothing: no key, no Ollama.
43
+
44
+ **Standalone mode (CLI)** — running checks yourself in a terminal, there's no agent to judge, so Sentinel uses its own model layer: **free local Ollama by default** (recommended model: `ollama pull qwen2.5-coder`), or your own Anthropic API key if you set one.
45
+
46
+ ```bash
47
+ git diff | sentinel test-security # standalone check, judged locally
48
+ git diff | sentinel test-hallucination
49
+ git diff | sentinel test-style
50
+ sentinel test-verify --task "Add rate limiting" --diff-file change.diff
51
+ ```
52
+
53
+ All `test-*` commands exit non-zero on findings, so they compose into scripts.
54
+
55
+ | | Agent mode (MCP) | Standalone mode (CLI) |
56
+ |---|---|---|
57
+ | Who judges | The calling agent | Sentinel's configured model |
58
+ | Requirements | Nothing | Ollama running, **or** `ANTHROPIC_API_KEY` |
59
+ | Result | Evidence + judgment instruction | Final verdict (exit code) |
60
+
61
+ Standalone settings: `SENTINEL_PROVIDER` (`ollama`/`anthropic`; default auto — Anthropic if a key is set, else Ollama), `SENTINEL_MODEL` (model override), `OLLAMA_HOST`, `ANTHROPIC_API_KEY` (env or a git-ignored `.env` in your project root).
62
+
63
+ ## The pre-commit hook
64
+
65
+ ```bash
66
+ sentinel install-hook # sentinel uninstall-hook to remove
67
+ ```
68
+
69
+ Every `git commit` checks the **staged** diff — `check_security` by default. Findings block the commit with a printed summary; bypass one commit with `git commit --no-verify`. **Fail-closed:** if the model layer is unavailable the commit is blocked with setup instructions — a broken setup never silently waves commits through. Install refuses to overwrite a hook Sentinel didn't create, and uninstall refuses to delete one.
70
+
71
+ ## The local dashboard
72
+
73
+ ```bash
74
+ sentinel dashboard # http://127.0.0.1:4747 (--port to change)
75
+ ```
76
+
77
+ Totals, per-tool flag rates, and full history with click-to-expand details, served from one self-contained page bound to `127.0.0.1` — no external assets, no analytics. Secrets are masked before storage, so the dashboard can never display one.
78
+
79
+ ## Configuration: `.sentinelrc` (optional)
80
+
81
+ Zero config is the default. An optional `.sentinelrc` (JSON, strictly validated — typos fail loudly) in your project root adds team standards:
82
+
83
+ ```json
84
+ {
85
+ "context": "React + TypeScript SPA using GSAP for animations. Modern browsers only.",
86
+ "style": { "indent": 2, "quotes": "single", "semicolons": "always", "naming": "camelCase", "moduleStyle": "esm" },
87
+ "tools": { "check_style_consistency": false },
88
+ "rules": [
89
+ "Never suggest React class components — function components and hooks only.",
90
+ "No new npm dependencies without discussion."
91
+ ],
92
+ "hook": { "tools": ["check_security", "check_hallucination"] }
93
+ }
94
+ ```
95
+
96
+ | Field | Effect |
97
+ |---|---|
98
+ | `context` | Free-text project description fed to all model prompts (max 2000 chars). |
99
+ | `style` | Convention overrides that **replace** inference — enforced even in brand-new projects. `indent` is `"tabs"` or a number of spaces. |
100
+ | `tools` | Disable any tool: it leaves the MCP server and its CLI command prints a skip notice. |
101
+ | `rules` | Plain-language policies (max 20 × 500 chars) the model layer flags violations of. Short and concrete works best. |
102
+ | `hook.tools` | What the pre-commit hook runs (default `["check_security"]`; `verify_task_match` can't run in a hook). |
103
+
104
+ ## Show you're covered
105
+
106
+ Integrated Sentinel in your project? Add the badge to your README:
107
+
108
+ ```markdown
109
+ [![Sentinel Verified ✅](https://img.shields.io/badge/Sentinel-Verified%20%E2%9C%85-2a78d6)](https://www.npmjs.com/package/sentinel-verify)
110
+ ```
111
+
112
+ [![Sentinel Verified ✅](https://img.shields.io/badge/Sentinel-Verified%20%E2%9C%85-2a78d6)](https://www.npmjs.com/package/sentinel-verify)
113
+
114
+ ## Design principles
115
+
116
+ - **100% local** — no Sentinel-owned backend, no telemetry; nothing leaves your machine except your own optional API calls to your own provider.
117
+ - **Free by default** — agent mode needs no model at all; standalone mode runs on local Ollama.
118
+ - **Deterministic first** — filesystem checks, pattern rules, and measurements are authoritative; model judgment fills the gaps and never overrides them.
119
+ - **Agent-agnostic** — plain MCP; not locked to any vendor.
120
+
121
+ ## Language coverage
122
+
123
+ The deterministic halves of `check_hallucination` and `check_style_consistency` cover JavaScript/TypeScript; `check_security`'s rules cover Node, Python, Go, and shell patterns. Other languages get the model-judgment half of every tool.
124
+
125
+ ## Contributing
126
+
127
+ Issues and PRs welcome. The codebase is deliberately small and readable: `src/verify/` holds the four tools plus shared diff/combine logic, `src/providers/` the model layer (Ollama, Anthropic — an OpenAI provider is a welcome contribution), `src/mcp/` the server, `src/cli/` the commands, `src/dashboard/` the UI. Keep dependencies minimal (currently three), keep secrets masked, and keep everything local. Licensed MIT.
@@ -0,0 +1,133 @@
1
+ // Purpose: implements the git pre-commit hook — install/uninstall the marker-signed hook script, and hook-run which checks the staged diff and blocks bad commits.
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from "node:fs";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { loadSentinelrc, isToolEnabled, DEFAULT_HOOK_TOOLS, } from "../../config/sentinelrc.js";
7
+ import { loadLocalEnv } from "../../config/env.js";
8
+ import { checkHallucination } from "../../verify/hallucination.js";
9
+ import { checkSecurity } from "../../verify/security.js";
10
+ import { checkStyleConsistency } from "../../verify/style.js";
11
+ /** Present in every hook Sentinel writes — install/uninstall refuse to touch files without it. */
12
+ const SENTINEL_MARKER = "# sentinel-hook";
13
+ const HOOK_RUNNERS = {
14
+ check_hallucination: checkHallucination,
15
+ check_security: checkSecurity,
16
+ check_style_consistency: checkStyleConsistency,
17
+ };
18
+ function git(args, cwd) {
19
+ return execFileSync("git", args, { encoding: "utf8", cwd }).trim();
20
+ }
21
+ /** Resolves the repo's hooks directory (respects core.hooksPath), or throws a friendly error. */
22
+ function hooksDir() {
23
+ let out;
24
+ try {
25
+ out = git(["rev-parse", "--git-path", "hooks"]);
26
+ }
27
+ catch {
28
+ throw new Error("Not a git repository — run this inside the repo you want the hook in.");
29
+ }
30
+ const dir = resolve(process.cwd(), out);
31
+ mkdirSync(dir, { recursive: true });
32
+ return dir;
33
+ }
34
+ /** Builds the hook script: prefer `sentinel` on PATH, fall back to the absolute paths captured at install time. */
35
+ function hookScript() {
36
+ const cliEntry = resolve(dirname(fileURLToPath(import.meta.url)), "..", "index.js");
37
+ return `#!/bin/sh
38
+ ${SENTINEL_MARKER} v1 — installed by Sentinel. Remove with: sentinel uninstall-hook. Bypass once with: git commit --no-verify
39
+ if command -v sentinel >/dev/null 2>&1; then
40
+ exec sentinel hook-run
41
+ fi
42
+ exec "${process.execPath}" "${cliEntry}" hook-run
43
+ `;
44
+ }
45
+ /** `sentinel install-hook` — writes .git/hooks/pre-commit, never overwriting a foreign hook. */
46
+ export function runInstallHook() {
47
+ const hookPath = join(hooksDir(), "pre-commit");
48
+ if (existsSync(hookPath) && !readFileSync(hookPath, "utf8").includes(SENTINEL_MARKER)) {
49
+ throw new Error(`A pre-commit hook already exists at ${hookPath} and it was not created by Sentinel. ` +
50
+ `Not overwriting it. To combine them, add this line to your existing hook manually: sentinel hook-run`);
51
+ }
52
+ writeFileSync(hookPath, hookScript(), { mode: 0o755 });
53
+ const config = loadSentinelrc(process.cwd());
54
+ const tools = config?.hook?.tools ?? DEFAULT_HOOK_TOOLS;
55
+ console.log(`Pre-commit hook installed at ${hookPath}`);
56
+ console.log(`It checks staged changes with: ${tools.join(", ")} (configure via "hook".tools in .sentinelrc).`);
57
+ console.log("Findings block the commit. Bypass a single commit with: git commit --no-verify");
58
+ }
59
+ /** `sentinel uninstall-hook` — removes the hook only if Sentinel created it. */
60
+ export function runUninstallHook() {
61
+ const hookPath = join(hooksDir(), "pre-commit");
62
+ if (!existsSync(hookPath)) {
63
+ console.log("No pre-commit hook installed — nothing to remove.");
64
+ return;
65
+ }
66
+ if (!readFileSync(hookPath, "utf8").includes(SENTINEL_MARKER)) {
67
+ throw new Error(`The pre-commit hook at ${hookPath} was not created by Sentinel — leaving it untouched.`);
68
+ }
69
+ unlinkSync(hookPath);
70
+ console.log("Sentinel pre-commit hook removed.");
71
+ }
72
+ /**
73
+ * `sentinel hook-run` — what the hook script executes. Checks the staged diff
74
+ * in standalone mode. Exit 0 allows the commit; anything else blocks it.
75
+ * Fail-closed: if the model layer is unavailable, the commit is blocked with
76
+ * instructions rather than silently allowed through.
77
+ */
78
+ export async function runHook() {
79
+ let projectDir;
80
+ try {
81
+ projectDir = git(["rev-parse", "--show-toplevel"]);
82
+ }
83
+ catch {
84
+ throw new Error("Not a git repository — the Sentinel hook only runs inside one.");
85
+ }
86
+ loadLocalEnv(projectDir);
87
+ const diff = git(["diff", "--cached"], projectDir);
88
+ if (diff.trim().length === 0) {
89
+ return; // nothing staged (e.g. empty or amend-only commit) — nothing to check
90
+ }
91
+ const config = loadSentinelrc(projectDir);
92
+ const tools = (config?.hook?.tools ?? DEFAULT_HOOK_TOOLS).filter((tool) => isToolEnabled(config, tool));
93
+ if (tools.length === 0) {
94
+ console.log("Sentinel pre-commit: all hook tools are disabled in .sentinelrc — skipping.");
95
+ return;
96
+ }
97
+ console.log(`Sentinel pre-commit: checking staged changes (${tools.join(", ")})…`);
98
+ let blocked = false;
99
+ for (const tool of tools) {
100
+ let outcome;
101
+ try {
102
+ outcome = await HOOK_RUNNERS[tool](projectDir, diff);
103
+ }
104
+ catch (error) {
105
+ // Fail closed: a broken setup must not silently wave commits through.
106
+ const message = error instanceof Error ? error.message : String(error);
107
+ console.error(`\n🚫 ${tool} could not run: ${message}`);
108
+ console.error("\nThe commit is blocked because Sentinel could not verify it. " +
109
+ "Fix the setup above, or bypass this one commit with: git commit --no-verify");
110
+ process.exitCode = 1;
111
+ return;
112
+ }
113
+ if (outcome.verdict === "no_issues") {
114
+ console.log(` ✅ ${tool}: clean`);
115
+ }
116
+ else {
117
+ blocked = true;
118
+ console.log(` 🚨 ${tool}: ${outcome.issues.length} issue(s)`);
119
+ outcome.issues.forEach((issue, index) => {
120
+ console.log(` ${index + 1}. [${issue.source}] ${issue.location}`);
121
+ console.log(` ${issue.reason}`);
122
+ });
123
+ }
124
+ }
125
+ if (blocked) {
126
+ console.log("\nCommit blocked by Sentinel. Fix the issues above, or bypass this one commit with: git commit --no-verify");
127
+ process.exitCode = 1;
128
+ }
129
+ else {
130
+ console.log("Sentinel pre-commit: all checks clean — commit allowed.");
131
+ }
132
+ }
133
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../src/cli/commands/hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ // Purpose: implements `sentinel init` — sets up the local .sentinel/ folder and SQLite database in the current project.
2
+ import { openDatabase, getSchemaVersion, SENTINEL_DIR, DB_FILENAME } from "../../storage/db.js";
3
+ /**
4
+ * Initializes Sentinel in the given project directory.
5
+ * Phase 0 placeholder: creates .sentinel/, the local database, and nothing else.
6
+ */
7
+ export function runInit(projectDir) {
8
+ const db = openDatabase(projectDir);
9
+ const version = getSchemaVersion(db);
10
+ db.close();
11
+ console.log("Sentinel initialized.");
12
+ console.log(` Created ${SENTINEL_DIR}/ with a local SQLite database (${SENTINEL_DIR}/${DB_FILENAME}).`);
13
+ console.log(` Schema version: ${version}`);
14
+ console.log("");
15
+ console.log("Everything Sentinel stores stays in this folder, on this machine.");
16
+ console.log(`A .gitignore inside ${SENTINEL_DIR}/ keeps it out of version control automatically.`);
17
+ console.log("");
18
+ console.log("Next step: connect your AI coding agent to the Sentinel MCP server.");
19
+ console.log(" Example (Claude Code): claude mcp add sentinel -- sentinel mcp");
20
+ }
21
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,wHAAwH;AAExH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEhG;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,UAAkB;IACxC,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACrC,EAAE,CAAC,KAAK,EAAE,CAAC;IAEX,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,aAAa,YAAY,mCAAmC,YAAY,IAAI,WAAW,IAAI,CAAC,CAAC;IACzG,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,uBAAuB,YAAY,kDAAkD,CAAC,CAAC;IACnG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,35 @@
1
+ // Purpose: implements `sentinel test-hallucination` — runs the check_hallucination tool from the command line, without needing an agent.
2
+ import { checkHallucination, HALLUCINATION_TOOL_NAME } from "../../verify/hallucination.js";
3
+ import { loadLocalEnv } from "../../config/env.js";
4
+ import { loadSentinelrc, isToolEnabled } from "../../config/sentinelrc.js";
5
+ import { readDiffInput } from "../diffInput.js";
6
+ import { SENTINEL_DIR, DB_FILENAME } from "../../storage/db.js";
7
+ /** Reads the diff, runs the hallucination check, and prints a human-readable report. */
8
+ export async function runTestHallucination(projectDir, options) {
9
+ loadLocalEnv(projectDir);
10
+ if (!isToolEnabled(loadSentinelrc(projectDir), HALLUCINATION_TOOL_NAME)) {
11
+ console.log(`Skipped: ${HALLUCINATION_TOOL_NAME} is disabled in .sentinelrc.`);
12
+ return;
13
+ }
14
+ const diff = readDiffInput("test-hallucination", options.diffFile);
15
+ console.log("Checking for hallucinated packages, files, and APIs…");
16
+ const result = await checkHallucination(projectDir, diff);
17
+ console.log("");
18
+ if (result.verdict === "no_issues") {
19
+ console.log("✅ NO ISSUES — nothing in the diff looks hallucinated");
20
+ }
21
+ else {
22
+ console.log(`🚨 POSSIBLE HALLUCINATION — ${result.issues.length} issue(s) found`);
23
+ result.issues.forEach((issue, index) => {
24
+ console.log(` ${index + 1}. [${issue.source}] ${issue.location}`);
25
+ console.log(` ${issue.reason}`);
26
+ });
27
+ }
28
+ console.log("");
29
+ console.log(`Checked by ${result.provider} (${result.model}).`);
30
+ console.log(`Result saved locally to ${SENTINEL_DIR}/${DB_FILENAME}.`);
31
+ if (result.verdict === "possible_hallucination") {
32
+ process.exitCode = 1;
33
+ }
34
+ }
35
+ //# sourceMappingURL=testHallucination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testHallucination.js","sourceRoot":"","sources":["../../../src/cli/commands/testHallucination.ts"],"names":[],"mappings":"AAAA,yIAAyI;AAEzI,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC5F,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMhE,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAAkB,EAClB,OAAiC;IAEjC,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAC,EAAE,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,YAAY,uBAAuB,8BAA8B,CAAC,CAAC;QAC/E,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,oBAAoB,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEnE,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAE1D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,CAAC,MAAM,CAAC,MAAM,iBAAiB,CAAC,CAAC;QAClF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,2BAA2B,YAAY,IAAI,WAAW,GAAG,CAAC,CAAC;IAEvE,IAAI,MAAM,CAAC,OAAO,KAAK,wBAAwB,EAAE,CAAC;QAChD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ // Purpose: implements `sentinel test-security` — runs the check_security tool from the command line, without needing an agent.
2
+ import { checkSecurity, SECURITY_TOOL_NAME } from "../../verify/security.js";
3
+ import { loadLocalEnv } from "../../config/env.js";
4
+ import { loadSentinelrc, isToolEnabled } from "../../config/sentinelrc.js";
5
+ import { readDiffInput } from "../diffInput.js";
6
+ import { SENTINEL_DIR, DB_FILENAME } from "../../storage/db.js";
7
+ /** Reads the diff, runs the security check, and prints a human-readable report. */
8
+ export async function runTestSecurity(projectDir, options) {
9
+ loadLocalEnv(projectDir);
10
+ if (!isToolEnabled(loadSentinelrc(projectDir), SECURITY_TOOL_NAME)) {
11
+ console.log(`Skipped: ${SECURITY_TOOL_NAME} is disabled in .sentinelrc.`);
12
+ return;
13
+ }
14
+ const diff = readDiffInput("test-security", options.diffFile);
15
+ console.log("Checking for leaked secrets and unsafe patterns…");
16
+ const result = await checkSecurity(projectDir, diff);
17
+ console.log("");
18
+ if (result.verdict === "no_issues") {
19
+ console.log("✅ NO ISSUES — no security risks found in the diff");
20
+ }
21
+ else {
22
+ console.log(`🚨 SECURITY RISK — ${result.issues.length} issue(s) found`);
23
+ result.issues.forEach((issue, index) => {
24
+ console.log(` ${index + 1}. [${issue.source}] ${issue.location}`);
25
+ console.log(` ${issue.reason}`);
26
+ });
27
+ }
28
+ console.log("");
29
+ console.log(`Checked by ${result.provider} (${result.model}). Secrets are masked in output and storage.`);
30
+ console.log(`Result saved locally to ${SENTINEL_DIR}/${DB_FILENAME}.`);
31
+ if (result.verdict === "security_risk") {
32
+ process.exitCode = 1;
33
+ }
34
+ }
35
+ //# sourceMappingURL=testSecurity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testSecurity.js","sourceRoot":"","sources":["../../../src/cli/commands/testSecurity.ts"],"names":[],"mappings":"AAAA,+HAA+H;AAE/H,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMhE,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,OAA4B;IAE5B,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,YAAY,kBAAkB,8BAA8B,CAAC,CAAC;QAC1E,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE9D,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IACnE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,MAAM,CAAC,MAAM,iBAAiB,CAAC,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,8CAA8C,CAAC,CAAC;IAC1G,OAAO,CAAC,GAAG,CAAC,2BAA2B,YAAY,IAAI,WAAW,GAAG,CAAC,CAAC;IAEvE,IAAI,MAAM,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ // Purpose: implements `sentinel test-style` — runs the check_style_consistency tool from the command line, without needing an agent.
2
+ import { checkStyleConsistency, STYLE_TOOL_NAME } from "../../verify/style.js";
3
+ import { loadLocalEnv } from "../../config/env.js";
4
+ import { loadSentinelrc, isToolEnabled } from "../../config/sentinelrc.js";
5
+ import { readDiffInput } from "../diffInput.js";
6
+ import { SENTINEL_DIR, DB_FILENAME } from "../../storage/db.js";
7
+ /** Reads the diff, runs the style check, and prints the profile plus a report. */
8
+ export async function runTestStyle(projectDir, options) {
9
+ loadLocalEnv(projectDir);
10
+ if (!isToolEnabled(loadSentinelrc(projectDir), STYLE_TOOL_NAME)) {
11
+ console.log(`Skipped: ${STYLE_TOOL_NAME} is disabled in .sentinelrc.`);
12
+ return;
13
+ }
14
+ const diff = readDiffInput("test-style", options.diffFile);
15
+ console.log("Profiling the project's style and checking the diff against it…");
16
+ const result = await checkStyleConsistency(projectDir, diff);
17
+ console.log("");
18
+ console.log("Project conventions detected:");
19
+ for (const line of result.profile.split("\n"))
20
+ console.log(` ${line}`);
21
+ console.log("");
22
+ if (result.verdict === "no_issues") {
23
+ console.log("✅ NO ISSUES — the diff is consistent with the project's style");
24
+ }
25
+ else {
26
+ console.log(`🎨 STYLE MISMATCH — ${result.issues.length} inconsistency(ies) found`);
27
+ result.issues.forEach((issue, index) => {
28
+ console.log(` ${index + 1}. [${issue.source}] ${issue.location}`);
29
+ console.log(` ${issue.reason}`);
30
+ });
31
+ }
32
+ console.log("");
33
+ if (result.provider !== "none") {
34
+ console.log(`Checked by ${result.provider} (${result.model}).`);
35
+ }
36
+ console.log(`Result saved locally to ${SENTINEL_DIR}/${DB_FILENAME}.`);
37
+ if (result.verdict === "style_mismatch") {
38
+ process.exitCode = 1;
39
+ }
40
+ }
41
+ //# sourceMappingURL=testStyle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testStyle.js","sourceRoot":"","sources":["../../../src/cli/commands/testStyle.ts"],"names":[],"mappings":"AAAA,qIAAqI;AAErI,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMhE,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB,EAAE,OAAyB;IAC9E,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC,EAAE,CAAC;QAChE,OAAO,CAAC,GAAG,CAAC,YAAY,eAAe,8BAA8B,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE3D,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IAC/E,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAE7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,MAAM,CAAC,MAAM,2BAA2B,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,2BAA2B,YAAY,IAAI,WAAW,GAAG,CAAC,CAAC;IAEvE,IAAI,MAAM,CAAC,OAAO,KAAK,gBAAgB,EAAE,CAAC;QACxC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ // Purpose: implements `sentinel test-verify` — runs the verify_task_match tool from the command line, without needing an agent connected.
2
+ import { verifyTaskMatch, TOOL_NAME } from "../../verify/taskMatch.js";
3
+ import { loadLocalEnv } from "../../config/env.js";
4
+ import { loadSentinelrc, isToolEnabled } from "../../config/sentinelrc.js";
5
+ import { readDiffInput } from "../diffInput.js";
6
+ import { SENTINEL_DIR, DB_FILENAME } from "../../storage/db.js";
7
+ const VERDICT_LABELS = {
8
+ match: "✅ MATCH — the diff accomplishes the stated task",
9
+ partial_match: "⚠️ PARTIAL MATCH — the diff only partly accomplishes the task",
10
+ no_match: "❌ NO MATCH — the diff does not accomplish the task",
11
+ };
12
+ /**
13
+ * Reads the diff (from --diff-file or piped stdin), runs the verification,
14
+ * and prints a human-readable verdict.
15
+ */
16
+ export async function runTestVerify(projectDir, options) {
17
+ loadLocalEnv(projectDir);
18
+ if (!isToolEnabled(loadSentinelrc(projectDir), TOOL_NAME)) {
19
+ console.log(`Skipped: ${TOOL_NAME} is disabled in .sentinelrc.`);
20
+ return;
21
+ }
22
+ const diff = readDiffInput('test-verify --task "..."', options.diffFile);
23
+ console.log("Verifying task match…");
24
+ const result = await verifyTaskMatch(projectDir, options.task, diff);
25
+ console.log("");
26
+ console.log(VERDICT_LABELS[result.verdict]);
27
+ console.log(`Reason: ${result.reason}`);
28
+ console.log("");
29
+ console.log(`Checked by ${result.provider} (${result.model}).`);
30
+ console.log(`Result saved locally to ${SENTINEL_DIR}/${DB_FILENAME}.`);
31
+ // Non-zero exit on no_match so this command can be used in scripts later.
32
+ if (result.verdict === "no_match") {
33
+ process.exitCode = 1;
34
+ }
35
+ }
36
+ //# sourceMappingURL=testVerify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testVerify.js","sourceRoot":"","sources":["../../../src/cli/commands/testVerify.ts"],"names":[],"mappings":"AAAA,0IAA0I;AAE1I,OAAO,EAAE,eAAe,EAAE,SAAS,EAAgB,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEhE,MAAM,cAAc,GAA4B;IAC9C,KAAK,EAAE,iDAAiD;IACxD,aAAa,EAAE,gEAAgE;IAC/E,QAAQ,EAAE,oDAAoD;CAC/D,CAAC;AAOF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB,EAClB,OAA0B;IAE1B,YAAY,CAAC,UAAU,CAAC,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,YAAY,SAAS,8BAA8B,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,0BAA0B,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEzE,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAErE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,2BAA2B,YAAY,IAAI,WAAW,GAAG,CAAC,CAAC;IAEvE,0EAA0E;IAC1E,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ // Purpose: shared helper for CLI commands that accept a diff via --diff-file or piped stdin.
2
+ import { readFileSync } from "node:fs";
3
+ /**
4
+ * Reads the diff to check: from the given file path if provided, otherwise
5
+ * from piped stdin (e.g. `git diff | sentinel ...`). Throws a friendly error
6
+ * when neither is available.
7
+ */
8
+ export function readDiffInput(commandName, diffFile) {
9
+ if (diffFile) {
10
+ return readFileSync(diffFile, "utf8");
11
+ }
12
+ if (!process.stdin.isTTY) {
13
+ return readFileSync(0, "utf8");
14
+ }
15
+ throw new Error(`No diff provided. Pass one with --diff-file <path>, or pipe it in: git diff | sentinel ${commandName} ...`);
16
+ }
17
+ //# sourceMappingURL=diffInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffInput.js","sourceRoot":"","sources":["../../src/cli/diffInput.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB,EAAE,QAAiB;IAClE,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0FAA0F,WAAW,MAAM,CAC5G,CAAC;AACJ,CAAC"}