torusguard 0.9.4 → 1.0.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.
- package/.torusguard/.manifest.json +15 -10
- package/.torusguard/config/torusguard.json +1 -1
- package/.torusguard/schemas/memory-context.schema.json +95 -0
- package/.torusguard/schemas/memory-event.schema.json +70 -0
- package/.torusguard/schemas/memory-pattern.schema.json +65 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
- package/.torusguard/scripts/diff_guard.py +71 -11
- package/.torusguard/scripts/finding_scorer.py +171 -86
- package/.torusguard/scripts/manifest_builder.py +2 -0
- package/.torusguard/scripts/memory_engine.py +912 -0
- package/.torusguard/scripts/run_manager.py +171 -89
- package/.torusguard/skills/torusguard/SKILL.md +12 -7
- package/.torusguard/skills/torusguard/bootstrap.py +344 -56
- package/.torusguard/skills/torusguard-audit/SKILL.md +25 -25
- package/.torusguard/skills/torusguard-harden/SKILL.md +3 -3
- package/.torusguard/skills/torusguard-recheck/SKILL.md +3 -3
- package/.torusguard/workflows/memory.md +53 -0
- package/README.md +12 -2
- package/bin/torusguard.js +173 -43
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +4 -3
- package/skills/torusguard/__pycache__/bootstrap.cpython-311.pyc +0 -0
- package/skills/torusguard/bootstrap.py +244 -80
- package/skills/torusguard/payload/.manifest.json +15 -10
- package/skills/torusguard/payload/config/torusguard.json +1 -1
- package/skills/torusguard/payload/schemas/memory-context.schema.json +95 -0
- package/skills/torusguard/payload/schemas/memory-event.schema.json +70 -0
- package/skills/torusguard/payload/schemas/memory-pattern.schema.json +65 -0
- package/skills/torusguard/payload/scripts/diff_guard.py +71 -11
- package/skills/torusguard/payload/scripts/finding_scorer.py +171 -86
- package/skills/torusguard/payload/scripts/manifest_builder.py +2 -0
- package/skills/torusguard/payload/scripts/memory_engine.py +912 -0
- package/skills/torusguard/payload/scripts/run_manager.py +171 -89
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +12 -7
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +344 -56
- package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +25 -25
- package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +3 -3
- package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +3 -3
- package/skills/torusguard/payload/workflows/memory.md +53 -0
- package/skills/torusguard-audit/SKILL.md +25 -25
- package/skills/torusguard-harden/SKILL.md +3 -3
- package/skills/torusguard-recheck/SKILL.md +3 -3
|
@@ -10,7 +10,6 @@ import sys
|
|
|
10
10
|
import shutil
|
|
11
11
|
import json
|
|
12
12
|
import argparse
|
|
13
|
-
import subprocess
|
|
14
13
|
from pathlib import Path
|
|
15
14
|
|
|
16
15
|
# Ensure UTF-8 stdout/stderr on Windows consoles
|
|
@@ -25,6 +24,19 @@ if sys.stderr and hasattr(sys.stderr, "reconfigure"):
|
|
|
25
24
|
except Exception:
|
|
26
25
|
pass
|
|
27
26
|
|
|
27
|
+
# ─── ANSI Color Helpers ───────────────────────────────────────────────────────
|
|
28
|
+
BOLD = "\033[1m"
|
|
29
|
+
DIM = "\033[2m"
|
|
30
|
+
RESET = "\033[0m"
|
|
31
|
+
CYAN = "\033[36m"
|
|
32
|
+
GREEN = "\033[32m"
|
|
33
|
+
YELLOW = "\033[33m"
|
|
34
|
+
WHITE = "\033[97m"
|
|
35
|
+
GRAY = "\033[90m"
|
|
36
|
+
RED = "\033[31m"
|
|
37
|
+
BG_GREEN = "\033[42m"
|
|
38
|
+
BG_CYAN = "\033[46m"
|
|
39
|
+
|
|
28
40
|
|
|
29
41
|
def find_project_root(start_dir=None):
|
|
30
42
|
"""Detect project root directory by searching for standard repo root markers."""
|
|
@@ -45,6 +57,114 @@ def find_project_root(start_dir=None):
|
|
|
45
57
|
return current
|
|
46
58
|
|
|
47
59
|
|
|
60
|
+
def print_header():
|
|
61
|
+
"""Print the branded TorusGuard header card."""
|
|
62
|
+
print(f"""
|
|
63
|
+
{CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}
|
|
64
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
65
|
+
{CYAN}│{RESET} {BOLD}{WHITE}🛡️ T O R U S G U A R D{RESET} {GRAY}v0.9.5{RESET} {CYAN}│{RESET}
|
|
66
|
+
{CYAN}│{RESET} {DIM}Autonomous Security Engine for AI-Built Applications{RESET} {CYAN}│{RESET}
|
|
67
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
68
|
+
{CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}
|
|
69
|
+
""")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def print_step_1_assets(file_count):
|
|
73
|
+
"""Print the enhanced Step 1 asset unpacking card."""
|
|
74
|
+
print(f""" {CYAN}┌─ {BOLD}Step 1/3{RESET} {CYAN}──────────────────────── {WHITE}Unpacking Governance Assets{RESET} {CYAN}──────┐{RESET}
|
|
75
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
76
|
+
{CYAN}│{RESET} {GREEN}✔{RESET} Canonical security rules {DIM}71 rules across 11 families{RESET} {CYAN}│{RESET}
|
|
77
|
+
{CYAN}│{RESET} {GREEN}✔{RESET} JSON validation schemas {DIM}8 formal schemas{RESET} {CYAN}│{RESET}
|
|
78
|
+
{CYAN}│{RESET} {GREEN}✔{RESET} Specialist agent specifications {DIM}5 isolated agents{RESET} {CYAN}│{RESET}
|
|
79
|
+
{CYAN}│{RESET} {GREEN}✔{RESET} Workflow templates {DIM}11 slash commands{RESET} {CYAN}│{RESET}
|
|
80
|
+
{CYAN}│{RESET} {GREEN}✔{RESET} Framework reference guides {DIM}10 security guides{RESET} {CYAN}│{RESET}
|
|
81
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
82
|
+
{CYAN}│{RESET} {BOLD}{WHITE}{file_count} files unpacked{RESET} {CYAN}│{RESET}
|
|
83
|
+
{CYAN}└───────────────────────────────────────────────────────────────────────┘{RESET}
|
|
84
|
+
""")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def print_step_2_profile(detected_stack=None):
|
|
88
|
+
"""Print the security profile & coverage card (in-process stack alignment)."""
|
|
89
|
+
if detected_stack and detected_stack.get("framework") and detected_stack.get("framework") != "None":
|
|
90
|
+
fw = str(detected_stack.get("framework"))
|
|
91
|
+
lang = str(detected_stack.get("language", ""))
|
|
92
|
+
label = f"Auto-aligned stack: {fw} ({lang})"
|
|
93
|
+
pad = " " * max(0, 66 - len(label))
|
|
94
|
+
status_line = f" {CYAN}│{RESET} {GREEN}✔{RESET} {BOLD}Auto-aligned stack:{RESET} {WHITE}{fw}{RESET} {DIM}({lang}){RESET}{pad}{CYAN}│{RESET}"
|
|
95
|
+
else:
|
|
96
|
+
status_line = f" {CYAN}│{RESET} {DIM}ℹ Stack auto-detected on first /torusguard-audit run{RESET} {CYAN}│{RESET}"
|
|
97
|
+
|
|
98
|
+
print(f""" {CYAN}┌─ {BOLD}Step 2/3{RESET} {CYAN}──────────────────── {WHITE}Security Profile & Coverage{RESET} {CYAN}────────┐{RESET}
|
|
99
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
100
|
+
{CYAN}│{RESET} {BOLD}Rule Families:{RESET} {CYAN}│{RESET}
|
|
101
|
+
{CYAN}│{RESET} {YELLOW}TG-SEC{RESET} Secrets & Credentials {YELLOW}TG-DB{RESET} Database Safety {CYAN}│{RESET}
|
|
102
|
+
{CYAN}│{RESET} {YELLOW}TG-INPUT{RESET} Input Validation {YELLOW}TG-AUTH{RESET} Authentication {CYAN}│{RESET}
|
|
103
|
+
{CYAN}│{RESET} {YELLOW}TG-CLIENT{RESET} Client Bundle Leaks {YELLOW}TG-DIFF{RESET} Diff Inspection {CYAN}│{RESET}
|
|
104
|
+
{CYAN}│{RESET} {YELLOW}TG-AGENT{RESET} AI Agent Security {YELLOW}TG-EDGE{RESET} Serverless {CYAN}│{RESET}
|
|
105
|
+
{CYAN}│{RESET} {YELLOW}TG-SUPPLY{RESET} Supply Chain & CI/CD {YELLOW}TG-SSRF{RESET} Outbound Net {CYAN}│{RESET}
|
|
106
|
+
{CYAN}│{RESET} {YELLOW}TG-BIZ{RESET} Business Logic {CYAN}│{RESET}
|
|
107
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
108
|
+
{CYAN}│{RESET} {BOLD}Supported Stacks:{RESET} {CYAN}│{RESET}
|
|
109
|
+
{CYAN}│{RESET} {GREEN}Python{RESET} Django {DIM}·{RESET} FastAPI {DIM}·{RESET} Flask {DIM}·{RESET} DRF {DIM}·{RESET} SQLAlchemy {CYAN}│{RESET}
|
|
110
|
+
{CYAN}│{RESET} {GREEN}Node.js{RESET} Next.js {DIM}·{RESET} Express {DIM}·{RESET} React {DIM}·{RESET} Supabase {DIM}·{RESET} Firebase {CYAN}│{RESET}
|
|
111
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
112
|
+
{status_line}
|
|
113
|
+
{CYAN}└───────────────────────────────────────────────────────────────────────┘{RESET}
|
|
114
|
+
""")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def print_step_3_bridges(registered_ides):
|
|
118
|
+
"""Print the IDE command registration card."""
|
|
119
|
+
lines = ""
|
|
120
|
+
for r in registered_ides:
|
|
121
|
+
lines += f" {CYAN}│{RESET} {GREEN}✔{RESET} {r:<67} {CYAN}│{RESET}\n"
|
|
122
|
+
|
|
123
|
+
print(f""" {CYAN}┌─ {BOLD}Step 3/3{RESET} {CYAN}─────────────────── {WHITE}AI IDE Command Registration{RESET} {CYAN}─────────┐{RESET}
|
|
124
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
125
|
+
{lines} {CYAN}│{RESET} {CYAN}│{RESET}
|
|
126
|
+
{CYAN}└───────────────────────────────────────────────────────────────────────┘{RESET}
|
|
127
|
+
""")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def print_success_card():
|
|
131
|
+
"""Print the final success card with next steps."""
|
|
132
|
+
print(f""" {GREEN}╔═══════════════════════════════════════════════════════════════════════╗{RESET}
|
|
133
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
134
|
+
{GREEN}║{RESET} {GREEN}{BOLD}✅ WORKSPACE INITIALIZED SUCCESSFULLY{RESET} {GREEN}║{RESET}
|
|
135
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
136
|
+
{GREEN}╠═══════════════════════════════════════════════════════════════════════╣{RESET}
|
|
137
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
138
|
+
{GREEN}║{RESET} {BOLD}Next Steps:{RESET} {GREEN}║{RESET}
|
|
139
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
140
|
+
{GREEN}║{RESET} {CYAN}1.{RESET} In AI Chat {WHITE}/torusguard-audit{RESET} or {WHITE}/torusguard{RESET} {GREEN}║{RESET}
|
|
141
|
+
{GREEN}║{RESET} {CYAN}2.{RESET} In Terminal {WHITE}npx torusguard status{RESET} {GREEN}║{RESET}
|
|
142
|
+
{GREEN}║{RESET} {CYAN}3.{RESET} In CI/CD {WHITE}npx torusguard audit{RESET} {GREEN}║{RESET}
|
|
143
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
144
|
+
{GREEN}║{RESET} {DIM}Docs{RESET} {CYAN}https://github.com/githubmofo/TorusGuard{RESET} {GREEN}║{RESET}
|
|
145
|
+
{GREEN}║{RESET} {DIM}NPM{RESET} {CYAN}https://npmjs.com/package/torusguard{RESET} {GREEN}║{RESET}
|
|
146
|
+
{GREEN}║{RESET} {GREEN}║{RESET}
|
|
147
|
+
{GREEN}╚═══════════════════════════════════════════════════════════════════════╝{RESET}
|
|
148
|
+
""")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def print_already_initialized(target_root, cfg):
|
|
152
|
+
"""Print the already-initialized status card."""
|
|
153
|
+
print(f"""
|
|
154
|
+
{CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}
|
|
155
|
+
{CYAN}│{RESET} {BOLD}{WHITE}🛡️ TORUSGUARD WORKSPACE{RESET} {GREEN}[Active]{RESET} {CYAN}│{RESET}
|
|
156
|
+
{CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}
|
|
157
|
+
|
|
158
|
+
{BOLD}▸ Project Root:{RESET} {GREEN}{target_root}{RESET}
|
|
159
|
+
{BOLD}▸ Workspace:{RESET} {GREEN}.torusguard/{RESET} {DIM}(Already Initialized){RESET}
|
|
160
|
+
{BOLD}▸ Version:{RESET} {CYAN}{cfg.get('version', '1.0.0')}{RESET}
|
|
161
|
+
{BOLD}▸ Severity Floor:{RESET} {YELLOW}{cfg.get('severity_threshold', 'medium')}{RESET}
|
|
162
|
+
|
|
163
|
+
{DIM}To refresh templates or re-scaffold, run:{RESET}
|
|
164
|
+
{CYAN}npx torusguard init --force{RESET}
|
|
165
|
+
""")
|
|
166
|
+
|
|
167
|
+
|
|
48
168
|
def scaffold_workspace(target_root=None, force=False, full_commands=False):
|
|
49
169
|
"""Scaffold the .torusguard workspace into the target project root."""
|
|
50
170
|
target_root = Path(target_root or find_project_root()).resolve()
|
|
@@ -53,40 +173,25 @@ def scaffold_workspace(target_root=None, force=False, full_commands=False):
|
|
|
53
173
|
payload_dir = script_dir / "payload"
|
|
54
174
|
|
|
55
175
|
if torusguard_target.exists() and not force:
|
|
56
|
-
print("""
|
|
57
|
-
╭──────────────────────────────────────────────────────────────────────────╮
|
|
58
|
-
│ 🛡️ TORUSGUARD WORKSPACE STATUS [Active] │
|
|
59
|
-
╰──────────────────────────────────────────────────────────────────────────╯
|
|
60
|
-
""")
|
|
61
|
-
print(f" ▸ Project Root: {target_root}")
|
|
62
|
-
print(f" ▸ Workspace: .torusguard/ (Already Initialized)")
|
|
63
176
|
config_file = torusguard_target / "config" / "torusguard.json"
|
|
177
|
+
cfg = {}
|
|
64
178
|
if config_file.exists():
|
|
65
179
|
try:
|
|
66
180
|
with open(config_file, "r", encoding="utf-8") as f:
|
|
67
181
|
cfg = json.load(f)
|
|
68
|
-
print(f" ▸ Version: {cfg.get('version', '0.9.3')}")
|
|
69
|
-
print(f" ▸ Severity Floor: {cfg.get('severity_threshold', 'medium')}")
|
|
70
182
|
except Exception:
|
|
71
183
|
pass
|
|
72
|
-
|
|
73
|
-
💡 Workspace already exists. To refresh templates or re-scaffold, run:
|
|
74
|
-
npx torusguard init --force
|
|
75
|
-
""")
|
|
184
|
+
print_already_initialized(target_root, cfg)
|
|
76
185
|
return True
|
|
77
186
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
╰──────────────────────────────────────────────────────────────────────────╯
|
|
83
|
-
""")
|
|
84
|
-
print(f" ▸ Target Project: {target_root}")
|
|
85
|
-
print(f" ▸ Destination: {torusguard_target}")
|
|
187
|
+
# ── Header ──────────────────────────────────────────────────────────────
|
|
188
|
+
print_header()
|
|
189
|
+
print(f" {BOLD}▸ Target Project:{RESET} {WHITE}{target_root}{RESET}")
|
|
190
|
+
print(f" {BOLD}▸ Destination:{RESET} {WHITE}{torusguard_target}{RESET}")
|
|
86
191
|
|
|
87
|
-
# 1
|
|
192
|
+
# ── Step 1: Unpack Payload ──────────────────────────────────────────────
|
|
193
|
+
file_count = 93
|
|
88
194
|
if payload_dir.is_dir():
|
|
89
|
-
print("\n ▸ Step 1/3: Unpacking Offline Governance Assets...")
|
|
90
195
|
try:
|
|
91
196
|
if torusguard_target.exists() and force:
|
|
92
197
|
shutil.rmtree(torusguard_target)
|
|
@@ -95,22 +200,25 @@ def scaffold_workspace(target_root=None, force=False, full_commands=False):
|
|
|
95
200
|
torusguard_target,
|
|
96
201
|
ignore=shutil.ignore_patterns("runs", "*.pyc", "__pycache__", ".git*")
|
|
97
202
|
)
|
|
98
|
-
|
|
203
|
+
# Count actual files
|
|
204
|
+
file_count = sum(1 for _ in torusguard_target.rglob("*") if _.is_file())
|
|
99
205
|
except Exception as e:
|
|
100
|
-
print(f"
|
|
206
|
+
print(f"\n {RED}✖ Failed to unpack payload: {e}{RESET}")
|
|
101
207
|
return False
|
|
102
208
|
else:
|
|
103
209
|
# Fallback: create base structure
|
|
104
|
-
print("\n1. Bundled payload not found. Creating core skeleton...")
|
|
105
210
|
subdirs = [
|
|
106
211
|
"config", "agents", "workflows", "scripts", "templates",
|
|
107
212
|
"schemas", "references", "rules/active", "runs"
|
|
108
213
|
]
|
|
109
214
|
for s in subdirs:
|
|
110
215
|
(torusguard_target / s).mkdir(parents=True, exist_ok=True)
|
|
111
|
-
|
|
216
|
+
file_count = 0
|
|
112
217
|
|
|
113
|
-
|
|
218
|
+
print()
|
|
219
|
+
print_step_1_assets(file_count)
|
|
220
|
+
|
|
221
|
+
# ── Ensure runs and rules/active directories exist ──────────────────────
|
|
114
222
|
runs_dir = torusguard_target / "runs"
|
|
115
223
|
runs_dir.mkdir(parents=True, exist_ok=True)
|
|
116
224
|
gitkeep_runs = runs_dir / ".gitkeep"
|
|
@@ -123,50 +231,115 @@ def scaffold_workspace(target_root=None, force=False, full_commands=False):
|
|
|
123
231
|
if not gitkeep_rules.exists():
|
|
124
232
|
gitkeep_rules.write_text("", encoding="utf-8")
|
|
125
233
|
|
|
126
|
-
#
|
|
127
|
-
|
|
234
|
+
# ── Ensure memory subsystem is initialized (v1.0.0) ────────────────────
|
|
235
|
+
memory_dir = torusguard_target / "memory"
|
|
236
|
+
memory_dir.mkdir(parents=True, exist_ok=True)
|
|
237
|
+
events_dir = memory_dir / "events"
|
|
238
|
+
events_dir.mkdir(parents=True, exist_ok=True)
|
|
239
|
+
|
|
240
|
+
gitkeep_events = events_dir / ".gitkeep"
|
|
241
|
+
if not gitkeep_events.exists():
|
|
242
|
+
gitkeep_events.write_text("", encoding="utf-8")
|
|
243
|
+
|
|
244
|
+
mem_gitignore = memory_dir / ".gitignore"
|
|
245
|
+
if not mem_gitignore.exists():
|
|
246
|
+
mem_gitignore.write_text("*\n", encoding="utf-8")
|
|
247
|
+
|
|
248
|
+
decay_file = memory_dir / "decay.json"
|
|
249
|
+
if not decay_file.exists():
|
|
250
|
+
decay_file.write_text(json.dumps({
|
|
251
|
+
"default_ttl_days": 90,
|
|
252
|
+
"decay_rate": 0.15,
|
|
253
|
+
"last_decay_run": None
|
|
254
|
+
}, indent=2), encoding="utf-8")
|
|
255
|
+
|
|
256
|
+
patterns_file = memory_dir / "patterns.json"
|
|
257
|
+
if not patterns_file.exists():
|
|
258
|
+
patterns_file.write_text("[]", encoding="utf-8")
|
|
259
|
+
|
|
260
|
+
profile_file = memory_dir / "profile.json"
|
|
261
|
+
if not profile_file.exists():
|
|
262
|
+
profile_file.write_text(json.dumps({
|
|
263
|
+
"stack": [],
|
|
264
|
+
"total_events": 0,
|
|
265
|
+
"active_patterns_count": 0,
|
|
266
|
+
"fix_rate_percentage": None,
|
|
267
|
+
"top_vulnerabilities": [],
|
|
268
|
+
"last_updated": None
|
|
269
|
+
}, indent=2), encoding="utf-8")
|
|
270
|
+
|
|
271
|
+
context_file = memory_dir / "context.json"
|
|
272
|
+
if not context_file.exists():
|
|
273
|
+
context_file.write_text(json.dumps({
|
|
274
|
+
"version": "1.0.0",
|
|
275
|
+
"generated_at": None,
|
|
276
|
+
"token_estimate": 0,
|
|
277
|
+
"max_token_budget": 2000,
|
|
278
|
+
"project_profile": {
|
|
279
|
+
"stack": [],
|
|
280
|
+
"total_events": 0,
|
|
281
|
+
"active_patterns_count": 0,
|
|
282
|
+
"fix_rate_percentage": None,
|
|
283
|
+
"top_vulnerabilities": []
|
|
284
|
+
},
|
|
285
|
+
"cards": []
|
|
286
|
+
}, indent=2), encoding="utf-8")
|
|
287
|
+
|
|
288
|
+
# Ensure target project root .gitignore excludes .torusguard/memory/
|
|
289
|
+
root_gitignore = target_root / ".gitignore"
|
|
290
|
+
if root_gitignore.exists():
|
|
291
|
+
try:
|
|
292
|
+
gi_content = root_gitignore.read_text(encoding="utf-8")
|
|
293
|
+
if ".torusguard/memory" not in gi_content:
|
|
294
|
+
root_gitignore.write_text(gi_content.rstrip() + "\n\n# TorusGuard security memory (local only)\n.torusguard/memory/\n", encoding="utf-8")
|
|
295
|
+
except Exception:
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
# ── Step 2: Security Profile & Coverage (In-Process, Zero Subprocess) ──
|
|
299
|
+
# Execute stack detection in-process without spawning child processes (Socket.dev safe)
|
|
300
|
+
detected_stack = None
|
|
128
301
|
stack_detect_script = torusguard_target / "scripts" / "stack_detect.py"
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if stack_detect_script.exists():
|
|
302
|
+
if stack_detect_script.is_file():
|
|
132
303
|
try:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
print(f" ⎿ Framework: {detected_stack.get('framework')}")
|
|
143
|
-
print(f" ⎿ Data Layer: {detected_stack.get('data_layer')}")
|
|
144
|
-
except Exception as e:
|
|
145
|
-
print(f" ⚠ Stack detection warning: {e}")
|
|
304
|
+
import importlib.util
|
|
305
|
+
spec = importlib.util.spec_from_file_location("stack_detect", str(stack_detect_script))
|
|
306
|
+
if spec and spec.loader:
|
|
307
|
+
sd_mod = importlib.util.module_from_spec(spec)
|
|
308
|
+
spec.loader.exec_module(sd_mod)
|
|
309
|
+
if hasattr(sd_mod, "detect_stack"):
|
|
310
|
+
detected_stack = sd_mod.detect_stack(target_root)
|
|
311
|
+
except Exception:
|
|
312
|
+
detected_stack = None
|
|
146
313
|
|
|
147
|
-
# 4. Update torusguard.json configuration
|
|
148
314
|
config_file = torusguard_target / "config" / "torusguard.json"
|
|
149
315
|
if config_file.exists():
|
|
150
316
|
try:
|
|
151
317
|
with open(config_file, "r", encoding="utf-8") as f:
|
|
152
318
|
cfg = json.load(f)
|
|
153
|
-
|
|
154
|
-
"
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
319
|
+
if detected_stack and detected_stack.get("framework") != "None":
|
|
320
|
+
cfg["detected_stack"] = {
|
|
321
|
+
"language": detected_stack.get("language"),
|
|
322
|
+
"framework": detected_stack.get("framework"),
|
|
323
|
+
"data_layer": detected_stack.get("data_layer")
|
|
324
|
+
}
|
|
325
|
+
else:
|
|
326
|
+
cfg["detected_stack"] = {
|
|
327
|
+
"language": "Unknown",
|
|
328
|
+
"framework": "None",
|
|
329
|
+
"data_layer": "None"
|
|
330
|
+
}
|
|
158
331
|
with open(config_file, "w", encoding="utf-8") as f:
|
|
159
332
|
json.dump(cfg, f, indent=2)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
333
|
+
except Exception:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
print_step_2_profile(detected_stack)
|
|
163
337
|
|
|
164
|
-
#
|
|
165
|
-
print("\n ▸ Step 3/3: Registering AI IDE Command Bridges...")
|
|
338
|
+
# ── Step 3: Register Slash Commands for AI IDEs ─────────────────────────
|
|
166
339
|
registered_ides = []
|
|
167
340
|
tg_workflow_content = """---
|
|
168
341
|
description: TorusGuard Autonomous Security Command Engine — run static security audits, authorized runtime web validation, governed remediation, and SARIF exports.
|
|
169
|
-
version: 0.
|
|
342
|
+
version: 1.0.0
|
|
170
343
|
tools: Read, Grep, Glob, Bash, Edit, Write
|
|
171
344
|
agent: auditor
|
|
172
345
|
---
|
|
@@ -192,16 +365,16 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
192
365
|
agents_wf.mkdir(parents=True, exist_ok=True)
|
|
193
366
|
(agents_wf / "torusguard.md").write_text(tg_workflow_content, encoding="utf-8")
|
|
194
367
|
dest_workflow_dirs.append(agents_wf)
|
|
195
|
-
registered_ides.append("Antigravity / Gemini
|
|
368
|
+
registered_ides.append("Antigravity / Gemini .agents/workflows/torusguard.md")
|
|
196
369
|
|
|
197
370
|
agent_wf = target_root / ".agent" / "workflows"
|
|
198
371
|
agent_wf.mkdir(parents=True, exist_ok=True)
|
|
199
372
|
(agent_wf / "torusguard.md").write_text(tg_workflow_content, encoding="utf-8")
|
|
200
373
|
dest_workflow_dirs.append(agent_wf)
|
|
201
|
-
registered_ides.append("
|
|
374
|
+
registered_ides.append("Tribunal Agent Kit .agent/workflows/torusguard.md")
|
|
202
375
|
|
|
203
376
|
# When full_commands is requested (e.g. via NPM package npx torusguard init),
|
|
204
|
-
# unlock and register all 11 individual slash commands
|
|
377
|
+
# unlock and register all 11 individual slash commands
|
|
205
378
|
if full_commands:
|
|
206
379
|
src_wf = torusguard_target / "workflows"
|
|
207
380
|
if src_wf.is_dir():
|
|
@@ -213,7 +386,7 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
213
386
|
for d in dest_workflow_dirs:
|
|
214
387
|
shutil.copy2(wf_file, d / cmd_filename)
|
|
215
388
|
count += 1
|
|
216
|
-
registered_ides.append(f"
|
|
389
|
+
registered_ides.append(f"{count} Slash Commands /torusguard-audit, -harden, -apply, ...")
|
|
217
390
|
|
|
218
391
|
# Claude Code (.claude/commands)
|
|
219
392
|
claude_dir = target_root / ".claude"
|
|
@@ -221,7 +394,7 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
221
394
|
claude_commands = claude_dir / "commands"
|
|
222
395
|
claude_commands.mkdir(parents=True, exist_ok=True)
|
|
223
396
|
(claude_commands / "torusguard.md").write_text(tg_workflow_content, encoding="utf-8")
|
|
224
|
-
registered_ides.append("Claude Code
|
|
397
|
+
registered_ides.append("Claude Code .claude/commands/torusguard.md")
|
|
225
398
|
|
|
226
399
|
# Cursor (.cursor/rules)
|
|
227
400
|
cursor_dir = target_root / ".cursor"
|
|
@@ -229,7 +402,7 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
229
402
|
cursor_rules = cursor_dir / "rules"
|
|
230
403
|
cursor_rules.mkdir(parents=True, exist_ok=True)
|
|
231
404
|
(cursor_rules / "torusguard.mdc").write_text(tg_workflow_content, encoding="utf-8")
|
|
232
|
-
registered_ides.append("Cursor
|
|
405
|
+
registered_ides.append("Cursor .cursor/rules/torusguard.mdc")
|
|
233
406
|
|
|
234
407
|
# Windsurf (.windsurfrules)
|
|
235
408
|
if (target_root / ".windsurfrules").exists() or (target_root / ".windsurf").exists():
|
|
@@ -237,7 +410,7 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
237
410
|
current_content = windsurf_rules.read_text(encoding="utf-8") if windsurf_rules.exists() else ""
|
|
238
411
|
if "TorusGuard" not in current_content:
|
|
239
412
|
windsurf_rules.write_text(current_content + "\n\n" + tg_workflow_content, encoding="utf-8")
|
|
240
|
-
registered_ides.append("Windsurf
|
|
413
|
+
registered_ides.append("Windsurf .windsurfrules")
|
|
241
414
|
|
|
242
415
|
# VS Code / GitHub Copilot (.github/copilot-instructions.md)
|
|
243
416
|
github_dir = target_root / ".github"
|
|
@@ -246,28 +419,19 @@ Parse the requested action from `$ARGUMENTS` (e.g. `audit`, `verify`, `web-valid
|
|
|
246
419
|
current_content = copilot_file.read_text(encoding="utf-8") if copilot_file.exists() else ""
|
|
247
420
|
if "TorusGuard" not in current_content:
|
|
248
421
|
copilot_file.write_text(current_content + "\n\n# TorusGuard Security Instructions\n" + tg_workflow_content, encoding="utf-8")
|
|
249
|
-
registered_ides.append("VS Code / Copilot
|
|
422
|
+
registered_ides.append("VS Code / Copilot .github/copilot-instructions.md")
|
|
250
423
|
|
|
251
424
|
# Universal AGENTS.md (Kimi, Codex, Open Agent Standard)
|
|
252
425
|
agents_md = target_root / "AGENTS.md"
|
|
253
426
|
current_agents = agents_md.read_text(encoding="utf-8") if agents_md.exists() else ""
|
|
254
427
|
if "TorusGuard" not in current_agents:
|
|
255
428
|
agents_md.write_text(current_agents + ("\n\n" if current_agents else "") + "# TorusGuard Security Guardrails\n\nRun `/torusguard audit` to scan the codebase or refer to `.torusguard/` for security guidelines.\n", encoding="utf-8")
|
|
256
|
-
registered_ides.append("Universal
|
|
429
|
+
registered_ides.append("Universal AGENTS.md Kimi / Open Agent Standard")
|
|
257
430
|
|
|
258
|
-
|
|
259
|
-
print(f" ✔ {r}")
|
|
431
|
+
print_step_3_bridges(registered_ides)
|
|
260
432
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
│ ✨ TorusGuard Workspace Initialized Successfully! │
|
|
264
|
-
│ │
|
|
265
|
-
│ 🚀 Next Steps: │
|
|
266
|
-
│ 1. In AI Chat: Type `/torusguard-audit` or `/torusguard` to scan │
|
|
267
|
-
│ 2. In Terminal: Run `npx torusguard status` for security posture │
|
|
268
|
-
│ 3. In CI/CD: Run `npx torusguard audit` to generate SARIF data │
|
|
269
|
-
╰──────────────────────────────────────────────────────────────────────────╯
|
|
270
|
-
""")
|
|
433
|
+
# ── Success Card ────────────────────────────────────────────────────────
|
|
434
|
+
print_success_card()
|
|
271
435
|
return True
|
|
272
436
|
|
|
273
437
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"agents/validator.md": "79fc4431de5c5507f8730aee6c5c3e61fd238cbf8cf8c7c6aac462eee9761ed6",
|
|
9
9
|
"config/scope.json": "b198a86186b4c6d5e1af516bf4e3a6afa8878e622ca1d57a5bf72d294f8c6f25",
|
|
10
10
|
"config/slash-commands.json": "5390f0d26f8117a62e56c58147d3a0d2deab1a20956302b212703281d13fa84e",
|
|
11
|
-
"config/torusguard.json": "
|
|
11
|
+
"config/torusguard.json": "b979d6ead31e64a63729e050d820f3e3228d3c825972a60ccae2087e6b27f574",
|
|
12
12
|
"references/django-security.md": "a15b7eb11b51dd1970d3d65544cf79ebea91fb99dc0e085e58a56532a718dce7",
|
|
13
13
|
"references/drf-security.md": "c7a7af3acaef895bf4798590acc88b3a2e9831a49eb2d23b4d8f31540ab264cc",
|
|
14
14
|
"references/express-security.md": "dff1db15e614d29d8c272f9da345b418077e62dc883fe210c7a50c552c76493f",
|
|
@@ -28,20 +28,24 @@
|
|
|
28
28
|
"schemas/confidence.schema.json": "08b3a564d7370e123dd5bfadde9fb077d2954e36b523685b9e1d7bdd2aa4b8cb",
|
|
29
29
|
"schemas/evidence.schema.json": "d3dcaa10fd2420a4046ea68836fb836a55d003ada4cdc6682229f232881182bf",
|
|
30
30
|
"schemas/finding.schema.json": "e0a7c28fa4a5f84bdedb5cf5223da26348250d633479d2dbf7ce3ebd6790a852",
|
|
31
|
+
"schemas/memory-context.schema.json": "6b9e032c55ba87b2517dd1e7ca0043b8e2439e85fa4a6eac6fc6041f3091e571",
|
|
32
|
+
"schemas/memory-event.schema.json": "4f653610e04fd480325d0ec77a4339bea4690f85e1d2fa0bd4f6b654c813c905",
|
|
33
|
+
"schemas/memory-pattern.schema.json": "df4aae2a3034a7c64ec50b3ae27b6ee9296aac7371c2a1938508834e5f778afe",
|
|
31
34
|
"schemas/provenance.schema.json": "781215801249532414643638b6608dc8f52febc9bca43ae03827b90aecc03256",
|
|
32
35
|
"schemas/remediation.schema.json": "217cb2743ae1a912dc074382e9f05634da93f2e30a880c03f6020600ef7e43d3",
|
|
33
36
|
"schemas/retest.schema.json": "773b764082f6d2e8c3656ea105e0ce09febd1020520bf262bbcd17f5924da11e",
|
|
34
37
|
"schemas/runtime-evidence.schema.json": "dbe13741a7fb68cca2a044d47a644ee0bdaacb83a28621dc2a98bffdd59989a6",
|
|
35
|
-
"scripts/diff_guard.py": "
|
|
36
|
-
"scripts/finding_scorer.py": "
|
|
37
|
-
"scripts/manifest_builder.py": "
|
|
38
|
+
"scripts/diff_guard.py": "5937b657c1047c78b7b9f0a36f3438cb699457cdb5ee56136d68b97edf56af42",
|
|
39
|
+
"scripts/finding_scorer.py": "c9f34c9153f5322c92bba824ce199ea281c450cd8edc637e61442c5f556d6183",
|
|
40
|
+
"scripts/manifest_builder.py": "136d5650a97dbb52fdf42c6d55b330a4f9593816e344b07913b0b2ba6b5cc409",
|
|
41
|
+
"scripts/memory_engine.py": "e99d34951b953bf3d96b6fca2413192de76778e6519278d4213fa3a72bc9b05f",
|
|
38
42
|
"scripts/monorepo_detector.py": "38f1eb948ee2856f175e1212956bce211effdd1327d3a31f7303d32ba9478ebe",
|
|
39
|
-
"scripts/run_manager.py": "
|
|
43
|
+
"scripts/run_manager.py": "b576fc3be9929ee6a52e67662e67f801e4e9317c158243582911e10c94a07619",
|
|
40
44
|
"scripts/safety_gate.py": "3379b7534b2590cf563717a448983ad8ed7e8f174f0ad65683d33e8a10f51558",
|
|
41
45
|
"scripts/sarif_exporter.py": "35c66637413afc4b72d5bd426638447c77231d2a8594dce5addceffd4803a20d",
|
|
42
46
|
"scripts/stack_detect.py": "99b7bdf9829d58ff5b8c59afd6e8b6429bbb11d830ac73dec491101cbbc41574",
|
|
43
|
-
"skills/torusguard/SKILL.md": "
|
|
44
|
-
"skills/torusguard/bootstrap.py": "
|
|
47
|
+
"skills/torusguard/SKILL.md": "e772e348716aee26e424ec96be5bd6750dbebcefc248bc4dab0df1e6c0e24fcf",
|
|
48
|
+
"skills/torusguard/bootstrap.py": "7c5a13687578a8736aef25cf767d1ef3c8b82dcae0eb7efce41bf131d27a120f",
|
|
45
49
|
"skills/torusguard/references/auth-and-sessions.md": "34c2843f8c0dbe3574e54f00a39a92a1f431527ec5f69b2d64c84240d424a39e",
|
|
46
50
|
"skills/torusguard/references/business-logic-and-abuse.md": "0d6088936bc1a01023070332fccef0f2e0734358a5119379055f3d2895c22567",
|
|
47
51
|
"skills/torusguard/references/cache-and-sensitive-responses.md": "8f920cacde29c50b1f5f98de341a60db0bc607fe8c212a7e618ef7af08c86511",
|
|
@@ -66,13 +70,13 @@
|
|
|
66
70
|
"skills/torusguard/references/webhook-security.md": "a0c7efb663c664dd840ba99b689cb67d4688643106f8b6e80d18a424c6add386",
|
|
67
71
|
"skills/torusguard/references/websocket-security.md": "5624a84abd75bbb4eed029cd217e0c794ee452cbecc0730a8ab4d0f371537cd7",
|
|
68
72
|
"skills/torusguard-apply/SKILL.md": "9436fa5cdefa661677ada22936a3949de06aca276e4f4bb27e0ef5b7ea34edc3",
|
|
69
|
-
"skills/torusguard-audit/SKILL.md": "
|
|
73
|
+
"skills/torusguard-audit/SKILL.md": "f879e280d357ea7f124c647ce39af2b24b79db608c9edd018f26e5312625a8b5",
|
|
70
74
|
"skills/torusguard-authorize/SKILL.md": "da849585f2e46c08ef528b89737be14647f1109fd365fd27f3f6da57a396724b",
|
|
71
75
|
"skills/torusguard-exploit-check/SKILL.md": "e9a173e154a88cc9cb929c59d79a65ab13d5cf9567b4112bd7f272746cf9c3f9",
|
|
72
76
|
"skills/torusguard-full/SKILL.md": "cc97684d37de378e51fefd12f6f9a7cd15cc469ce46079a0599440aa7b073f7b",
|
|
73
|
-
"skills/torusguard-harden/SKILL.md": "
|
|
77
|
+
"skills/torusguard-harden/SKILL.md": "326fa84bda4832f96b19d73824c3472dfe185247ed3e3db9715dd943d46d9874",
|
|
74
78
|
"skills/torusguard-init/SKILL.md": "1f7844d66219955581fdb266029eefdc355dbebb5741b848daf0062e606202a2",
|
|
75
|
-
"skills/torusguard-recheck/SKILL.md": "
|
|
79
|
+
"skills/torusguard-recheck/SKILL.md": "457b2a1cce064a738fbe43f0dba6b4393f1630a82f60698ea5ebe655ec017e50",
|
|
76
80
|
"skills/torusguard-report/SKILL.md": "bde44d114bdc85c04472644a644a7796ed2f182a23e0c057838a3251e4910a09",
|
|
77
81
|
"skills/torusguard-status/SKILL.md": "4e71d03c71b240afd7f6108569530b7da3702634c512a74ca8dc3174f3c6b0fb",
|
|
78
82
|
"skills/torusguard-verify/SKILL.md": "10c19e0926255686351e3b75ae774880cf35562d42bd30b87a877bb8f284fc1f",
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
"workflows/exploit-check.md": "9684009b835a61d5aca8827a971ee686a24c800425d86ef96f963dd27de845b8",
|
|
88
92
|
"workflows/harden.md": "509484f844c9dcd10dd14c8bb6e3456021928e6cf6abd374faaf34be8fc34ce2",
|
|
89
93
|
"workflows/init.md": "3d1668a9f6b87417c31f47c887c495f863595ed4a29074e0c5a8ee1ece1d4a35",
|
|
94
|
+
"workflows/memory.md": "c034f9266380db3c05e9ffed943c2bb4ba73c060507e3508e47b543a035a5873",
|
|
90
95
|
"workflows/recheck.md": "7d374c29a5585c31b81f84fb32d6a466f2e9cb339726dc0523dba25e57bc5d13",
|
|
91
96
|
"workflows/report.md": "56b3449cff362172c63093ee6150446d11bf05e3c8143661966dad9d42df1470",
|
|
92
97
|
"workflows/status.md": "921129646518c592cb47ad8e8ccf3d703e8d11ea6eb89bd21a206f545e022dba",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "torusguard-memory-context.schema.json",
|
|
4
|
+
"title": "TorusGuard Memory Context Window",
|
|
5
|
+
"description": "Pre-computed, token-budgeted context window for AI agent consumption formatted as structured JSON cards.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "generated_at", "token_estimate", "max_token_budget", "project_profile", "cards"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"version": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "TorusGuard engine version (e.g., 1.0.0)."
|
|
12
|
+
},
|
|
13
|
+
"generated_at": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"format": "date-time",
|
|
16
|
+
"description": "ISO 8601 UTC timestamp when this context window was computed."
|
|
17
|
+
},
|
|
18
|
+
"token_estimate": {
|
|
19
|
+
"type": "integer",
|
|
20
|
+
"minimum": 0,
|
|
21
|
+
"description": "Estimated token count of this context document."
|
|
22
|
+
},
|
|
23
|
+
"max_token_budget": {
|
|
24
|
+
"type": "integer",
|
|
25
|
+
"default": 2000,
|
|
26
|
+
"description": "Maximum allowed tokens for agent context injection (default: 2000)."
|
|
27
|
+
},
|
|
28
|
+
"project_profile": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["stack", "total_events", "active_patterns_count"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"stack": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": { "type": "string" },
|
|
35
|
+
"description": "Detected technology stack frameworks/languages."
|
|
36
|
+
},
|
|
37
|
+
"total_events": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"minimum": 0
|
|
40
|
+
},
|
|
41
|
+
"active_patterns_count": {
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"minimum": 0
|
|
44
|
+
},
|
|
45
|
+
"fix_rate_percentage": {
|
|
46
|
+
"type": ["number", "null"],
|
|
47
|
+
"minimum": 0,
|
|
48
|
+
"maximum": 100
|
|
49
|
+
},
|
|
50
|
+
"top_vulnerabilities": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": { "type": "string" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"cards": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"description": "Ordered list of high-priority intelligence cards for LLM grounding.",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["card_id", "card_type", "priority", "title", "summary", "card_data"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"card_id": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "Unique card identifier (e.g., CARD-001)."
|
|
66
|
+
},
|
|
67
|
+
"card_type": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"enum": ["pattern", "profile", "regression_watch", "false_positive", "fix_idiom"],
|
|
70
|
+
"description": "Semantic category of this context card."
|
|
71
|
+
},
|
|
72
|
+
"priority": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"minimum": 1,
|
|
75
|
+
"maximum": 100,
|
|
76
|
+
"description": "Rank order priority (higher = more relevant/critical)."
|
|
77
|
+
},
|
|
78
|
+
"title": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Short, human-readable and agent-scannable card title."
|
|
81
|
+
},
|
|
82
|
+
"summary": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "One-line operational summary for rapid agent reasoning."
|
|
85
|
+
},
|
|
86
|
+
"card_data": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"description": "Structured card payload containing rule_id, fix_strategy, confidence, affected_files, etc.",
|
|
89
|
+
"additionalProperties": true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|