@kevinrabun/judges 3.45.0 → 3.47.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/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adoption-report.d.ts +8 -0
- package/dist/commands/adoption-report.d.ts.map +1 -0
- package/dist/commands/adoption-report.js +219 -0
- package/dist/commands/adoption-report.js.map +1 -0
- package/dist/commands/ai-model-trust.d.ts +17 -0
- package/dist/commands/ai-model-trust.d.ts.map +1 -0
- package/dist/commands/ai-model-trust.js +235 -0
- package/dist/commands/ai-model-trust.js.map +1 -0
- package/dist/commands/ai-prompt-audit.d.ts +23 -0
- package/dist/commands/ai-prompt-audit.d.ts.map +1 -0
- package/dist/commands/ai-prompt-audit.js +255 -0
- package/dist/commands/ai-prompt-audit.js.map +1 -0
- package/dist/commands/audit-bundle.d.ts +29 -0
- package/dist/commands/audit-bundle.d.ts.map +1 -0
- package/dist/commands/audit-bundle.js +235 -0
- package/dist/commands/audit-bundle.js.map +1 -0
- package/dist/commands/code-owner-suggest.d.ts +17 -0
- package/dist/commands/code-owner-suggest.d.ts.map +1 -0
- package/dist/commands/code-owner-suggest.js +215 -0
- package/dist/commands/code-owner-suggest.js.map +1 -0
- package/dist/commands/config-drift.d.ts +25 -0
- package/dist/commands/config-drift.d.ts.map +1 -0
- package/dist/commands/config-drift.js +214 -0
- package/dist/commands/config-drift.js.map +1 -0
- package/dist/commands/cost-forecast.d.ts +19 -0
- package/dist/commands/cost-forecast.d.ts.map +1 -0
- package/dist/commands/cost-forecast.js +194 -0
- package/dist/commands/cost-forecast.js.map +1 -0
- package/dist/commands/dev-score.d.ts +37 -0
- package/dist/commands/dev-score.d.ts.map +1 -0
- package/dist/commands/dev-score.js +204 -0
- package/dist/commands/dev-score.js.map +1 -0
- package/dist/commands/generate.d.ts +8 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +404 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/learn.d.ts +27 -0
- package/dist/commands/learn.d.ts.map +1 -0
- package/dist/commands/learn.js +289 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/model-risk.d.ts +28 -0
- package/dist/commands/model-risk.d.ts.map +1 -0
- package/dist/commands/model-risk.js +221 -0
- package/dist/commands/model-risk.js.map +1 -0
- package/dist/commands/pr-quality-gate.d.ts +29 -0
- package/dist/commands/pr-quality-gate.d.ts.map +1 -0
- package/dist/commands/pr-quality-gate.js +208 -0
- package/dist/commands/pr-quality-gate.js.map +1 -0
- package/dist/commands/reg-watch.d.ts +21 -0
- package/dist/commands/reg-watch.d.ts.map +1 -0
- package/dist/commands/reg-watch.js +220 -0
- package/dist/commands/reg-watch.js.map +1 -0
- package/dist/commands/retro.d.ts +23 -0
- package/dist/commands/retro.d.ts.map +1 -0
- package/dist/commands/retro.js +217 -0
- package/dist/commands/retro.js.map +1 -0
- package/dist/commands/team-leaderboard.d.ts +25 -0
- package/dist/commands/team-leaderboard.d.ts.map +1 -0
- package/dist/commands/team-leaderboard.js +228 -0
- package/dist/commands/team-leaderboard.js.map +1 -0
- package/dist/commands/team-rules-sync.d.ts +8 -0
- package/dist/commands/team-rules-sync.d.ts.map +1 -0
- package/dist/commands/team-rules-sync.js +251 -0
- package/dist/commands/team-rules-sync.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure code template generator — pre-hardened templates
|
|
3
|
+
* for common patterns with Judges findings pre-mitigated.
|
|
4
|
+
*
|
|
5
|
+
* All output is generated locally — no data transmitted.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Template library ───────────────────────────────────────────────────────
|
|
8
|
+
const TEMPLATE_LIBRARY = [
|
|
9
|
+
{
|
|
10
|
+
id: "express-api-route",
|
|
11
|
+
title: "Express API Route (Secure)",
|
|
12
|
+
framework: "express",
|
|
13
|
+
language: "typescript",
|
|
14
|
+
description: "Express REST endpoint with input validation, error handling, and rate limiting",
|
|
15
|
+
mitigations: [
|
|
16
|
+
"Input validation with schema check",
|
|
17
|
+
"Error handler hides internal details",
|
|
18
|
+
"Parameterized DB queries",
|
|
19
|
+
"Rate limiting per IP",
|
|
20
|
+
],
|
|
21
|
+
code: `import { Router, Request, Response, NextFunction } from "express";
|
|
22
|
+
|
|
23
|
+
const router = Router();
|
|
24
|
+
|
|
25
|
+
// Simple in-memory rate limiter
|
|
26
|
+
const hits = new Map<string, { count: number; reset: number }>();
|
|
27
|
+
function rateLimit(limit: number, windowMs: number) {
|
|
28
|
+
return (req: Request, res: Response, next: NextFunction): void => {
|
|
29
|
+
const key = req.ip ?? "unknown";
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const entry = hits.get(key);
|
|
32
|
+
if (!entry || now > entry.reset) {
|
|
33
|
+
hits.set(key, { count: 1, reset: now + windowMs });
|
|
34
|
+
return next();
|
|
35
|
+
}
|
|
36
|
+
if (entry.count >= limit) {
|
|
37
|
+
res.status(429).json({ error: "Too many requests" });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
entry.count++;
|
|
41
|
+
next();
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Input validation
|
|
46
|
+
function validateBody(body: unknown): body is { name: string; email: string } {
|
|
47
|
+
if (typeof body !== "object" || body === null) return false;
|
|
48
|
+
const b = body as Record<string, unknown>;
|
|
49
|
+
return typeof b.name === "string" && b.name.length <= 200
|
|
50
|
+
&& typeof b.email === "string" && /^[^@]+@[^@]+$/.test(b.email);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
router.post("/api/users", rateLimit(100, 60_000), (req: Request, res: Response) => {
|
|
54
|
+
if (!validateBody(req.body)) {
|
|
55
|
+
res.status(400).json({ error: "Invalid input" });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Use parameterized queries — never interpolate user input
|
|
59
|
+
// db.query("INSERT INTO users (name, email) VALUES ($1, $2)", [req.body.name, req.body.email]);
|
|
60
|
+
res.status(201).json({ ok: true });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Centralized error handler — hides internals
|
|
64
|
+
router.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
|
|
65
|
+
console.error(err); // log server-side only
|
|
66
|
+
res.status(500).json({ error: "Internal server error" });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default router;`,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "react-auth-component",
|
|
73
|
+
title: "React Auth Component (Secure)",
|
|
74
|
+
framework: "react",
|
|
75
|
+
language: "typescript",
|
|
76
|
+
description: "React authentication form with CSRF protection and secure state management",
|
|
77
|
+
mitigations: [
|
|
78
|
+
"CSRF token included in form submission",
|
|
79
|
+
"Password field never logged or serialized",
|
|
80
|
+
"AuthN errors are generic — no user enumeration",
|
|
81
|
+
"State cleared on unmount",
|
|
82
|
+
],
|
|
83
|
+
code: `import React, { useState, useCallback, useEffect } from "react";
|
|
84
|
+
|
|
85
|
+
interface LoginFormProps {
|
|
86
|
+
csrfToken: string;
|
|
87
|
+
onLogin: (token: string) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function LoginForm({ csrfToken, onLogin }: LoginFormProps) {
|
|
91
|
+
const [email, setEmail] = useState("");
|
|
92
|
+
const [password, setPassword] = useState("");
|
|
93
|
+
const [error, setError] = useState<string | null>(null);
|
|
94
|
+
const [loading, setLoading] = useState(false);
|
|
95
|
+
|
|
96
|
+
// Clear sensitive state on unmount
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
return () => {
|
|
99
|
+
setPassword("");
|
|
100
|
+
};
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
const handleSubmit = useCallback(
|
|
104
|
+
async (e: React.FormEvent) => {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
setError(null);
|
|
107
|
+
setLoading(true);
|
|
108
|
+
try {
|
|
109
|
+
const resp = await fetch("/api/login", {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
"X-CSRF-Token": csrfToken,
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({ email, password }),
|
|
116
|
+
credentials: "same-origin",
|
|
117
|
+
});
|
|
118
|
+
if (!resp.ok) {
|
|
119
|
+
// Generic error — prevents user enumeration
|
|
120
|
+
setError("Invalid email or password");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const data = await resp.json();
|
|
124
|
+
onLogin(data.token);
|
|
125
|
+
} catch {
|
|
126
|
+
setError("Login failed. Please try again.");
|
|
127
|
+
} finally {
|
|
128
|
+
setLoading(false);
|
|
129
|
+
setPassword(""); // clear password from memory
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
[email, password, csrfToken, onLogin],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<form onSubmit={handleSubmit}>
|
|
137
|
+
<label>Email
|
|
138
|
+
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
|
|
139
|
+
</label>
|
|
140
|
+
<label>Password
|
|
141
|
+
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required autoComplete="current-password" />
|
|
142
|
+
</label>
|
|
143
|
+
{error && <p role="alert">{error}</p>}
|
|
144
|
+
<button type="submit" disabled={loading}>{loading ? "Logging in…" : "Log In"}</button>
|
|
145
|
+
</form>
|
|
146
|
+
);
|
|
147
|
+
}`,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "node-file-upload",
|
|
151
|
+
title: "Node.js File Upload (Secure)",
|
|
152
|
+
framework: "node",
|
|
153
|
+
language: "typescript",
|
|
154
|
+
description: "File upload handler with type checking, size limits, and path traversal prevention",
|
|
155
|
+
mitigations: [
|
|
156
|
+
"File type allowlist — rejects unexpected MIME types",
|
|
157
|
+
"Size limit enforced before writing",
|
|
158
|
+
"Sanitized filename prevents path traversal",
|
|
159
|
+
"Upload directory is outside web root",
|
|
160
|
+
],
|
|
161
|
+
code: `import { randomUUID } from "crypto";
|
|
162
|
+
import { join, extname } from "path";
|
|
163
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
164
|
+
|
|
165
|
+
const UPLOAD_DIR = "/var/uploads"; // outside web root
|
|
166
|
+
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
167
|
+
const ALLOWED_TYPES = new Set(["image/png", "image/jpeg", "image/gif", "application/pdf"]);
|
|
168
|
+
const ALLOWED_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".pdf"]);
|
|
169
|
+
|
|
170
|
+
interface UploadResult {
|
|
171
|
+
ok: boolean;
|
|
172
|
+
id?: string;
|
|
173
|
+
error?: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function handleUpload(
|
|
177
|
+
filename: string,
|
|
178
|
+
contentType: string,
|
|
179
|
+
data: Buffer,
|
|
180
|
+
): UploadResult {
|
|
181
|
+
// 1. Size check
|
|
182
|
+
if (data.length > MAX_SIZE) return { ok: false, error: "File too large" };
|
|
183
|
+
|
|
184
|
+
// 2. MIME type check
|
|
185
|
+
if (!ALLOWED_TYPES.has(contentType)) return { ok: false, error: "File type not allowed" };
|
|
186
|
+
|
|
187
|
+
// 3. Extension check (defense in depth)
|
|
188
|
+
const ext = extname(filename).toLowerCase();
|
|
189
|
+
if (!ALLOWED_EXTS.has(ext)) return { ok: false, error: "File extension not allowed" };
|
|
190
|
+
|
|
191
|
+
// 4. Generate safe filename — never use user-supplied name directly
|
|
192
|
+
const id = randomUUID();
|
|
193
|
+
const safeName = id + ext;
|
|
194
|
+
const dest = join(UPLOAD_DIR, safeName);
|
|
195
|
+
|
|
196
|
+
// 5. Ensure directory exists
|
|
197
|
+
if (!existsSync(UPLOAD_DIR)) mkdirSync(UPLOAD_DIR, { recursive: true });
|
|
198
|
+
|
|
199
|
+
// 6. Write file
|
|
200
|
+
writeFileSync(dest, data);
|
|
201
|
+
return { ok: true, id };
|
|
202
|
+
}`,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "python-flask-api",
|
|
206
|
+
title: "Flask API Endpoint (Secure)",
|
|
207
|
+
framework: "flask",
|
|
208
|
+
language: "python",
|
|
209
|
+
description: "Flask REST endpoint with CORS, input validation, and secure error responses",
|
|
210
|
+
mitigations: [
|
|
211
|
+
"CORS restricted to allowed origins",
|
|
212
|
+
"Input validated before processing",
|
|
213
|
+
"Errors return generic messages",
|
|
214
|
+
"SQL parameterized via SQLAlchemy",
|
|
215
|
+
],
|
|
216
|
+
code: `from flask import Flask, request, jsonify
|
|
217
|
+
from flask_cors import CORS
|
|
218
|
+
|
|
219
|
+
app = Flask(__name__)
|
|
220
|
+
CORS(app, origins=["https://app.example.com"]) # restrict origins
|
|
221
|
+
|
|
222
|
+
@app.route("/api/items", methods=["POST"])
|
|
223
|
+
def create_item():
|
|
224
|
+
data = request.get_json(silent=True)
|
|
225
|
+
if not data:
|
|
226
|
+
return jsonify({"error": "Invalid JSON"}), 400
|
|
227
|
+
|
|
228
|
+
name = data.get("name", "")
|
|
229
|
+
if not isinstance(name, str) or len(name) > 200:
|
|
230
|
+
return jsonify({"error": "Invalid input"}), 400
|
|
231
|
+
|
|
232
|
+
# Use parameterized queries — never f-strings with user input
|
|
233
|
+
# db.session.execute(text("INSERT INTO items (name) VALUES (:name)"), {"name": name})
|
|
234
|
+
return jsonify({"ok": True}), 201
|
|
235
|
+
|
|
236
|
+
@app.errorhandler(Exception)
|
|
237
|
+
def handle_error(e):
|
|
238
|
+
app.logger.exception("Request error") # log server-side
|
|
239
|
+
return jsonify({"error": "Internal server error"}), 500
|
|
240
|
+
`,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: "go-http-handler",
|
|
244
|
+
title: "Go HTTP Handler (Secure)",
|
|
245
|
+
framework: "net/http",
|
|
246
|
+
language: "go",
|
|
247
|
+
description: "Go HTTP handler with timeout, input validation, and secure headers",
|
|
248
|
+
mitigations: [
|
|
249
|
+
"Read/write timeouts prevent slowloris",
|
|
250
|
+
"Request body size limited",
|
|
251
|
+
"Security headers set on every response",
|
|
252
|
+
"Input validation before processing",
|
|
253
|
+
],
|
|
254
|
+
code: `package main
|
|
255
|
+
|
|
256
|
+
import (
|
|
257
|
+
\t"encoding/json"
|
|
258
|
+
\t"log"
|
|
259
|
+
\t"net/http"
|
|
260
|
+
\t"time"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
type CreateRequest struct {
|
|
264
|
+
\tName string \`json:"name"\`
|
|
265
|
+
\tEmail string \`json:"email"\`
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
func securityHeaders(next http.Handler) http.Handler {
|
|
269
|
+
\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
270
|
+
\t\tw.Header().Set("X-Content-Type-Options", "nosniff")
|
|
271
|
+
\t\tw.Header().Set("X-Frame-Options", "DENY")
|
|
272
|
+
\t\tw.Header().Set("Content-Security-Policy", "default-src 'self'")
|
|
273
|
+
\t\tnext.ServeHTTP(w, r)
|
|
274
|
+
\t})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
func createHandler(w http.ResponseWriter, r *http.Request) {
|
|
278
|
+
\tif r.Method != http.MethodPost {
|
|
279
|
+
\t\thttp.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
280
|
+
\t\treturn
|
|
281
|
+
\t}
|
|
282
|
+
|
|
283
|
+
\t// Limit request body size
|
|
284
|
+
\tr.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB
|
|
285
|
+
|
|
286
|
+
\tvar req CreateRequest
|
|
287
|
+
\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
288
|
+
\t\thttp.Error(w, "Invalid input", http.StatusBadRequest)
|
|
289
|
+
\t\treturn
|
|
290
|
+
\t}
|
|
291
|
+
|
|
292
|
+
\tif len(req.Name) == 0 || len(req.Name) > 200 {
|
|
293
|
+
\t\thttp.Error(w, "Invalid name", http.StatusBadRequest)
|
|
294
|
+
\t\treturn
|
|
295
|
+
\t}
|
|
296
|
+
|
|
297
|
+
\tw.Header().Set("Content-Type", "application/json")
|
|
298
|
+
\tjson.NewEncoder(w).Encode(map[string]bool{"ok": true})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func main() {
|
|
302
|
+
\tmux := http.NewServeMux()
|
|
303
|
+
\tmux.HandleFunc("/api/create", createHandler)
|
|
304
|
+
|
|
305
|
+
\tsrv := &http.Server{
|
|
306
|
+
\t\tAddr: ":8080",
|
|
307
|
+
\t\tHandler: securityHeaders(mux),
|
|
308
|
+
\t\tReadTimeout: 5 * time.Second,
|
|
309
|
+
\t\tWriteTimeout: 10 * time.Second,
|
|
310
|
+
\t\tIdleTimeout: 120 * time.Second,
|
|
311
|
+
\t}
|
|
312
|
+
\tlog.Fatal(srv.ListenAndServe())
|
|
313
|
+
}
|
|
314
|
+
`,
|
|
315
|
+
},
|
|
316
|
+
];
|
|
317
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
318
|
+
export function runGenerate(argv) {
|
|
319
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
320
|
+
console.log(`
|
|
321
|
+
judges generate — Secure code template generator
|
|
322
|
+
|
|
323
|
+
Usage:
|
|
324
|
+
judges generate --list
|
|
325
|
+
judges generate --template express-api-route
|
|
326
|
+
judges generate --template express-api-route --out ./src/routes/users.ts
|
|
327
|
+
judges generate --lang typescript
|
|
328
|
+
judges generate --framework flask
|
|
329
|
+
|
|
330
|
+
Options:
|
|
331
|
+
--list List all available templates
|
|
332
|
+
--template <id> Generate a specific template
|
|
333
|
+
--lang <language> Filter templates by language
|
|
334
|
+
--framework <name> Filter templates by framework
|
|
335
|
+
--out <path> Write template to file (stdout if omitted)
|
|
336
|
+
--format json JSON output
|
|
337
|
+
--help, -h Show this help
|
|
338
|
+
`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
342
|
+
const lang = argv.find((_a, i) => argv[i - 1] === "--lang");
|
|
343
|
+
const framework = argv.find((_a, i) => argv[i - 1] === "--framework");
|
|
344
|
+
// Filter
|
|
345
|
+
let templates = TEMPLATE_LIBRARY;
|
|
346
|
+
if (lang)
|
|
347
|
+
templates = templates.filter((t) => t.language === lang.toLowerCase());
|
|
348
|
+
if (framework)
|
|
349
|
+
templates = templates.filter((t) => t.framework === framework.toLowerCase());
|
|
350
|
+
// List
|
|
351
|
+
if (argv.includes("--list") || !argv.find((_a, i) => argv[i - 1] === "--template")) {
|
|
352
|
+
if (format === "json") {
|
|
353
|
+
console.log(JSON.stringify(templates.map(({ code: _c, ...rest }) => rest), null, 2));
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
console.log(`\n Secure Code Templates (${templates.length})\n ──────────────────────────`);
|
|
357
|
+
for (const t of templates) {
|
|
358
|
+
console.log(` ${t.id.padEnd(25)} ${t.language.padEnd(12)} ${t.framework.padEnd(10)} ${t.title}`);
|
|
359
|
+
}
|
|
360
|
+
console.log(`\n Use: judges generate --template <id>\n`);
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
// Specific template
|
|
365
|
+
const templateId = argv.find((_a, i) => argv[i - 1] === "--template");
|
|
366
|
+
if (!templateId)
|
|
367
|
+
return;
|
|
368
|
+
const tmpl = TEMPLATE_LIBRARY.find((t) => t.id === templateId);
|
|
369
|
+
if (!tmpl) {
|
|
370
|
+
console.error(` Template not found: ${templateId}`);
|
|
371
|
+
console.error(` Use --list to see available templates.`);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// Output path
|
|
375
|
+
const outPath = argv.find((_a, i) => argv[i - 1] === "--out");
|
|
376
|
+
if (format === "json") {
|
|
377
|
+
console.log(JSON.stringify(tmpl, null, 2));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const header = [
|
|
381
|
+
`// ═══════════════════════════════════════════════════`,
|
|
382
|
+
`// ${tmpl.title}`,
|
|
383
|
+
`// Framework: ${tmpl.framework} | Language: ${tmpl.language}`,
|
|
384
|
+
`// ${tmpl.description}`,
|
|
385
|
+
`//`,
|
|
386
|
+
`// Security mitigations applied:`,
|
|
387
|
+
...tmpl.mitigations.map((m) => `// ✓ ${m}`),
|
|
388
|
+
`//`,
|
|
389
|
+
`// Generated by: judges generate --template ${tmpl.id}`,
|
|
390
|
+
`// ═══════════════════════════════════════════════════`,
|
|
391
|
+
``,
|
|
392
|
+
].join("\n");
|
|
393
|
+
const output = header + tmpl.code;
|
|
394
|
+
if (outPath) {
|
|
395
|
+
const { writeFileSync: wfs } = require("fs");
|
|
396
|
+
wfs(outPath, output);
|
|
397
|
+
console.log(` ✅ Written to ${outPath}`);
|
|
398
|
+
console.log(` Template: ${tmpl.title}`);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
console.log(output);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Developer learning path — personalized, structured learning
|
|
3
|
+
* based on a developer's finding history.
|
|
4
|
+
*
|
|
5
|
+
* Uses .judges-scores/ and .judges-learn/ for progress.
|
|
6
|
+
*/
|
|
7
|
+
interface LearningModule {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
category: string;
|
|
11
|
+
difficulty: "beginner" | "intermediate" | "advanced";
|
|
12
|
+
concepts: string[];
|
|
13
|
+
exercises: string[];
|
|
14
|
+
completed: boolean;
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
interface LearningPath {
|
|
18
|
+
author: string;
|
|
19
|
+
modules: LearningModule[];
|
|
20
|
+
progress: number;
|
|
21
|
+
streak: number;
|
|
22
|
+
lastActivity: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function completeModule(author: string, moduleId: string): LearningPath;
|
|
25
|
+
export declare function runLearn(argv: string[]): void;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=learn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn.d.ts","sourceRoot":"","sources":["../../src/commands/learn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,UAAU,CAAC;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAgLD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAY7E;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsG7C"}
|