@misterhuydo/sentinel 1.0.67 → 1.0.69
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-03-
|
|
1
|
+
2026-03-22T16:24:09.877Z
|
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-22T16:
|
|
3
|
-
"checkpoint_at": "2026-03-22T16:
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-22T16:26:17.040Z",
|
|
3
|
+
"checkpoint_at": "2026-03-22T16:26:17.042Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -100,11 +100,10 @@ What you can do (tools available):
|
|
|
100
100
|
process. Safe to run at any time — no restart if already up to date.
|
|
101
101
|
e.g. "upgrade sentinel", "update sentinel", "upgrade yourself"
|
|
102
102
|
|
|
103
|
-
19.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"what does the cairn backend do?", "scan elprint for security issues"
|
|
103
|
+
19. ask_codebase — Ask any natural-language question about a managed repo's codebase.
|
|
104
|
+
Claude Code answers using its full knowledge of the code.
|
|
105
|
+
e.g. "what does the 1881 backend do?", "find PIN validation in elprint",
|
|
106
|
+
"any TODOs in cairn?", "are there security issues in elprint-sales?"
|
|
108
107
|
|
|
109
108
|
20. restart_project — Stop and restart a specific project instance (stop.sh + start.sh).
|
|
110
109
|
e.g. "restart 1881", "reboot elprint", "restart the cairn project"
|
|
@@ -125,11 +124,8 @@ reply with a short summary grouped by category:
|
|
|
125
124
|
• `search_logs` — live SSH grep on production servers — "search logs for illegal PIN in 1881"
|
|
126
125
|
• `tail_log` — last N lines of a log source, no filter — "show recent SSOLWA logs"
|
|
127
126
|
|
|
128
|
-
*Codebase
|
|
129
|
-
• `
|
|
130
|
-
• `query_codebase search` — find classes/functions by concept — "find PIN validation in elprint"
|
|
131
|
-
• `query_codebase outline` — full structure + issue detection — "outline the cairn repo"
|
|
132
|
-
• `query_codebase security` — vulnerability scan — "scan elprint for security issues"
|
|
127
|
+
*Codebase questions*
|
|
128
|
+
• `ask_codebase` — any question about a repo's code — "what does 1881 do?", "find PIN validation", "any TODOs?", "security issues?"
|
|
133
129
|
|
|
134
130
|
*Fix management*
|
|
135
131
|
• `get_fix_details` — full details of a specific fix — "show fix abc123"
|
|
@@ -456,36 +452,28 @@ _TOOLS = [
|
|
|
456
452
|
"input_schema": {"type": "object", "properties": {}},
|
|
457
453
|
},
|
|
458
454
|
{
|
|
459
|
-
"name": "
|
|
455
|
+
"name": "ask_codebase",
|
|
460
456
|
"description": (
|
|
461
|
-
"
|
|
462
|
-
"
|
|
463
|
-
"
|
|
464
|
-
"'
|
|
465
|
-
"'
|
|
466
|
-
"
|
|
467
|
-
"'summarize the cairn repo', 'scan elprint-sales for security issues', "
|
|
468
|
-
"'what classes handle auth in 1881?'"
|
|
457
|
+
"Ask any natural-language question about a managed repo's codebase. "
|
|
458
|
+
"Claude Code answers using its full codebase knowledge — no need to specify how. "
|
|
459
|
+
"Use for any codebase question: 'what does the 1881 backend do?', "
|
|
460
|
+
"'find PIN validation code in elprint', 'are there TODOs in cairn?', "
|
|
461
|
+
"'what classes handle auth?', 'any security issues in elprint-sales?', "
|
|
462
|
+
"'summarize the architecture of 1881', 'show me the dependency graph'."
|
|
469
463
|
),
|
|
470
464
|
"input_schema": {
|
|
471
465
|
"type": "object",
|
|
472
466
|
"properties": {
|
|
473
467
|
"repo": {
|
|
474
468
|
"type": "string",
|
|
475
|
-
"description": "Repo name (partial match, e.g. '1881', 'elprint-sales')",
|
|
469
|
+
"description": "Repo name (partial match, e.g. '1881', 'elprint-sales', 'cairn')",
|
|
476
470
|
},
|
|
477
|
-
"
|
|
471
|
+
"question": {
|
|
478
472
|
"type": "string",
|
|
479
|
-
"
|
|
480
|
-
"description": "describe=summarize, search=find by concept, outline=structure+issues, security=vuln scan",
|
|
481
|
-
"default": "describe",
|
|
482
|
-
},
|
|
483
|
-
"query": {
|
|
484
|
-
"type": "string",
|
|
485
|
-
"description": "For 'search': the concept or symbol to look for (e.g. 'PIN validation', 'AuthService')",
|
|
473
|
+
"description": "Natural language question about the codebase",
|
|
486
474
|
},
|
|
487
475
|
},
|
|
488
|
-
"required": ["repo"],
|
|
476
|
+
"required": ["repo", "question"],
|
|
489
477
|
},
|
|
490
478
|
},
|
|
491
479
|
{
|
|
@@ -1045,59 +1033,29 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
1045
1033
|
"note": "Upgrade started — pulling latest version via npm and restarting. Give me ~30 seconds then I'll be back.",
|
|
1046
1034
|
})
|
|
1047
1035
|
|
|
1048
|
-
if name == "
|
|
1036
|
+
if name == "ask_codebase":
|
|
1049
1037
|
repo_name = inputs.get("repo", "").lower()
|
|
1050
|
-
|
|
1051
|
-
query = inputs.get("query", "")
|
|
1038
|
+
question = inputs.get("question", "")
|
|
1052
1039
|
|
|
1053
|
-
# Match repo by name
|
|
1054
1040
|
repo_cfg = next(
|
|
1055
1041
|
(r for rn, r in cfg_loader.repos.items() if repo_name in rn.lower()),
|
|
1056
1042
|
None,
|
|
1057
1043
|
)
|
|
1058
1044
|
if not repo_cfg:
|
|
1059
|
-
available
|
|
1060
|
-
return json.dumps({"error": f"Repo '{repo_name}' not found", "available": available})
|
|
1045
|
+
return json.dumps({"error": f"Repo '{repo_name}' not found", "available": list(cfg_loader.repos.keys())})
|
|
1061
1046
|
|
|
1062
1047
|
local_path = Path(repo_cfg.local_path)
|
|
1063
1048
|
if not local_path.exists():
|
|
1064
1049
|
return json.dumps({"error": f"Repo not cloned yet at {local_path}. Run pull_repo first."})
|
|
1065
1050
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
f"its purpose, main architecture, key classes/services, and tech stack. "
|
|
1072
|
-
f"Be concise but informative. Output plain text only."
|
|
1073
|
-
)
|
|
1074
|
-
elif operation == "search":
|
|
1075
|
-
if not query:
|
|
1076
|
-
return json.dumps({"error": "query is required for operation=search"})
|
|
1077
|
-
prompt = (
|
|
1078
|
-
f"You are analysing the codebase at: {local_path}\n"
|
|
1079
|
-
f"Use the cairn_search MCP tool to find '{query}'. "
|
|
1080
|
-
f"List matching classes, functions, files, and a brief description of each. "
|
|
1081
|
-
f"Output plain text only."
|
|
1082
|
-
)
|
|
1083
|
-
elif operation == "outline":
|
|
1084
|
-
prompt = (
|
|
1085
|
-
f"You are analysing the codebase at: {local_path}\n"
|
|
1086
|
-
f"Use the cairn_outline MCP tool to give a full structural overview. "
|
|
1087
|
-
f"Include key modules, packages, detected issues (god classes, gaps, etc.). "
|
|
1088
|
-
f"Output plain text only."
|
|
1089
|
-
)
|
|
1090
|
-
elif operation == "security":
|
|
1091
|
-
prompt = (
|
|
1092
|
-
f"You are analysing the codebase at: {local_path}\n"
|
|
1093
|
-
f"Use the cairn_security MCP tool to scan for vulnerabilities. "
|
|
1094
|
-
f"List findings grouped by severity. Output plain text only."
|
|
1095
|
-
)
|
|
1096
|
-
else:
|
|
1097
|
-
return json.dumps({"error": f"Unknown operation '{operation}'"})
|
|
1051
|
+
prompt = (
|
|
1052
|
+
f"You are a code analyst. Answer the following question about the codebase at: {local_path}\n\n"
|
|
1053
|
+
f"Question: {question}\n\n"
|
|
1054
|
+
f"Use whatever tools you need to answer accurately. Be concise and direct. Plain text only."
|
|
1055
|
+
)
|
|
1098
1056
|
|
|
1099
|
-
cfg
|
|
1100
|
-
env
|
|
1057
|
+
cfg = cfg_loader.sentinel
|
|
1058
|
+
env = os.environ.copy()
|
|
1101
1059
|
if cfg.anthropic_api_key:
|
|
1102
1060
|
env["ANTHROPIC_API_KEY"] = cfg.anthropic_api_key
|
|
1103
1061
|
|
|
@@ -1113,13 +1071,10 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
1113
1071
|
"error": f"`claude --print` failed (rc={r.returncode})",
|
|
1114
1072
|
"stderr": (r.stderr or "").strip()[:300],
|
|
1115
1073
|
})
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
"operation": operation,
|
|
1119
|
-
"result": output[:4000],
|
|
1120
|
-
})
|
|
1074
|
+
logger.info("Boss ask_codebase %s rc=%d len=%d", repo_cfg.repo_name, r.returncode, len(output))
|
|
1075
|
+
return json.dumps({"repo": repo_cfg.repo_name, "answer": output[:4000]})
|
|
1121
1076
|
except subprocess.TimeoutExpired:
|
|
1122
|
-
return json.dumps({"error": "
|
|
1077
|
+
return json.dumps({"error": "Codebase query timed out after 180s"})
|
|
1123
1078
|
except Exception as e:
|
|
1124
1079
|
return json.dumps({"error": str(e)})
|
|
1125
1080
|
|
|
@@ -318,9 +318,15 @@ async def _dispatch(event: dict, client, cfg_loader, store) -> None:
|
|
|
318
318
|
|
|
319
319
|
# ── Turn processor ────────────────────────────────────────────────────────────
|
|
320
320
|
|
|
321
|
+
_MAX_HISTORY_TURNS = 20 # keep last 20 exchanges (~40 messages) to stay well within context limits
|
|
322
|
+
|
|
321
323
|
async def _run_turn(session: _Session, message: str, client, cfg_loader, store) -> None:
|
|
322
324
|
channel = session.channel
|
|
323
325
|
|
|
326
|
+
# Trim history to avoid context overflow on long conversations
|
|
327
|
+
if len(session.history) > _MAX_HISTORY_TURNS * 2:
|
|
328
|
+
session.history = session.history[-(_MAX_HISTORY_TURNS * 2):]
|
|
329
|
+
|
|
324
330
|
# Typing indicator
|
|
325
331
|
await _post(client, channel, "_thinking..._")
|
|
326
332
|
|