@raishin/vanguard-frontier-agentic 3.1.1 → 3.2.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.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gcp-firebase-developer
3
3
  description: "Build, configure, and operate Firebase-powered web and mobile applications — covering Firestore, Firebase Auth, Firebase Hosting, Cloud Functions for Firebase, Firebase Storage, App Check, Firebase Remote Config, and Firebase Analytics. Use when building mobile/web apps with Firebase, setting up authentication flows, designing Firestore data models, deploying Firebase Hosting, or configuring Firebase security rules."
4
- allowed-tools: Read Grep Glob
4
+ allowed-tools: Read Grep Glob Bash(npm install:*) Bash(firebase:*)
5
5
  metadata:
6
6
  author: "github: Raishin"
7
7
  version: "0.1.0"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gcp-gke-platform-operator
3
3
  description: Operate GKE clusters (Standard and Autopilot), manage node pools, configure Workload Identity, enforce Binary Authorization, plan node pool upgrades, and review cluster security posture.
4
- allowed-tools: Read Grep Glob
4
+ allowed-tools: Read Grep Glob Bash(gcloud container:*) Bash(kubectl apply:*)
5
5
  metadata:
6
6
  author: "github: Raishin"
7
7
  version: "0.2.0"
@@ -2,7 +2,7 @@
2
2
  name: salesforce-agentforce-stdm-observer-skill
3
3
  description: "Queries Salesforce Telemetry & Data Management (STDM) and Data Cloud for live Agentforce session traces, faithfulness scores, answer relevance scores, action invocation telemetry, and quality metrics under T1 least-privilege scope (api + refresh_token + cdp_query_api). Answers the production observability question: \"is my Agentforce agent working correctly right now?\" Operational counterpart to the static-review salesforce-agentforce-risk-review-skill. TRIGGER when: user asks for Agentforce session metrics, faithfulness scores, answer relevance, AI Evaluation results, action telemetry, STDM queries, agent performance KPIs, hallucination rates. Trigger phrases: \"how is my agent performing\", \"show STDM data\", \"agent observability\", \"agent telemetry\", \"AiAgentTagAssociation\", \"AiEvaluationDefinition results\", \"agentforce production metrics\". DO NOT TRIGGER when: user wants static configuration review (use salesforce-agentforce-risk-review-skill); when modifying agent configurations (T3 — escalate to salesforce-live-guard-agent); when Agent Script authoring is needed (out of Wave 4 scope, route to forcedotcom/sf-skills developing-agentforce reference)."
4
4
  license: MIT
5
- allowed-tools: Bash(sf data query:*) Bash(sf agent test:*) Bash(sf org display:*) Read Grep Glob
5
+ allowed-tools: Bash(sf data query:*) Bash(sf agent test:*) Bash(sf org display:*) Read Grep Glob Bash(sf api request:*) Bash(sf apex run:*)
6
6
  metadata:
7
7
  author: "github: Raishin"
8
8
  version: 0.1.0
@@ -2,7 +2,7 @@
2
2
  name: salesforce-apex-log-analyzer-skill
3
3
  description: "Retrieves and analyzes Apex debug logs from a connected Salesforce org to identify governor-limit hits, SOQL N+1 patterns, unhandled exceptions, and async job failures. T1 read-only runtime — retrieves logs only, never executes code or mutates data. TRIGGER when: user asks to analyze an Apex log, debug a trigger failure, diagnose a governor limit hit, interpret a stack trace from a Salesforce org, or review a DEBUG log for performance issues. Trigger phrases: analyze apex log, debug this trigger, why is my trigger failing, governor limit hit, DEBUG log analysis, check my log file. DO NOT TRIGGER when: user wants to run live tests (use salesforce-apex-test-runner-skill), static code review without logs (use salesforce-apex-lwc-code-review-skill), generating new Apex code (use salesforce-apex-generator-skill), or Agentforce session telemetry (use salesforce-agentforce-stdm-observer-skill)."
4
4
  license: MIT
5
- allowed-tools: Bash(sf apex get log:*) Bash(sf apex tail log:*) Bash(sf data query:*) Read Grep Glob
5
+ allowed-tools: Bash(sf apex get log:*) Bash(sf apex tail log:*) Bash(sf data query:*) Read Grep Glob Bash(sf org display:*)
6
6
  metadata:
