skillops-cli 0.0.0-stage → 1.2.1
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/.github/agents/address-review.agent.md +14 -0
- package/.github/agents/docs-sync.agent.md +18 -0
- package/.github/agents/pr-brief.agent.md +24 -0
- package/.github/agents/skill-auditor.agent.md +22 -0
- package/LICENSE +21 -0
- package/README.md +157 -2
- package/SKILL_CONTRACT.md +146 -0
- package/catalog/ai-engineering/agent-harness-auditor/SKILL.md +44 -0
- package/catalog/ai-engineering/agent-harness-auditor/evals/benchmark.json +22 -0
- package/catalog/ai-engineering/agent-harness-auditor/evals/evals.json +40 -0
- package/catalog/ai-engineering/agent-harness-auditor/scripts/harness_linter.py +101 -0
- package/catalog/ai-engineering/context-pruner-advisor/SKILL.md +44 -0
- package/catalog/ai-engineering/context-pruner-advisor/evals/benchmark.json +22 -0
- package/catalog/ai-engineering/context-pruner-advisor/evals/evals.json +40 -0
- package/catalog/ai-engineering/context-pruner-advisor/scripts/context_pruner.py +114 -0
- package/catalog/ai-engineering/mcp-architect/SKILL.md +45 -0
- package/catalog/ai-engineering/mcp-architect/evals/benchmark.json +22 -0
- package/catalog/ai-engineering/mcp-architect/evals/evals.json +54 -0
- package/catalog/ai-engineering/mcp-architect/scripts/validate_mcp_server.py +156 -0
- package/catalog/ai-engineering/prompt-red-teaming/SKILL.md +43 -0
- package/catalog/ai-engineering/prompt-red-teaming/evals/benchmark.json +22 -0
- package/catalog/ai-engineering/prompt-red-teaming/evals/evals.json +40 -0
- package/catalog/ai-engineering/prompt-red-teaming/scripts/injection_scanner.py +79 -0
- package/catalog/arch-quality/ast-blast-radius/SKILL.md +41 -0
- package/catalog/arch-quality/ast-blast-radius/evals/benchmark.json +22 -0
- package/catalog/arch-quality/ast-blast-radius/evals/evals.json +40 -0
- package/catalog/arch-quality/ast-blast-radius/scripts/analyze_call_graph.py +114 -0
- package/catalog/arch-quality/bug-reproducer-tdd/SKILL.md +42 -0
- package/catalog/arch-quality/bug-reproducer-tdd/evals/benchmark.json +22 -0
- package/catalog/arch-quality/bug-reproducer-tdd/evals/evals.json +40 -0
- package/catalog/arch-quality/bug-reproducer-tdd/scripts/repro_harness_check.py +129 -0
- package/catalog/arch-quality/security-sast-gate/SKILL.md +42 -0
- package/catalog/arch-quality/security-sast-gate/evals/benchmark.json +22 -0
- package/catalog/arch-quality/security-sast-gate/evals/evals.json +40 -0
- package/catalog/arch-quality/security-sast-gate/scripts/owasp_ast_scanner.py +153 -0
- package/catalog/python-backend/concurrency-race-detector/SKILL.md +43 -0
- package/catalog/python-backend/concurrency-race-detector/evals/benchmark.json +22 -0
- package/catalog/python-backend/concurrency-race-detector/evals/evals.json +40 -0
- package/catalog/python-backend/concurrency-race-detector/scripts/event_loop_blocking_scanner.py +127 -0
- package/catalog/python-backend/fastapi-production/SKILL.md +44 -0
- package/catalog/python-backend/fastapi-production/evals/benchmark.json +22 -0
- package/catalog/python-backend/fastapi-production/evals/evals.json +54 -0
- package/catalog/python-backend/fastapi-production/scripts/audit_fastapi_routes.py +169 -0
- package/catalog/python-backend/postgres-query-doctor/SKILL.md +44 -0
- package/catalog/python-backend/postgres-query-doctor/evals/benchmark.json +22 -0
- package/catalog/python-backend/postgres-query-doctor/evals/evals.json +54 -0
- package/catalog/python-backend/postgres-query-doctor/scripts/analyze_sql_safety.py +130 -0
- package/catalog/subagents/blast-radius-auditor/SUBAGENT.md +26 -0
- package/catalog/subagents/context-scout/SUBAGENT.md +23 -0
- package/catalog/subagents/red-team-breaker/SUBAGENT.md +22 -0
- package/catalog/subagents/tdd-reproducer-agent/SUBAGENT.md +23 -0
- package/catalog/subagents/token-budget-governor/SUBAGENT.md +26 -0
- package/dist/cli.js +4578 -0
- package/dist/index.d.ts +2039 -0
- package/dist/index.js +4129 -0
- package/docs/BENCHMARKS.md +82 -0
- package/package.json +84 -4
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Deterministic AST auditor for FastAPI routers and async endpoint safety."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import ast
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
_HTTP_METHODS = frozenset({"get", "post", "put", "delete", "patch"})
|
|
13
|
+
_BLOCKING_CALLS = frozenset(
|
|
14
|
+
{
|
|
15
|
+
"time.sleep",
|
|
16
|
+
"requests.get",
|
|
17
|
+
"requests.post",
|
|
18
|
+
"requests.put",
|
|
19
|
+
"requests.delete",
|
|
20
|
+
"requests.request",
|
|
21
|
+
"subprocess.run",
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _call_qualname(node: ast.Call) -> str:
|
|
27
|
+
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
|
|
28
|
+
return f"{node.func.value.id}.{node.func.attr}"
|
|
29
|
+
if isinstance(node.func, ast.Name):
|
|
30
|
+
return node.func.id
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_route_decorator(dec: ast.expr) -> ast.Call | None:
|
|
35
|
+
if (
|
|
36
|
+
isinstance(dec, ast.Call)
|
|
37
|
+
and isinstance(dec.func, ast.Attribute)
|
|
38
|
+
and dec.func.attr in _HTTP_METHODS
|
|
39
|
+
):
|
|
40
|
+
return dec
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def audit_fastapi_file(path: Path) -> list[dict[str, Any]]:
|
|
45
|
+
"""Inspect a Python file for FastAPI production hazards."""
|
|
46
|
+
source = path.read_text(encoding="utf-8")
|
|
47
|
+
tree = ast.parse(source, filename=path.as_posix())
|
|
48
|
+
findings: list[dict[str, Any]] = []
|
|
49
|
+
|
|
50
|
+
for node in ast.walk(tree):
|
|
51
|
+
if isinstance(node, ast.Call):
|
|
52
|
+
for kw in node.keywords:
|
|
53
|
+
if (
|
|
54
|
+
kw.arg == "allow_origins"
|
|
55
|
+
and isinstance(kw.value, ast.List)
|
|
56
|
+
and any(
|
|
57
|
+
isinstance(elt, ast.Constant) and elt.value == "*" for elt in kw.value.elts
|
|
58
|
+
)
|
|
59
|
+
):
|
|
60
|
+
findings.append(
|
|
61
|
+
{
|
|
62
|
+
"file": path.as_posix(),
|
|
63
|
+
"line": node.lineno,
|
|
64
|
+
"symbol": "CORSMiddleware(allow_origins=['*'])",
|
|
65
|
+
"impact": (
|
|
66
|
+
"Wildcard CORS allow_origins=['*'] detected; restrict to explicit "
|
|
67
|
+
"trusted origins in production."
|
|
68
|
+
),
|
|
69
|
+
"severity": "warning",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
74
|
+
route_calls = [
|
|
75
|
+
call
|
|
76
|
+
for dec in node.decorator_list
|
|
77
|
+
if (call := _is_route_decorator(dec)) is not None
|
|
78
|
+
]
|
|
79
|
+
if not route_calls:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
has_response_model = any(
|
|
83
|
+
any(kw.arg == "response_model" for kw in call.keywords) for call in route_calls
|
|
84
|
+
)
|
|
85
|
+
if not has_response_model and node.returns is None:
|
|
86
|
+
findings.append(
|
|
87
|
+
{
|
|
88
|
+
"file": path.as_posix(),
|
|
89
|
+
"line": node.lineno,
|
|
90
|
+
"symbol": node.name,
|
|
91
|
+
"impact": (
|
|
92
|
+
f"FastAPI route '{node.name}' is missing both 'response_model' "
|
|
93
|
+
"and an explicit return type annotation."
|
|
94
|
+
),
|
|
95
|
+
"severity": "error",
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if isinstance(node, ast.AsyncFunctionDef):
|
|
100
|
+
for child in ast.walk(node):
|
|
101
|
+
if isinstance(child, ast.Call):
|
|
102
|
+
qual = _call_qualname(child)
|
|
103
|
+
if qual in _BLOCKING_CALLS:
|
|
104
|
+
findings.append(
|
|
105
|
+
{
|
|
106
|
+
"file": path.as_posix(),
|
|
107
|
+
"line": child.lineno,
|
|
108
|
+
"symbol": f"{node.name}:{qual}",
|
|
109
|
+
"impact": (
|
|
110
|
+
f"Blocking call '{qual}()' inside async def '{node.name}' "
|
|
111
|
+
"stalls the asyncio event loop."
|
|
112
|
+
),
|
|
113
|
+
"severity": "error",
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return findings
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def main(argv: list[str] | None = None) -> int:
|
|
121
|
+
"""CLI entrypoint for audit_fastapi_routes.py."""
|
|
122
|
+
parser = argparse.ArgumentParser(description="Audit FastAPI route files.")
|
|
123
|
+
parser.add_argument("--path", required=True, help="Target Python file or directory.")
|
|
124
|
+
parser.add_argument("--json", action="store_true", dest="as_json", help="Emit JSON output.")
|
|
125
|
+
args = parser.parse_args(argv)
|
|
126
|
+
|
|
127
|
+
target_path = Path(args.path)
|
|
128
|
+
if not target_path.exists():
|
|
129
|
+
if args.as_json:
|
|
130
|
+
print(
|
|
131
|
+
json.dumps(
|
|
132
|
+
{
|
|
133
|
+
"skill": "fastapi-production",
|
|
134
|
+
"script": "audit_fastapi_routes.py",
|
|
135
|
+
"status": "error",
|
|
136
|
+
"target": target_path.as_posix(),
|
|
137
|
+
"summary": {"files_scanned": 0, "findings_count": 0},
|
|
138
|
+
"findings": [],
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
return 2
|
|
143
|
+
|
|
144
|
+
py_files = [target_path] if target_path.is_file() else sorted(target_path.rglob("*.py"))
|
|
145
|
+
all_findings: list[dict[str, Any]] = []
|
|
146
|
+
for py_file in py_files:
|
|
147
|
+
all_findings.extend(audit_fastapi_file(py_file))
|
|
148
|
+
|
|
149
|
+
status = "ok" if not all_findings else "findings_detected"
|
|
150
|
+
payload = {
|
|
151
|
+
"skill": "fastapi-production",
|
|
152
|
+
"script": "audit_fastapi_routes.py",
|
|
153
|
+
"status": status,
|
|
154
|
+
"target": target_path.as_posix(),
|
|
155
|
+
"summary": {
|
|
156
|
+
"files_scanned": len(py_files),
|
|
157
|
+
"findings_count": len(all_findings),
|
|
158
|
+
},
|
|
159
|
+
"findings": all_findings,
|
|
160
|
+
}
|
|
161
|
+
if args.as_json:
|
|
162
|
+
print(json.dumps(payload, indent=2))
|
|
163
|
+
else:
|
|
164
|
+
sys.stdout.write(f"[{status.upper()}] {len(all_findings)} finding(s) in {target_path}\n")
|
|
165
|
+
return 0 if not all_findings else 1
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "postgres-query-doctor"
|
|
3
|
+
description: "Use when diagnosing slow PostgreSQL queries, authoring SQL schema migrations, or analyzing EXPLAIN ANALYZE execution plans to detect sequential table scans, missing composite or foreign-key B-Tree indexes, unsafe non-concurrent DDL table locks, and unparameterized SQL strings across high-concurrency transactional production database workloads."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
track: "python-backend"
|
|
6
|
+
tags:
|
|
7
|
+
- "postgresql"
|
|
8
|
+
- "sql"
|
|
9
|
+
- "database"
|
|
10
|
+
- "migrations"
|
|
11
|
+
- "query-optimization"
|
|
12
|
+
routing_signals:
|
|
13
|
+
languages: ["sql", "python"]
|
|
14
|
+
files_any: ["alembic.ini", "migrations", "schema.sql", "pyproject.toml"]
|
|
15
|
+
dependencies_any: ["sqlalchemy", "asyncpg", "psycopg", "alembic"]
|
|
16
|
+
negative_triggers:
|
|
17
|
+
- "Do not trigger for pure in-memory data structures, CSS layouts, or frontend state management."
|
|
18
|
+
entrypoints:
|
|
19
|
+
scripts:
|
|
20
|
+
- path: "scripts/analyze_sql_safety.py"
|
|
21
|
+
description: "Static SQL and migration safety analyzer detecting non-concurrent index locks, SELECT *, and unbounded DELETE/UPDATE statements."
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Postgres Query Doctor (`postgres-query-doctor`)
|
|
25
|
+
|
|
26
|
+
## When to Use This Skill
|
|
27
|
+
Invoke this skill when writing or reviewing **PostgreSQL** queries, Alembic/SQL migrations, or SQLAlchemy repositories to prevent production table lockouts, sequential scan regressions, and SQL injection risks.
|
|
28
|
+
|
|
29
|
+
## Mandatory Execution Workflow
|
|
30
|
+
|
|
31
|
+
1. **Run the Deterministic SQL Safety Analyzer First:**
|
|
32
|
+
```bash
|
|
33
|
+
python catalog/python-backend/postgres-query-doctor/scripts/analyze_sql_safety.py --path <target.sql> --json
|
|
34
|
+
```
|
|
35
|
+
2. **Prevent DDL Table Lockouts in Migrations:**
|
|
36
|
+
- Always use `CREATE INDEX CONCURRENTLY` and `DROP INDEX CONCURRENTLY` on existing production tables to avoid `SHARE` locks that block writes.
|
|
37
|
+
- When adding a `NOT NULL` constraint to a large table, add a `CHECK (... IS NOT NULL) NOT VALID` constraint first, run `VALIDATE CONSTRAINT`, and only then set `NOT NULL`.
|
|
38
|
+
3. **Eliminate `SELECT *` & Unbounded Mutations:**
|
|
39
|
+
- Project explicit columns instead of `SELECT *` to enable index-only scans and prevent network payload bloat.
|
|
40
|
+
- Never execute `UPDATE` or `DELETE` without an explicit `WHERE` clause.
|
|
41
|
+
4. **Index Foreign Keys & Filter Predicates:**
|
|
42
|
+
- PostgreSQL does not automatically create an index on the referencing side of a `FOREIGN KEY`; always add a B-Tree index on foreign-key columns used in joins or cascading deletes.
|
|
43
|
+
5. **Re-Verify with the Bundled Script:**
|
|
44
|
+
Re-run `analyze_sql_safety.py --path <target.sql> --json` and confirm `"status": "ok"`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "postgres-query-doctor",
|
|
3
|
+
"runner": "deterministic-ast",
|
|
4
|
+
"timestamp": "2026-10-29T14:00:00Z",
|
|
5
|
+
"with_skill": {
|
|
6
|
+
"pass_rate_mean": 0.95,
|
|
7
|
+
"pass_rate_stddev": 0.04,
|
|
8
|
+
"tokens_mean": 2950,
|
|
9
|
+
"latency_ms_mean": 3400
|
|
10
|
+
},
|
|
11
|
+
"without_skill": {
|
|
12
|
+
"pass_rate_mean": 0.56,
|
|
13
|
+
"pass_rate_stddev": 0.13,
|
|
14
|
+
"tokens_mean": 5490,
|
|
15
|
+
"latency_ms_mean": 6400
|
|
16
|
+
},
|
|
17
|
+
"delta": {
|
|
18
|
+
"pass_rate_gain": 0.39,
|
|
19
|
+
"token_reduction_pct": -46.27,
|
|
20
|
+
"verdict": "CERTIFIED_IMPROVEMENT"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "postgres-query-doctor",
|
|
3
|
+
"schema_version": "1.0",
|
|
4
|
+
"evals": [
|
|
5
|
+
{
|
|
6
|
+
"id": "eval-01-concurrent-index-migration",
|
|
7
|
+
"name": "Convert blocking CREATE INDEX to CREATE INDEX CONCURRENTLY",
|
|
8
|
+
"prompt": "Review our PostgreSQL migration `004_add_orders_user_idx.sql` that adds an index on `orders(user_id)` in a 50M-row table and ensure zero write downtime.",
|
|
9
|
+
"fixtures": [],
|
|
10
|
+
"assertions": [
|
|
11
|
+
{
|
|
12
|
+
"id": "assert-ran-sql-safety-script",
|
|
13
|
+
"type": "tool_call_contains",
|
|
14
|
+
"target": "analyze_sql_safety.py",
|
|
15
|
+
"description": "Agent executed the deterministic SQL migration safety analyzer."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "assert-create-index-concurrently",
|
|
19
|
+
"type": "regex_match",
|
|
20
|
+
"target": "CREATE\\s+(UNIQUE\\s+)?INDEX\\s+CONCURRENTLY",
|
|
21
|
+
"description": "Migration uses CREATE INDEX CONCURRENTLY to avoid table write locks."
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "eval-02-avoid-select-star",
|
|
27
|
+
"name": "Replace SELECT * with explicit column projection for index-only scan",
|
|
28
|
+
"prompt": "Optimize the slow PostgreSQL query `SELECT * FROM audit_events WHERE tenant_id = $1 ORDER BY created_at DESC LIMIT 50`.",
|
|
29
|
+
"fixtures": [],
|
|
30
|
+
"assertions": [
|
|
31
|
+
{
|
|
32
|
+
"id": "assert-composite-index-proposed",
|
|
33
|
+
"type": "regex_match",
|
|
34
|
+
"target": "\\(tenant_id,\\s*created_at",
|
|
35
|
+
"description": "Proposed composite B-Tree index matching equality + sort predicate."
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "eval-03-safe-not-null-constraint",
|
|
41
|
+
"name": "Add NOT NULL constraint safely via NOT VALID + VALIDATE CONSTRAINT",
|
|
42
|
+
"prompt": "Write a PostgreSQL migration to enforce `NOT NULL` on `invoices.customer_id` without locking concurrent inserts for a full table scan.",
|
|
43
|
+
"fixtures": [],
|
|
44
|
+
"assertions": [
|
|
45
|
+
{
|
|
46
|
+
"id": "assert-not-valid-constraint-pattern",
|
|
47
|
+
"type": "regex_match",
|
|
48
|
+
"target": "NOT VALID[\\s\\S]*VALIDATE CONSTRAINT",
|
|
49
|
+
"description": "Used two-phase CHECK ... NOT VALID followed by VALIDATE CONSTRAINT."
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Deterministic static SQL and PostgreSQL migration safety analyzer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
_NON_CONCURRENT_INDEX = re.compile(
|
|
13
|
+
r"\bCREATE\s+(?:UNIQUE\s+)?INDEX\b(?!\s+CONCURRENTLY\b)",
|
|
14
|
+
re.IGNORECASE,
|
|
15
|
+
)
|
|
16
|
+
_SELECT_STAR = re.compile(r"\bSELECT\s+\*\s+FROM\b", re.IGNORECASE)
|
|
17
|
+
_UNBOUNDED_MUTATION = re.compile(
|
|
18
|
+
r"\b(DELETE\s+FROM|UPDATE\s+[a-zA-Z0-9_.]+(?:\s+SET\s+[^;]+)?)\s*;",
|
|
19
|
+
re.IGNORECASE,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def audit_sql_file(path: Path) -> list[dict[str, Any]]:
|
|
24
|
+
"""Inspect a .sql or migration file for PostgreSQL production hazards."""
|
|
25
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
26
|
+
findings: list[dict[str, Any]] = []
|
|
27
|
+
|
|
28
|
+
for idx, line in enumerate(lines, start=1):
|
|
29
|
+
stripped = line.strip()
|
|
30
|
+
if not stripped or stripped.startswith("--"):
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
if _NON_CONCURRENT_INDEX.search(stripped):
|
|
34
|
+
findings.append(
|
|
35
|
+
{
|
|
36
|
+
"file": path.as_posix(),
|
|
37
|
+
"line": idx,
|
|
38
|
+
"symbol": "CREATE INDEX",
|
|
39
|
+
"impact": (
|
|
40
|
+
"CREATE INDEX without CONCURRENTLY acquires a table lock that blocks "
|
|
41
|
+
"concurrent writes in production."
|
|
42
|
+
),
|
|
43
|
+
"severity": "error",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if _SELECT_STAR.search(stripped):
|
|
48
|
+
findings.append(
|
|
49
|
+
{
|
|
50
|
+
"file": path.as_posix(),
|
|
51
|
+
"line": idx,
|
|
52
|
+
"symbol": "SELECT *",
|
|
53
|
+
"impact": (
|
|
54
|
+
"SELECT * prevents PostgreSQL index-only scans and inflates wire I/O; "
|
|
55
|
+
"project explicit column names."
|
|
56
|
+
),
|
|
57
|
+
"severity": "warning",
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if _UNBOUNDED_MUTATION.search(stripped) and "WHERE" not in stripped.upper():
|
|
62
|
+
findings.append(
|
|
63
|
+
{
|
|
64
|
+
"file": path.as_posix(),
|
|
65
|
+
"line": idx,
|
|
66
|
+
"symbol": "UPDATE/DELETE without WHERE",
|
|
67
|
+
"impact": (
|
|
68
|
+
"Unbounded UPDATE or DELETE statement without a WHERE clause detected."
|
|
69
|
+
),
|
|
70
|
+
"severity": "error",
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return findings
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main(argv: list[str] | None = None) -> int:
|
|
78
|
+
"""CLI entrypoint for analyze_sql_safety.py."""
|
|
79
|
+
parser = argparse.ArgumentParser(description="Analyze PostgreSQL SQL and migration files.")
|
|
80
|
+
parser.add_argument("--path", required=True, help="Target .sql/.py file or directory.")
|
|
81
|
+
parser.add_argument("--json", action="store_true", dest="as_json", help="Emit JSON output.")
|
|
82
|
+
args = parser.parse_args(argv)
|
|
83
|
+
|
|
84
|
+
target_path = Path(args.path)
|
|
85
|
+
if not target_path.exists():
|
|
86
|
+
if args.as_json:
|
|
87
|
+
print(
|
|
88
|
+
json.dumps(
|
|
89
|
+
{
|
|
90
|
+
"skill": "postgres-query-doctor",
|
|
91
|
+
"script": "analyze_sql_safety.py",
|
|
92
|
+
"status": "error",
|
|
93
|
+
"target": target_path.as_posix(),
|
|
94
|
+
"summary": {"files_scanned": 0, "findings_count": 0},
|
|
95
|
+
"findings": [],
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
return 2
|
|
100
|
+
|
|
101
|
+
files = (
|
|
102
|
+
[target_path]
|
|
103
|
+
if target_path.is_file()
|
|
104
|
+
else sorted(list(target_path.rglob("*.sql")) + list(target_path.rglob("*.py")))
|
|
105
|
+
)
|
|
106
|
+
all_findings: list[dict[str, Any]] = []
|
|
107
|
+
for sql_file in files:
|
|
108
|
+
all_findings.extend(audit_sql_file(sql_file))
|
|
109
|
+
|
|
110
|
+
status = "ok" if not all_findings else "findings_detected"
|
|
111
|
+
payload = {
|
|
112
|
+
"skill": "postgres-query-doctor",
|
|
113
|
+
"script": "analyze_sql_safety.py",
|
|
114
|
+
"status": status,
|
|
115
|
+
"target": target_path.as_posix(),
|
|
116
|
+
"summary": {
|
|
117
|
+
"files_scanned": len(files),
|
|
118
|
+
"findings_count": len(all_findings),
|
|
119
|
+
},
|
|
120
|
+
"findings": all_findings,
|
|
121
|
+
}
|
|
122
|
+
if args.as_json:
|
|
123
|
+
print(json.dumps(payload, indent=2))
|
|
124
|
+
else:
|
|
125
|
+
sys.stdout.write(f"[{status.upper()}] {len(all_findings)} finding(s) in {target_path}\n")
|
|
126
|
+
return 0 if not all_findings else 1
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blast-radius-auditor
|
|
3
|
+
description: Use before refactoring shared functions, database models, public API contracts, or TypeScript/Python interfaces to map direct callers, transitive dependents, and breaking contract risks in an isolated context window. Do not trigger for isolated leaf-file typo fixes.
|
|
4
|
+
tools: [read, search, bash]
|
|
5
|
+
user_invocable: true
|
|
6
|
+
context_isolation: true
|
|
7
|
+
max_output_tokens: 400
|
|
8
|
+
model_tier: reasoning
|
|
9
|
+
tags: [architecture, ast, refactoring, blast-radius]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are **Blast Radius Auditor**, a pre-refactoring impact analyzer running in an isolated context window. You prevent broken callers, silent schema drift, and incomplete migrations before the parent agent edits shared code.
|
|
13
|
+
|
|
14
|
+
## Operational Protocol
|
|
15
|
+
1. **Deterministic Graph Scan:** If `catalog/arch-quality/ast-blast-radius/scripts/analyze_call_graph.py` is present and the target is Python, run it with `--json`. For TypeScript/Node or polyglot targets, search all imports, type references, re-exports, and test fixtures referencing the target symbol.
|
|
16
|
+
2. **Classify Impact Tiers:**
|
|
17
|
+
- **Direct Callers:** Files invoking or instantiating the symbol directly.
|
|
18
|
+
- **Transitive Dependents:** Downstream routers, CLI commands, MCP handlers, or serializers coupled to the return type or error contract.
|
|
19
|
+
- **Test & Contract Coverage:** Existing unit/integration tests covering the symbol.
|
|
20
|
+
|
|
21
|
+
## Mandatory Output Contract (`<= 400 tokens`)
|
|
22
|
+
Report strictly:
|
|
23
|
+
- **Risk Level:** `LOW` | `MEDIUM` | `HIGH` | `CRITICAL`
|
|
24
|
+
- **Direct Callers (`file:line` + required signature/type update)**
|
|
25
|
+
- **Contract & Serialization Boundaries Affected**
|
|
26
|
+
- **Safe Refactoring Order:** Ordered checklist (`1..N`) so the parent agent updates definitions, callers, and tests without breaking compilation.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-scout
|
|
3
|
+
description: Use when exploring an unfamiliar codebase or tracing cross-module data flows to locate exact file line ranges, symbol signatures, and architectural boundaries in an isolated context window without polluting the parent agent session. Do not trigger for editing files or running destructive commands.
|
|
4
|
+
tools: [read, search]
|
|
5
|
+
user_invocable: true
|
|
6
|
+
context_isolation: true
|
|
7
|
+
max_output_tokens: 350
|
|
8
|
+
model_tier: fast
|
|
9
|
+
tags: [exploration, token-economy, cartography, read-only]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are **Context Scout**, a read-only codebase cartographer operating inside an isolated child context window. Your mission is to absorb heavy multi-file grep and read exploration so the parent agent never wastes tokens reading irrelevant files.
|
|
13
|
+
|
|
14
|
+
## Operational Protocol
|
|
15
|
+
1. **Targeted Search First:** Use `search` to locate symbol definitions, exports, route handlers, and schema contracts before reading full files.
|
|
16
|
+
2. **Slice-Only Inspection:** Read only the relevant line ranges around target symbols rather than dumping entire files into your response.
|
|
17
|
+
3. **Zero Mutation:** Never propose raw multi-hundred-line code blocks or modify files.
|
|
18
|
+
|
|
19
|
+
## Mandatory Output Contract (`<= 350 tokens`)
|
|
20
|
+
Return a compact Markdown briefing containing strictly:
|
|
21
|
+
- **Entrypoints & Primary Symbols:** `path/to/file.ext:L<start>-L<end>` — exact function/class/type signature and 1-line responsibility.
|
|
22
|
+
- **Data & Call Flow:** `Caller -> Callee -> Storage/External Boundary` in at most 5 bullet points.
|
|
23
|
+
- **Recommended Read Slices for Parent Agent:** The exact `2–4` file line ranges the parent agent should read to execute the change safely.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: red-team-breaker
|
|
3
|
+
description: Use when stress-testing architectural plans, pull request diffs, authentication flows, or LLM prompt pipelines to uncover race conditions, security bypasses, injection vectors, and edge-case regressions. Do not trigger for routine documentation formatting.
|
|
4
|
+
tools: [read, search]
|
|
5
|
+
user_invocable: true
|
|
6
|
+
context_isolation: true
|
|
7
|
+
max_output_tokens: 400
|
|
8
|
+
model_tier: reasoning
|
|
9
|
+
tags: [security, red-team, concurrency, adversarial-review]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are **Red-Team Breaker**, an adversarial security and reliability reviewer operating in a read-only isolated context window. You assume the proposed design or diff will fail in production and systematically hunt for the exact failure mechanism.
|
|
13
|
+
|
|
14
|
+
## Attack Surface Checklist
|
|
15
|
+
1. **Security & Trust Boundaries:** SQL/command/path-traversal injection, SSRF, missing auth/tenant checks, wildcard tool permissions (`["*"]`), or indirect prompt injection in untrusted inputs.
|
|
16
|
+
2. **Concurrency & State Hazards:** TOCTOU file/database races, unawaited promises, event-loop blocking I/O, non-idempotent retries, or lock contention.
|
|
17
|
+
3. **Silent Failure & Data Corruption:** Swallowed exceptions, partial writes without atomic rollback/lockfiles, or schema drift between producer and consumer.
|
|
18
|
+
|
|
19
|
+
## Mandatory Output Contract (`<= 400 tokens`)
|
|
20
|
+
List only concrete, reproducible findings (`0` fluff):
|
|
21
|
+
- **`[CRITICAL | HIGH | MEDIUM]` `file:line` — Failure Mode:** Exact exploit or trigger input -> broken invariant -> concrete 1–3 line countermeasure.
|
|
22
|
+
- **Verdict:** `BLOCKED_BY_FINDINGS` or `HARDENED_PASS` (if zero exploitable flaws exist).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-reproducer-agent
|
|
3
|
+
description: Use when diagnosing a bug report, stack trace, or failing CI check to isolate the root cause and synthesize a minimal deterministic failing reproduction test (Red phase) before any production code is modified. Do not trigger for greenfield feature scaffolding.
|
|
4
|
+
tools: [read, search, bash]
|
|
5
|
+
user_invocable: true
|
|
6
|
+
context_isolation: true
|
|
7
|
+
max_output_tokens: 400
|
|
8
|
+
model_tier: reasoning
|
|
9
|
+
tags: [tdd, debugging, bug-reproduction, root-cause]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are **TDD Reproducer Agent**, a fault-localization specialist operating in an isolated context window. You turn stack traces and bug reports into a verified root-cause hypothesis and a minimal failing test specification (`Red` phase) without mutating production code.
|
|
13
|
+
|
|
14
|
+
## Operational Protocol
|
|
15
|
+
1. **Trace Execution Backwards:** Start from the crash site, assertion failure, or unexpected output and trace inputs backwards to the first violated invariant.
|
|
16
|
+
2. **Isolate Minimal Trigger:** Identify the smallest input payload, CLI invocation, or state transition that deterministically reproduces the failure offline (zero external APIs or network calls).
|
|
17
|
+
3. **Design the `Red` Assertion:** Formulate the exact Vitest or Pytest test case that fails on current `HEAD` for the right reason and will pass once the root cause is fixed.
|
|
18
|
+
|
|
19
|
+
## Mandatory Output Contract (`<= 400 tokens`)
|
|
20
|
+
Return strictly:
|
|
21
|
+
- **Root Cause (`file:line`):** Exact condition causing the invariant failure.
|
|
22
|
+
- **Minimal Reproduction Snippet:** Compact (`<= 20 lines`) deterministic test case (`Red` phase) targeting the existing test suite.
|
|
23
|
+
- **Surgical Fix Boundary:** Which function/branch in production code must change and what invariant must hold (`Green` phase).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: token-budget-governor
|
|
3
|
+
description: Use when auditing repository Startup Token Tax across CLAUDE.md, AGENTS.md, GEMINI.md, Cursor rules, SKILL.md catalogs, and MCP tool schemas to eliminate context window bloat and enforce progressive disclosure budgets. Do not trigger for application business logic changes.
|
|
4
|
+
tools: [read, search, bash]
|
|
5
|
+
user_invocable: true
|
|
6
|
+
context_isolation: true
|
|
7
|
+
max_output_tokens: 400
|
|
8
|
+
model_tier: fast
|
|
9
|
+
tags: [token-economy, governance, progressive-disclosure, mcp]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are **Token Budget Governor**, a specialist subagent dedicated to eliminating Startup Token Tax and enforcing **SkillOps** 3-level progressive disclosure budgets (`INV-SKILL-01` & `INV-SKILL-04`).
|
|
13
|
+
|
|
14
|
+
## Operational Protocol
|
|
15
|
+
1. **Execute Deterministic Token Audit:** Run `npx skillops@latest tokens --format json` and `npx skillops@latest audit --strict --format json` when available, or inspect always-on root instruction files (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/*.mdc`, `.mcp.json`).
|
|
16
|
+
2. **Identify Token Hotspots:**
|
|
17
|
+
- Always-on root instruction files exceeding `600 tokens` or duplicating prose without `@AGENTS.md` delegation.
|
|
18
|
+
- Cursor `.mdc` rules with `alwaysApply: true` or missing `globs` filters.
|
|
19
|
+
- `SKILL.md` files exceeding `160` L1 description tokens or `250` L2 body lines, or embedding `> 40` lines of static code blocks that belong in `scripts/` or `references/`.
|
|
20
|
+
- Over-provisioned `.mcp.json` servers (> 5 active servers).
|
|
21
|
+
|
|
22
|
+
## Mandatory Output Contract (`<= 400 tokens`)
|
|
23
|
+
Return a prioritized remediation table:
|
|
24
|
+
- **Current Startup Token Tax vs. `2,500`-Token Budget (`PASS` / `OVER_BUDGET`)**
|
|
25
|
+
- **Top Token Waste Hotspots:** `file` | `estimated_tokens` | `wasted_tokens` | `surgical fix (L1/L2/L3 split or @AGENTS.md delegation)`
|
|
26
|
+
- **Projected Token Savings (`tokens` and `%`)** after applying the fixes.
|