@misterhuydo/sentinel 1.5.48 → 1.5.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.5.48",
3
+ "version": "1.5.50",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -173,7 +173,12 @@ fetch_from_properties() {
173
173
 
174
174
  # Build remote command
175
175
  local remote_cmd="sudo cat $remote_path 2>/dev/null"
176
- [[ -n "$GREP_FILTER" ]] && remote_cmd+=" | grep -E '$GREP_FILTER'"
176
+ if [[ -n "$GREP_FILTER" ]]; then
177
+ local grep_ctx=""
178
+ [[ -n "$SENTINEL_GREP_BEFORE" ]] && grep_ctx+=" -B $SENTINEL_GREP_BEFORE"
179
+ [[ -n "$SENTINEL_GREP_AFTER" ]] && grep_ctx+=" -A $SENTINEL_GREP_AFTER"
180
+ remote_cmd+=" | grep -E$grep_ctx '$GREP_FILTER'"
181
+ fi
177
182
  [[ -n "$GREP_EXCLUDE" ]] && remote_cmd+=" | grep -iv '$GREP_EXCLUDE'"
178
183
  [[ -n "$TAIL" ]] && remote_cmd+=" | tail -n $TAIL"
179
184
  [[ -n "$HEAD" ]] && [[ -z "$TAIL" ]] && remote_cmd+=" | head -n $HEAD"
@@ -1 +1 @@
1
- __version__ = "1.5.48"
1
+ __version__ = "1.5.50"
@@ -518,9 +518,9 @@ Never pad responses. Never say "Great question!" or "Certainly!".
518
518
  If you don't know something, use a tool to find out before saying you don't know.
519
519
 
520
520
  Response length:
521
- - Log fetch/filter/search results: one line summary + the data. No bullet lists explaining
522
- what the result means unless the user asked for analysis. No "this means X", no "once Y
523
- happens you'll see Z". Just: "3 matches:" or "No matches for `provision/phone` in SSOLWA."
521
+ - Log fetch/filter/search/tail results: return ONLY the matches (or "No matches for X in Y.").
522
+ Nothing else. No health info, no explanations, no "this means", no "next steps", no context.
523
+ The user reads the log lines themselves do not interpret them.
524
524
  - Status/health data: one line. Don't re-list every field from the JSON.
525
525
  - Actions (fix, merge, release): brief confirmation of what happened.
526
526
 
@@ -1091,6 +1091,14 @@ _TOOLS = [
1091
1091
  "description": "Case-sensitive match (default false)",
1092
1092
  "default": False,
1093
1093
  },
1094
+ "before": {
1095
+ "type": "integer",
1096
+ "description": "Lines of context before each match (like grep -B)",
1097
+ },
1098
+ "after": {
1099
+ "type": "integer",
1100
+ "description": "Lines of context after each match (like grep -A)",
1101
+ },
1094
1102
  },
1095
1103
  "required": ["query"],
1096
1104
  },
@@ -1211,6 +1219,14 @@ _TOOLS = [
1211
1219
  "Use when searching for INFO-level or feature-specific patterns."
1212
1220
  ),
1213
1221
  },
1222
+ "before": {
1223
+ "type": "integer",
1224
+ "description": "Lines of context before each match on remote server (grep -B)",
1225
+ },
1226
+ "after": {
1227
+ "type": "integer",
1228
+ "description": "Lines of context after each match on remote server (grep -A)",
1229
+ },
1214
1230
  },
1215
1231
  },
1216
1232
  },
@@ -2895,6 +2911,8 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2895
2911
  since_hours = inputs.get("since_hours")
2896
2912
  max_matches = int(inputs.get("max_matches", 300))
2897
2913
  case_flag = 0 if inputs.get("case_sensitive") else _re.IGNORECASE
2914
+ ctx_before = int(inputs.get("before", 0))
2915
+ ctx_after = int(inputs.get("after", 0))
2898
2916
  try:
2899
2917
  pat = _re.compile(query_f, case_flag)
2900
2918
  except _re.error as e:
@@ -2941,7 +2959,8 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2941
2959
  continue
2942
2960
  try:
2943
2961
  lines = log_file.read_text(encoding="utf-8", errors="replace").splitlines()
2944
- for line in lines:
2962
+ seen_indices = set()
2963
+ for i, line in enumerate(lines):
2945
2964
  if not pat.search(line):
2946
2965
  continue
2947
2966
  if cutoff:
@@ -2949,7 +2968,14 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2949
2968
  ts = _parse_line_ts(line)
2950
2969
  if ts and ts < cutoff:
2951
2970
  continue
2952
- all_matches.append((label, line[:300]))
2971
+ # Emit context window (before + match + after), no duplicates
2972
+ start = max(0, i - ctx_before)
2973
+ end = min(len(lines), i + ctx_after + 1)
2974
+ for j in range(start, end):
2975
+ if j not in seen_indices:
2976
+ seen_indices.add(j)
2977
+ prefix = ">" if j == i else " "
2978
+ all_matches.append((label, f"{prefix} {lines[j][:300]}"))
2953
2979
  sources_hit.add(label)
2954
2980
  if len(all_matches) >= max_matches:
2955
2981
  break
@@ -3133,6 +3159,9 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
3133
3159
  shutil.rmtree(temp_base)
3134
3160
  temp_base.mkdir(parents=True, exist_ok=True)
3135
3161
 
3162
+ ctx_before = int(inputs.get("before", 0))
3163
+ ctx_after = int(inputs.get("after", 0))
3164
+
3136
3165
  results = []
3137
3166
  for props in props_files:
3138
3167
  env = os.environ.copy()
@@ -3140,6 +3169,10 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
3140
3169
  env["TAIL"] = str(tail_override)
3141
3170
  if grep_override:
3142
3171
  env["SENTINEL_GREP_FILTER_OVERRIDE"] = grep_override
3172
+ if ctx_before:
3173
+ env["SENTINEL_GREP_BEFORE"] = str(ctx_before)
3174
+ if ctx_after:
3175
+ env["SENTINEL_GREP_AFTER"] = str(ctx_after)
3143
3176
  if use_temp:
3144
3177
  # Tell fetch_log.sh where to write output files
3145
3178
  env["OUTPUT_DIR"] = str(temp_base)