7
7
  author: "github: Raishin"
8
8
  version: "0.1.0"
@@ -98,7 +98,7 @@ sf apex run test \
98
98
  --test-level RunLocalTests \
99
99
  --target-org <alias> \
100
100
  --result-format json \
101
- --wait 30
101
+ --wait 30 \
102
102
  --code-coverage
103
103
  ```
104
104
 
@@ -2,7 +2,7 @@
2
2
  name: salesforce-soql-explorer-skill
3
3
  description: "Executes read-only SOQL queries against a connected Salesforce org via the sf data query CLI under T1 least-privilege scope (api + refresh_token only, Run As service account with no ModifyAllData/ViewAllData/ViewEncryptedData). Returns sanitized JSON with a structured audit envelope. Live operational counterpart to the static-review skills. TRIGGER when: user asks to query records, run SOQL, fetch live data, inspect records by ID, count records, run aggregate queries, or check field values in a live org. Trigger phrases: query my org, run SOQL, show me records where, how many opportunities, what is the value of field X on record Y. DO NOT TRIGGER when: user pastes a metadata XML export for static review (use salesforce-metadata-review-skill); request requires DML — write, update, delete — those are T3 prohibited; bulk data operations needed (use salesforce-bulk-data-ops-skill); only schema metadata needed without data (use salesforce-metadata-fetcher-skill)."
4
4
  license: MIT
5
- allowed-tools: Bash(sf data query:*) Bash(sf org list:*) Bash(sf org display:*) Read Grep Glob
5
+ allowed-tools: Bash(sf data query:*) Bash(sf org list:*) Bash(sf org display:*) Read Grep Glob Bash(sf sobject describe:*) Bash(jq:*)
6
6
  metadata:
7
7
  author: "github: Raishin"
8
8
  version: "0.1.0"
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env python3
2
+ """Validate that every SKILL.md's shell examples are covered by its allowed-tools.
3
+
4
+ Every fenced shell command a SKILL.md demonstrates must be covered by its
5
+ declared `allowed-tools` frontmatter — least-privilege declarations must
6
+ match the skill's own examples. A skill that shows `gcloud container ...`
7
+ but only declares `Read Grep Glob` is under-declared (Claude Code would
8
+ need a Bash grant it never asked for); a skill that declares bare `Bash`
9
+ while only ever running `npm test` is over-declared. This gate catches the
10
+ under-declared case: real command examples with no matching `Bash`/
11
+ `Bash(pattern)` coverage.
12
+
13
+ Scope: only fenced code blocks whose info string is EXACTLY one of
14
+ `bash`, `sh`, `shell`, or `console` are scanned. Untagged (plain ```)
15
+ blocks are deliberately out of scope — they are not reliably shell
16
+ examples (could be pseudo-code, transcripts, etc.) and are not a signal
17
+ the harness's `Bash(...)` permission grammar can be checked against
18
+ anyway. Only `skills/**/SKILL.md` is scanned; `references/*.md` files are
19
+ out of scope per the task spec (SKILL.md is the harness-facing contract
20
+ that carries `allowed-tools`; references are supporting docs).
21
+
22
+ Reuses `parse_frontmatter` and `tokenize_allowed_tools` from
23
+ validate-skill-allowed-tools.py so the two gates never disagree about
24
+ what a token is.
25
+
26
+ Command extraction (per fenced block):
27
+ - iterate physical lines; strip a leading "$ " prompt
28
+ - join trailing-backslash line continuations into one logical line
29
+ - skip blank lines and lines starting with "#"
30
+ - split compound lines on `&&`, `||`, `;`, `|` into segments, tracking
31
+ single-/double-quote state so an operator character *inside* a quoted
32
+ string (e.g. the `|` in a `jq '.a[] | .b'` filter) is not mistaken for
33
+ a shell pipeline operator — this parser is quote-depth-aware only; it
34
+ does not handle backslash-escaped quotes inside single-quoted spans or
35
+ heredocs, which is a correct simplification for the shell one-liners
36
+ this repo's SKILL.md files demonstrate
37
+ - for each segment, skip leading VAR=value assignment words, then take
38
+ the command token
39
+ - ignore shell builtins/environment ops that never need a Bash grant:
40
+ export cd echo printf set unset source . true false exit pushd popd
41
+ - everything else is a command segment that needs `allowed-tools` coverage
42
+
43
+ Coverage semantics: a bare `Bash` token in allowed-tools covers every
44
+ segment. Each `Bash(inner)` token becomes a regex matcher applied to the
45
+ whitespace-normalized full command segment: escape `inner`, turn a
46
+ trailing "<literal>:*" suffix into "<literal>(\\s.*)?$" (command-plus-
47
+ any-args semantics), turn every other escaped `*` into `.*`, and anchor
48
+ at the start. A segment is covered if ANY matcher matches. If a skill
49
+ shows command segments but declares no Bash-ish token at all, every
50
+ segment is a violation.
51
+ """
52
+
53
+ from __future__ import annotations
54
+
55
+ import importlib.util
56
+ import os
57
+ import re
58
+ import sys
59
+ from pathlib import Path
60
+
61
+ ROOT = Path(__file__).resolve().parents[1]
62
+ SKILLS_DIR = ROOT / "skills"
63
+
64
+ # ── reuse the sibling gate's frontmatter/token parsing (don't fork it) ──────
65
+ _vsat_spec = importlib.util.spec_from_file_location(
66
+ "vsat", os.path.join(os.path.dirname(__file__), "validate-skill-allowed-tools.py")
67
+ )
68
+ assert _vsat_spec is not None and _vsat_spec.loader is not None
69
+ vsat = importlib.util.module_from_spec(_vsat_spec)
70
+ _vsat_spec.loader.exec_module(vsat)
71
+
72
+ FENCE_LANGS = {"bash", "sh", "shell", "console"}
73
+
74
+ # Shell builtins / environment ops that never need a Bash allowed-tools grant.
75
+ IGNORE_BUILTINS = {
76
+ "export",
77
+ "cd",
78
+ "echo",
79
+ "printf",
80
+ "set",
81
+ "unset",
82
+ "source",
83
+ ".",
84
+ "true",
85
+ "false",
86
+ "exit",
87
+ "pushd",
88
+ "popd",
89
+ }
90
+
91
+ ASSIGNMENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
92
+ FENCE_OPEN_RE = re.compile(r"^(`{3,}|~{3,})\s*([A-Za-z0-9_+-]*)")
93
+
94
+ # Cap on '*' wildcards per Bash(inner) token. Each '*' becomes a regex `.*`;
95
+ # an unbounded chain of `.*` wildcards (e.g. Bash(a*a*a*...*a)) can
96
+ # catastrophically backtrack on a near-miss command and hang the gate. 12 is
97
+ # generous for any realistic allowed-tools pattern in this repo.
98
+ MAX_WILDCARDS = 12
99
+
100
+
101
+ def split_segments(text: str) -> list[str]:
102
+ """Split on &&, ||, ;, | — but never inside a single- or double-quoted span.
103
+
104
+ A bare regex split on `|` would fracture a real compound pipeline like
105
+ `sf data query ... | jq '.a[] | .b'` at the `|` *inside* the quoted jq
106
+ filter too, producing a bogus non-command fragment. Shell operators are
107
+ only operators outside quotes, so track quote state while scanning.
108
+ """
109
+ segments: list[str] = []
110
+ buf: list[str] = []
111
+ in_single = False
112
+ in_double = False
113
+ i = 0
114
+ n = len(text)
115
+ while i < n:
116
+ ch = text[i]
117
+ if in_single:
118
+ buf.append(ch)
119
+ if ch == "'":
120
+ in_single = False
121
+ i += 1
122
+ continue
123
+ if in_double:
124
+ buf.append(ch)
125
+ if ch == "\\" and i + 1 < n:
126
+ buf.append(text[i + 1])
127
+ i += 1
128
+ elif ch == '"':
129
+ in_double = False
130
+ i += 1
131
+ continue
132
+ if ch == "'":
133
+ in_single = True
134
+ buf.append(ch)
135
+ i += 1
136
+ continue
137
+ if ch == '"':
138
+ in_double = True
139
+ buf.append(ch)
140
+ i += 1
141
+ continue
142
+ if text[i : i + 2] in ("&&", "||"):
143
+ segments.append("".join(buf))
144
+ buf = []
145
+ i += 2
146
+ continue
147
+ if ch in (";", "|"):
148
+ segments.append("".join(buf))
149
+ buf = []
150
+ i += 1
151
+ continue
152
+ buf.append(ch)
153
+ i += 1
154
+ segments.append("".join(buf))
155
+ return segments
156
+
157
+
158
+ def extract_allowed_tools_tokens(text: str) -> list[str]:
159
+ """Return the raw allowed-tools tokens for a SKILL.md's frontmatter."""
160
+ fm = vsat.parse_frontmatter(text)
161
+ if fm is None:
162
+ return []
163
+ raw = fm.get("allowed-tools", "").strip()
164
+ if not raw:
165
+ return []
166
+ if raw.startswith("[") and raw.endswith("]"):
167
+ # Bracketed-list variant, parsed the same way
168
+ # validate-skill-frontmatter-schema.py parses inline YAML sequences.
169
+ inner = raw[1:-1].strip()
170
+ return [t.strip().strip("'\"") for t in inner.split(",") if t.strip()]
171
+ return vsat.tokenize_allowed_tools(raw)
172
+
173
+
174
+ def compile_bash_matcher(inner: str) -> re.Pattern[str]:
175
+ """Turn a Bash(inner) constraint into a regex matcher on the full segment."""
176
+ if inner.count("*") > MAX_WILDCARDS:
177
+ raise ValueError(
178
+ f"Bash(...) inner pattern has too many '*' wildcards "
179
+ f"(>{MAX_WILDCARDS}), refusing to compile (ReDoS risk): {inner!r}"
180
+ )
181
+ escaped = re.escape(inner)
182
+ for suffix in ("\\:\\*", ":\\*"):
183
+ if escaped.endswith(suffix):
184
+ escaped = escaped[: -len(suffix)] + r"(\s.*)?$"
185
+ break
186
+ escaped = escaped.replace(r"\*", ".*")
187
+ return re.compile("^" + escaped)
188
+
189
+
190
+ def build_bash_matchers(tokens: list[str]) -> tuple[bool, list[re.Pattern[str]]]:
191
+ """Return (has_bare_bash, [compiled Bash(...) matchers])."""
192
+ has_bare_bash = False
193
+ matchers: list[re.Pattern[str]] = []
194
+ for tok in tokens:
195
+ if tok == "Bash":
196
+ has_bare_bash = True
197
+ elif tok.startswith("Bash(") and tok.endswith(")"):
198
+ matchers.append(compile_bash_matcher(tok[len("Bash(") : -1]))
199
+ return has_bare_bash, matchers
200
+
201
+
202
+ def normalize(segment: str) -> str:
203
+ return " ".join(segment.split())
204
+
205
+
206
+ def iter_fenced_blocks(lines: list[str]):
207
+ """Yield (info_string_lower, [(lineno, raw_line), ...]) for each fence."""
208
+ i = 0
209
+ n = len(lines)
210
+ while i < n:
211
+ m = FENCE_OPEN_RE.match(lines[i])
212
+ if not m:
213
+ i += 1
214
+ continue
215
+ fence_char = m.group(1)[0]
216
+ fence_min_len = len(m.group(1))
217
+ info = m.group(2).lower()
218
+ i += 1
219
+ body: list[tuple[int, str]] = []
220
+ while i < n:
221
+ stripped = lines[i].strip()
222
+ if stripped and set(stripped) == {fence_char} and len(stripped) >= fence_min_len:
223
+ i += 1
224
+ break
225
+ body.append((i + 1, lines[i]))
226
+ i += 1
227
+ yield info, body
228
+
229
+
230
+ def extract_command_segments(block_lines: list[tuple[int, str]]) -> list[tuple[int, str]]:
231
+ """Return [(lineno, segment_text), ...] for segments that need coverage."""
232
+ results: list[tuple[int, str]] = []
233
+ i = 0
234
+ n = len(block_lines)
235
+ while i < n:
236
+ start_lineno, first_line = block_lines[i]
237
+ text = first_line.strip()
238
+ if text.startswith("$ "):
239
+ text = text[2:]
240
+ elif text == "$":
241
+ text = ""
242
+
243
+ # Join trailing-backslash continuations into one logical line.
244
+ while text.rstrip().endswith("\\") and i + 1 < n:
245
+ text = text.rstrip()[:-1].rstrip()
246
+ i += 1
247
+ _, next_line = block_lines[i]
248
+ text = (text + " " + next_line.strip()).strip()
249
+
250
+ i += 1
251
+
252
+ if not text or text.startswith("#"):
253
+ continue
254
+
255
+ for raw_seg in split_segments(text):
256
+ seg = normalize(raw_seg)
257
+ if not seg:
258
+ continue
259
+ words = seg.split(" ")
260
+ idx = 0
261
+ while idx < len(words) and ASSIGNMENT_RE.match(words[idx]):
262
+ idx += 1
263
+ if idx >= len(words):
264
+ continue # segment was only VAR=value assignments
265
+ command_token = words[idx]
266
+ if command_token in IGNORE_BUILTINS:
267
+ continue
268
+ results.append((start_lineno, " ".join(words[idx:])))
269
+ return results
270
+
271
+
272
+ def scan_skill(
273
+ skill_md: Path,
274
+ ) -> tuple[bool, int, list[tuple[int, str]], str | None]:
275
+ """Return (has_shell_block, segment_count, [(lineno, segment), ...] violations, error).
276
+
277
+ `error` is None unless the skill's allowed-tools declares a Bash(...)
278
+ token that fails to compile (e.g. too many '*' wildcards — see
279
+ MAX_WILDCARDS). In that case the other fields are unpopulated
280
+ (False, 0, []) and the caller must treat this skill as a hard failure
281
+ rather than silently skipping or crashing on it.
282
+ """
283
+ text = skill_md.read_text(encoding="utf-8")
284
+ lines = text.splitlines()
285
+ tokens = extract_allowed_tools_tokens(text)
286
+ try:
287
+ has_bare_bash, matchers = build_bash_matchers(tokens)
288
+ except ValueError as exc:
289
+ return False, 0, [], f"{skill_md}: {exc}"
290
+
291
+ has_shell_block = False
292
+ segment_count = 0
293
+ violations: list[tuple[int, str]] = []
294
+
295
+ for info, body in iter_fenced_blocks(lines):
296
+ if info not in FENCE_LANGS:
297
+ continue
298
+ has_shell_block = True
299
+ for lineno, seg in extract_command_segments(body):
300
+ segment_count += 1
301
+ if has_bare_bash:
302
+ continue
303
+ if any(m.match(seg) for m in matchers):
304
+ continue
305
+ violations.append((lineno, seg))
306
+
307
+ return has_shell_block, segment_count, violations, None
308
+
309
+
310
+ def main() -> int:
311
+ skill_files = sorted(SKILLS_DIR.glob("*/*/SKILL.md"))
312
+ if not skill_files:
313
+ print("ERROR: no SKILL.md files found", file=sys.stderr)
314
+ return 2
315
+
316
+ skills_with_blocks = 0
317
+ total_segments = 0
318
+ total_violations = 0
319
+ skills_with_violations = 0
320
+ violation_lines: list[str] = []
321
+ scan_errors: list[str] = []
322
+
323
+ for skill_md in skill_files:
324
+ has_shell_block, segment_count, violations, error = scan_skill(skill_md)
325
+ if error is not None:
326
+ scan_errors.append(error)
327
+ continue
328
+ if has_shell_block:
329
+ skills_with_blocks += 1
330
+ total_segments += segment_count
331
+ if violations:
332
+ skills_with_violations += 1
333
+ total_violations += len(violations)
334
+ for lineno, seg in violations:
335
+ violation_lines.append(
336
+ f"{skill_md}:{lineno}: command not covered by allowed-tools: {seg}"
337
+ )
338
+
339
+ if scan_errors:
340
+ for line in scan_errors:
341
+ print(f"ERROR: {line}", file=sys.stderr)
342
+
343
+ if violation_lines:
344
+ for line in violation_lines:
345
+ print(line, file=sys.stderr)
346
+ print(
347
+ f"ERROR: {total_violations} uncovered command(s) in "
348
+ f"{skills_with_violations} skill(s); fix the skill's allowed-tools "
349
+ f"(narrowest matching pattern), never delete the command.",
350
+ file=sys.stderr,
351
+ )
352
+
353
+ if scan_errors or violation_lines:
354
+ return 1
355
+
356
+ print(
357
+ f"OK: skill coherence — {skills_with_blocks} skill(s) with shell blocks, "
358
+ f"{total_segments} command segment(s), all covered"
359
+ )
360
+ return 0
361
+
362
+
363
+ if __name__ == "__main__":
364
+ sys.exit(main())