@misterhuydo/sentinel 1.6.2 → 1.6.4

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/.cairn/.hint-lock CHANGED
@@ -1 +1 @@
1
- 2026-04-24T10:50:33.264Z
1
+ 2026-04-24T12:01:49.183Z
@@ -1,6 +1,6 @@
1
1
  {
2
- "message": "Auto-checkpoint at 2026-04-24T11:12:56.361Z",
3
- "checkpoint_at": "2026-04-24T11:12:56.362Z",
2
+ "message": "Auto-checkpoint at 2026-04-24T12:05:54.299Z",
3
+ "checkpoint_at": "2026-04-24T12:05:54.301Z",
4
4
  "active_files": [
5
5
  "J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js",
6
6
  "J:\\Projects\\Sentinel\\cli\\lib\\test.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -1 +1 @@
1
- __version__ = "1.6.2"
1
+ __version__ = "1.6.4"
@@ -220,10 +220,11 @@ def _build_prompt(
220
220
  " BOSS_ESCALATE: <description of what needs to change in Sentinel>",
221
221
  " This escalates to Patch, the Sentinel dev agent, who will implement it.",
222
222
  "",
223
- "FINAL STEP checkpoint cairn",
224
- "10. Before you stop, call the `cairn_checkpoint` MCP tool with a one-line summary",
225
- " of what you changed (e.g. \"Broaden FirstName regex; bump consumer to 2.7.1\").",
226
- " This lets the next session resume with full context of prior fixes.",
223
+ "CRITICAL — your FINAL message must be the patch itself",
224
+ " The unified diff (with the `# Affected repos:` header for multi-repo) MUST be",
225
+ " the very last text you emit. Do NOT call any tool (including cairn_checkpoint)",
226
+ " after the diff Sentinel only reads the FINAL assistant message and a trailing",
227
+ " tool call leaves the result field empty.",
227
228
  ]
228
229
  return "\n".join(lines_out)
229
230
 
@@ -262,6 +263,9 @@ def _is_auth_error(output: str) -> bool:
262
263
  return any(hint in low for hint in _AUTH_ERROR_HINTS)
263
264
 
264
265
 
266
+ _CAIRN_ONLY_MCP_CONFIG = '{"mcpServers":{"cairn":{"command":"cairn-mcp"}}}'
267
+
268
+
265
269
  def _claude_cmd(
266
270
  bin_path: str,
267
271
  prompt: str,
@@ -278,6 +282,14 @@ def _claude_cmd(
278
282
  and MUST be False for the OAuth attempt (otherwise claude refuses to read
279
283
  the cached `claude login` token). The caller picks per attempt.
280
284
 
285
+ For the OAuth path (use_bare=False) we ALSO pass:
286
+ --setting-sources project,local (skip user settings.json — bypasses the
287
+ cairn `minify` / `edit-guard` PreToolUse hooks that block Read/Edit
288
+ with `exit 2` and force Claude to fall back to hand-crafting diffs)
289
+ --mcp-config '{...cairn...}' (re-add cairn MCP tools that we just
290
+ bypassed by skipping user settings — the prompt's cairn_checkpoint
291
+ instruction needs them)
292
+
281
293
  Output is forced to `--output-format json` so the caller can extract the
282
294
  session_id, cost, and result text deterministically.
283
295
  """
@@ -291,6 +303,10 @@ def _claude_cmd(
291
303
  cmd.append("--bare")
292
304
  if skip:
293
305
  cmd.append("--dangerously-skip-permissions")
306
+ if not use_bare:
307
+ # OAuth-mode isolation: skip user-scope cairn hooks, re-load cairn MCP.
308
+ cmd += ["--setting-sources", "project,local"]
309
+ cmd += ["--mcp-config", _CAIRN_ONLY_MCP_CONFIG]
294
310
  if session_id:
295
311
  cmd += ["--resume", session_id]
296
312
  cmd += ["--output-format", "json", "--print", prompt]
@@ -51,3 +51,40 @@ def test_print_and_prompt_are_last():
51
51
  cmd = _claude_cmd("claude", "the actual prompt", use_bare=False)
52
52
  assert cmd[-2] == "--print"
53
53
  assert cmd[-1] == "the actual prompt"
54
+
55
+
56
+ # ── OAuth-mode isolation (skip user settings, keep cairn MCP) ─────────────────
57
+
58
+ def test_oauth_mode_skips_user_settings_to_avoid_cairn_hooks():
59
+ """Cairn PreToolUse hooks live in user settings.json and block Read/Edit
60
+ for OAuth mode. --setting-sources project,local bypasses them."""
61
+ cmd = _claude_cmd("claude", "x", use_bare=False)
62
+ assert "--setting-sources" in cmd
63
+ i = cmd.index("--setting-sources")
64
+ assert cmd[i + 1] == "project,local"
65
+
66
+
67
+ def test_bare_mode_does_not_set_setting_sources():
68
+ """--bare already skips hooks; no need for --setting-sources, and avoiding
69
+ it keeps the cmd surface identical to legacy behaviour."""
70
+ cmd = _claude_cmd("claude", "x", use_bare=True)
71
+ assert "--setting-sources" not in cmd
72
+
73
+
74
+ def test_oauth_mode_loads_cairn_mcp_via_inline_config():
75
+ """When user settings are skipped, cairn MCP tools must be re-enabled
76
+ explicitly via --mcp-config so the prompt's cairn_checkpoint instruction works."""
77
+ cmd = _claude_cmd("claude", "x", use_bare=False)
78
+ # Either --mcp-config <json> or --mcp-config=<json>
79
+ found = False
80
+ for i, tok in enumerate(cmd):
81
+ if tok == "--mcp-config" and i + 1 < len(cmd) and "cairn" in cmd[i + 1]:
82
+ found = True; break
83
+ if tok.startswith("--mcp-config=") and "cairn" in tok:
84
+ found = True; break
85
+ assert found, f"--mcp-config with cairn missing from cmd: {cmd}"
86
+
87
+
88
+ def test_bare_mode_does_not_set_mcp_config():
89
+ cmd = _claude_cmd("claude", "x", use_bare=True)
90
+ assert not any(tok.startswith("--mcp-config") for tok in cmd)