opencode-skills-antigravity 0.0.7 → 0.0.9
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/README.md +5 -3
- package/bundled-skills/007/scripts/full_audit.py +6 -4
- package/bundled-skills/007/scripts/score_calculator.py +67 -7
- package/bundled-skills/algorithmic-art/templates/viewer.html +2 -2
- package/bundled-skills/apify-actorization/SKILL.md +1 -2
- package/bundled-skills/apify-actorization/references/cli-actorization.md +4 -4
- package/bundled-skills/docs/COMMUNITY_GUIDELINES.md +1 -1
- package/bundled-skills/docs/contributors/community-guidelines.md +3 -32
- package/bundled-skills/docs/integrations/jetski-gemini-loader/loader.ts +21 -3
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-18-addendum.md +22 -0
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/walkthrough.md +21 -17
- package/bundled-skills/dotnet-backend-patterns/resources/implementation-playbook.md +2 -2
- package/bundled-skills/instagram/scripts/auth.py +15 -6
- package/bundled-skills/landing-page-generator/SKILL.md +203 -0
- package/bundled-skills/landing-page-generator/references/conversion-patterns.md +176 -0
- package/bundled-skills/landing-page-generator/references/frameworks.md +177 -0
- package/bundled-skills/landing-page-generator/references/landing-page-patterns.md +98 -0
- package/bundled-skills/landing-page-generator/references/seo-checklist.md +109 -0
- package/bundled-skills/landing-page-generator/scripts/landing_page_scaffolder.py +568 -0
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +33 -1073
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +7 -4
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +15 -3
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +85 -88
- package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +260 -456
- package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package.json +4 -2
- package/bundled-skills/notebooklm/scripts/auth_manager.py +17 -3
- package/bundled-skills/notebooklm/scripts/browser_session.py +11 -2
- package/bundled-skills/radix-ui-design-system/examples/README.md +1 -1
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +5 -3
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/app.py +21 -13
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/webhook_handler.py +11 -4
- package/package.json +1 -1
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +0 -35
- /package/bundled-skills/dotnet-backend-patterns/assets/{repository-template.cs → repository-template.cs.template} +0 -0
- /package/bundled-skills/dotnet-backend-patterns/assets/{service-template.cs → service-template.cs.template} +0 -0
- /package/bundled-skills/radix-ui-design-system/templates/{component-template.tsx → component-template.tsx.template} +0 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenCode Skills Antigravity
|
|
2
2
|
|
|
3
3
|
An [OpenCode CLI](https://opencode.ai/) plugin that bundles and manages the [Antigravity Awesome Skills](https://github.com/sickn33/antigravity-awesome-skills) repository for instant use.
|
|
4
4
|
|
|
@@ -14,14 +14,16 @@ OpenCode automatically detects all skills and makes them available to the AI age
|
|
|
14
14
|
You can then invoke any skill explicitly in your prompt:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
opencode run
|
|
17
|
+
opencode run /brainstorming help me plan a feature
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
Skills are also available as `/` commands directly in the OpenCode chat (e.g., `/brainstorming`).
|
|
21
|
+
|
|
20
22
|
Or simply describe what you want and OpenCode will pick the right skill automatically.
|
|
21
23
|
|
|
22
24
|
## 🚀 Installation
|
|
23
25
|
|
|
24
|
-
###
|
|
26
|
+
### Add the plugin to your global OpenCode config
|
|
25
27
|
|
|
26
28
|
Edit (or create) `~/.config/opencode/opencode.json`:
|
|
27
29
|
|
|
@@ -1081,6 +1081,7 @@ def run_audit(
|
|
|
1081
1081
|
inj_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1082
1082
|
quick_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1083
1083
|
all_findings: list[dict] = []
|
|
1084
|
+
report_findings: list[dict] = []
|
|
1084
1085
|
|
|
1085
1086
|
if need_scanners:
|
|
1086
1087
|
logger.info("Running scanners for phases %s...", [p for p in phases_list if p >= 3])
|
|
@@ -1121,6 +1122,7 @@ def run_audit(
|
|
|
1121
1122
|
+ quick_report.get("findings", [])
|
|
1122
1123
|
)
|
|
1123
1124
|
all_findings = score_calculator._deduplicate_findings(raw)
|
|
1125
|
+
report_findings = score_calculator.redact_findings_for_report(all_findings)
|
|
1124
1126
|
|
|
1125
1127
|
# ------------------------------------------------------------------
|
|
1126
1128
|
# Collect source files if needed for phase 6
|
|
@@ -1142,7 +1144,7 @@ def run_audit(
|
|
|
1142
1144
|
if 2 in phases_list:
|
|
1143
1145
|
# Phase 2 benefits from phase 1 data and findings
|
|
1144
1146
|
surface = phases_data.get("phase1") or _phase1_surface_mapping(target, verbose=verbose)
|
|
1145
|
-
phases_data["phase2"] = _phase2_threat_modeling_hints(surface,
|
|
1147
|
+
phases_data["phase2"] = _phase2_threat_modeling_hints(surface, report_findings)
|
|
1146
1148
|
|
|
1147
1149
|
if 3 in phases_list:
|
|
1148
1150
|
phases_data["phase3"] = _phase3_security_checklist(
|
|
@@ -1164,10 +1166,10 @@ def run_audit(
|
|
|
1164
1166
|
)
|
|
1165
1167
|
|
|
1166
1168
|
if 4 in phases_list:
|
|
1167
|
-
phases_data["phase4"] = _phase4_red_team_scenarios(
|
|
1169
|
+
phases_data["phase4"] = _phase4_red_team_scenarios(report_findings, auth_score)
|
|
1168
1170
|
|
|
1169
1171
|
if 5 in phases_list:
|
|
1170
|
-
phases_data["phase5"] = _phase5_blue_team_recommendations(
|
|
1172
|
+
phases_data["phase5"] = _phase5_blue_team_recommendations(report_findings, auth_score)
|
|
1171
1173
|
|
|
1172
1174
|
if 6 in phases_list:
|
|
1173
1175
|
phases_data["phase6"] = _phase6_verdict(
|
|
@@ -1227,7 +1229,7 @@ def run_audit(
|
|
|
1227
1229
|
"phases_run": phases_list,
|
|
1228
1230
|
"phases": phases_data,
|
|
1229
1231
|
"total_findings": len(all_findings),
|
|
1230
|
-
"findings":
|
|
1232
|
+
"findings": report_findings,
|
|
1231
1233
|
"report_path": str(report_path),
|
|
1232
1234
|
}
|
|
1233
1235
|
|
|
@@ -64,6 +64,17 @@ import quick_scan # noqa: E402
|
|
|
64
64
|
# ---------------------------------------------------------------------------
|
|
65
65
|
logger = setup_logging("007-score-calculator")
|
|
66
66
|
|
|
67
|
+
_SENSITIVE_FINDING_KEYS = {
|
|
68
|
+
"snippet",
|
|
69
|
+
"secret",
|
|
70
|
+
"token",
|
|
71
|
+
"password",
|
|
72
|
+
"access_token",
|
|
73
|
+
"app_secret",
|
|
74
|
+
"authorization_code",
|
|
75
|
+
"client_secret",
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
|
|
68
79
|
# ---------------------------------------------------------------------------
|
|
69
80
|
# Positive-signal patterns (auth, encryption, resilience, monitoring)
|
|
@@ -360,6 +371,51 @@ def _bar(score: float, width: int = 20) -> str:
|
|
|
360
371
|
return "[" + "#" * filled + "." * (width - filled) + "]"
|
|
361
372
|
|
|
362
373
|
|
|
374
|
+
def _redact_report_value(value):
|
|
375
|
+
"""Recursively redact sensitive values from report payloads."""
|
|
376
|
+
if isinstance(value, dict):
|
|
377
|
+
return {key: _redact_report_value(value[key]) for key in value}
|
|
378
|
+
if isinstance(value, list):
|
|
379
|
+
return [_redact_report_value(item) for item in value]
|
|
380
|
+
return value
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def redact_findings_for_report(findings: list[dict]) -> list[dict]:
|
|
384
|
+
"""Return findings safe to serialize in user-facing reports."""
|
|
385
|
+
redacted: list[dict] = []
|
|
386
|
+
|
|
387
|
+
for finding in findings:
|
|
388
|
+
safe_finding: dict = {}
|
|
389
|
+
finding_type = str(finding.get("type", "")).lower()
|
|
390
|
+
|
|
391
|
+
for key, value in finding.items():
|
|
392
|
+
key_lower = key.lower()
|
|
393
|
+
if key_lower in _SENSITIVE_FINDING_KEYS:
|
|
394
|
+
safe_finding[key] = "[redacted]"
|
|
395
|
+
continue
|
|
396
|
+
if finding_type == "secret" and key_lower in {"entropy", "match", "raw", "value"}:
|
|
397
|
+
safe_finding[key] = "[redacted]"
|
|
398
|
+
continue
|
|
399
|
+
safe_finding[key] = _redact_report_value(value)
|
|
400
|
+
|
|
401
|
+
redacted.append(safe_finding)
|
|
402
|
+
|
|
403
|
+
return redacted
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def build_safe_scanner_summaries(scanner_summaries: dict[str, dict]) -> dict[str, dict]:
|
|
407
|
+
"""Return scanner summaries with primitive numeric values only."""
|
|
408
|
+
safe_summaries: dict[str, dict] = {}
|
|
409
|
+
|
|
410
|
+
for scanner_name, summary in scanner_summaries.items():
|
|
411
|
+
safe_summaries[scanner_name] = {
|
|
412
|
+
"findings": int(summary.get("findings", 0)),
|
|
413
|
+
"score": float(summary.get("score", 0)),
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return safe_summaries
|
|
417
|
+
|
|
418
|
+
|
|
363
419
|
def format_text_report(
|
|
364
420
|
target: str,
|
|
365
421
|
domain_scores: dict[str, float],
|
|
@@ -430,6 +486,7 @@ def build_json_report(
|
|
|
430
486
|
elapsed: float,
|
|
431
487
|
) -> dict:
|
|
432
488
|
"""Build a structured JSON report."""
|
|
489
|
+
safe_findings = redact_findings_for_report(all_findings)
|
|
433
490
|
return {
|
|
434
491
|
"report": "score_calculator",
|
|
435
492
|
"target": target,
|
|
@@ -444,7 +501,7 @@ def build_json_report(
|
|
|
444
501
|
"emoji": verdict["emoji"],
|
|
445
502
|
},
|
|
446
503
|
"scanner_summaries": scanner_summaries,
|
|
447
|
-
"findings":
|
|
504
|
+
"findings": safe_findings,
|
|
448
505
|
}
|
|
449
506
|
|
|
450
507
|
|
|
@@ -564,6 +621,9 @@ def run_score(
|
|
|
564
621
|
all_findings_raw = secrets_findings + dep_findings + inj_findings + quick_findings
|
|
565
622
|
all_findings = _deduplicate_findings(all_findings_raw)
|
|
566
623
|
total_findings = len(all_findings)
|
|
624
|
+
safe_findings = redact_findings_for_report(all_findings)
|
|
625
|
+
safe_total_findings = len(safe_findings)
|
|
626
|
+
safe_scanner_summaries = build_safe_scanner_summaries(scanner_summaries)
|
|
567
627
|
|
|
568
628
|
logger.info(
|
|
569
629
|
"Aggregated %d raw findings -> %d unique (deduplicated)",
|
|
@@ -613,8 +673,8 @@ def run_score(
|
|
|
613
673
|
result=f"final_score={final_score}, verdict={verdict['label']}",
|
|
614
674
|
details={
|
|
615
675
|
"domain_scores": domain_scores,
|
|
616
|
-
"total_findings":
|
|
617
|
-
"scanner_summaries":
|
|
676
|
+
"total_findings": safe_total_findings,
|
|
677
|
+
"scanner_summaries": safe_scanner_summaries,
|
|
618
678
|
"duration_seconds": round(elapsed, 3),
|
|
619
679
|
},
|
|
620
680
|
)
|
|
@@ -627,9 +687,9 @@ def run_score(
|
|
|
627
687
|
domain_scores=domain_scores,
|
|
628
688
|
final_score=final_score,
|
|
629
689
|
verdict=verdict,
|
|
630
|
-
scanner_summaries=
|
|
690
|
+
scanner_summaries=safe_scanner_summaries,
|
|
631
691
|
all_findings=all_findings,
|
|
632
|
-
total_findings=
|
|
692
|
+
total_findings=safe_total_findings,
|
|
633
693
|
elapsed=elapsed,
|
|
634
694
|
)
|
|
635
695
|
|
|
@@ -641,8 +701,8 @@ def run_score(
|
|
|
641
701
|
domain_scores=domain_scores,
|
|
642
702
|
final_score=final_score,
|
|
643
703
|
verdict=verdict,
|
|
644
|
-
scanner_summaries=
|
|
645
|
-
total_findings=
|
|
704
|
+
scanner_summaries=safe_scanner_summaries,
|
|
705
|
+
total_findings=safe_total_findings,
|
|
646
706
|
elapsed=elapsed,
|
|
647
707
|
))
|
|
648
708
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<meta charset="UTF-8">
|
|
21
21
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
22
22
|
<title>Generative Art Viewer</title>
|
|
23
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
|
23
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js" integrity="sha512-bcfltY+lNLlNxz38yBBm/HLaUB1gTV6I0e+fahbF9pS6roIdzUytozWdnFV8ZnM6cSAG5EbmO0ag0a/fLZSG4Q==" crossorigin="anonymous"></script>
|
|
24
24
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
25
25
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
26
26
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
|
|
@@ -596,4 +596,4 @@
|
|
|
596
596
|
});
|
|
597
597
|
</script>
|
|
598
598
|
</body>
|
|
599
|
-
</html>
|
|
599
|
+
</html>
|
|
@@ -45,10 +45,9 @@ Verify CLI is logged in:
|
|
|
45
45
|
apify info # Should return your username
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
If not logged in, check if `APIFY_TOKEN` environment variable is defined. If not, ask the user to generate one at https://console.apify.com/settings/integrations, then:
|
|
48
|
+
If not logged in, check if `APIFY_TOKEN` environment variable is defined. If not, ask the user to generate one at https://console.apify.com/settings/integrations, add it to their shell or secret manager without putting the literal token in command history, then run:
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
export APIFY_TOKEN="your_token_here"
|
|
52
51
|
apify login
|
|
53
52
|
```
|
|
54
53
|
|
|
@@ -33,12 +33,12 @@ Reference the [cli-start template Dockerfile](https://github.com/apify/actor-tem
|
|
|
33
33
|
```dockerfile
|
|
34
34
|
FROM apify/actor-node:20
|
|
35
35
|
|
|
36
|
-
# Install ubi
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
# Install ubi from a package source or a verified release artifact
|
|
37
|
+
# Example: use your base image package manager or vendor a pinned binary in the build context
|
|
38
|
+
# RUN apt-get update && apt-get install -y ubi
|
|
39
39
|
|
|
40
40
|
# Install your CLI tool from GitHub releases (example)
|
|
41
|
-
# RUN
|
|
41
|
+
# RUN install -m 0755 ./vendor/your-tool /usr/local/bin/your-tool
|
|
42
42
|
|
|
43
43
|
# Or install apify-cli and jq manually
|
|
44
44
|
RUN npm install -g apify-cli
|
|
@@ -1,33 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Community Guidelines
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone.
|
|
6
|
-
|
|
7
|
-
## Our Standards
|
|
8
|
-
|
|
9
|
-
Examples of behavior that contributes to creating a positive environment include:
|
|
10
|
-
|
|
11
|
-
- Using welcoming and inclusive language
|
|
12
|
-
- Being respectful of differing viewpoints and experiences
|
|
13
|
-
- Gracefully accepting constructive criticism
|
|
14
|
-
- Focusing on what is best for the community
|
|
15
|
-
- Showing empathy towards other community members
|
|
16
|
-
|
|
17
|
-
Examples of unacceptable behavior by participants include:
|
|
18
|
-
|
|
19
|
-
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
20
|
-
- Trolling, insulting/derogatory comments, and personal or political attacks
|
|
21
|
-
- Public or private harassment
|
|
22
|
-
- Publishing others' private information without explicit permission
|
|
23
|
-
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
24
|
-
|
|
25
|
-
## Enforcement
|
|
26
|
-
|
|
27
|
-
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
28
|
-
|
|
29
|
-
## Attribution
|
|
30
|
-
|
|
31
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1.
|
|
32
|
-
|
|
33
|
-
[homepage]: https://www.contributor-covenant.org
|
|
3
|
+
The canonical project policy now lives in the repository root at
|
|
4
|
+
[`CODE_OF_CONDUCT.md`](../../CODE_OF_CONDUCT.md).
|
|
@@ -83,16 +83,34 @@ export async function loadSkillBodies(
|
|
|
83
83
|
): Promise<string[]> {
|
|
84
84
|
const bodies: string[] = [];
|
|
85
85
|
const rootPath = path.resolve(skillsRoot);
|
|
86
|
+
const rootRealPath = await fs.promises.realpath(rootPath);
|
|
86
87
|
|
|
87
88
|
for (const meta of metas) {
|
|
88
|
-
const
|
|
89
|
-
const relativePath = path.relative(rootPath,
|
|
89
|
+
const skillDirPath = path.resolve(rootPath, meta.path);
|
|
90
|
+
const relativePath = path.relative(rootPath, skillDirPath);
|
|
90
91
|
|
|
91
92
|
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
92
93
|
throw new Error(`Skill path escapes skills root: ${meta.id}`);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
const
|
|
96
|
+
const skillDirStat = await fs.promises.lstat(skillDirPath);
|
|
97
|
+
if (!skillDirStat.isDirectory() || skillDirStat.isSymbolicLink()) {
|
|
98
|
+
throw new Error(`Skill directory must be a regular directory inside the skills root: ${meta.id}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const fullPath = path.join(skillDirPath, "SKILL.md");
|
|
102
|
+
const skillFileStat = await fs.promises.lstat(fullPath);
|
|
103
|
+
if (!skillFileStat.isFile() || skillFileStat.isSymbolicLink()) {
|
|
104
|
+
throw new Error(`SKILL.md must be a regular file inside the skills root: ${meta.id}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const realPath = await fs.promises.realpath(fullPath);
|
|
108
|
+
const realRelativePath = path.relative(rootRealPath, realPath);
|
|
109
|
+
if (realRelativePath.startsWith("..") || path.isAbsolute(realRelativePath)) {
|
|
110
|
+
throw new Error(`SKILL.md resolves outside the skills root: ${meta.id}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const text = await fs.promises.readFile(realPath, "utf8");
|
|
96
114
|
bodies.push(text);
|
|
97
115
|
}
|
|
98
116
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Security Findings Triage Addendum (2026-03-18)
|
|
2
|
+
|
|
3
|
+
This addendum supersedes the previous Jetski loader assessment in
|
|
4
|
+
`security-findings-triage-2026-03-15.md`.
|
|
5
|
+
|
|
6
|
+
## Correction
|
|
7
|
+
|
|
8
|
+
- Finding: `Example loader trusts manifest paths, enabling file read`
|
|
9
|
+
- Path: `docs/integrations/jetski-gemini-loader/loader.ts`
|
|
10
|
+
- Previous triage status on 2026-03-15: `obsolete/not reproducible on current HEAD`
|
|
11
|
+
- Corrected assessment: the loader was still reproducible via a symlinked
|
|
12
|
+
`SKILL.md` that resolved outside `skillsRoot`. A local proof read the linked
|
|
13
|
+
file contents successfully.
|
|
14
|
+
|
|
15
|
+
## Current Status
|
|
16
|
+
|
|
17
|
+
- The loader now rejects symlinked skill directories and symlinked `SKILL.md`
|
|
18
|
+
files.
|
|
19
|
+
- The loader now resolves the real path for `SKILL.md` and rejects any target
|
|
20
|
+
outside the configured `skillsRoot`.
|
|
21
|
+
- Regression coverage lives in
|
|
22
|
+
`tools/scripts/tests/jetski_gemini_loader.test.js`.
|
|
@@ -1,42 +1,46 @@
|
|
|
1
|
-
# Walkthrough: Release v8.
|
|
1
|
+
# Walkthrough: Release v8.2.0 Maintenance Sweep
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
This walkthrough captures the maintainer-side documentation and release
|
|
5
|
+
This walkthrough captures the maintainer-side documentation and release publication work for **v8.2.0** after the 2026-03-18 maintenance sweep.
|
|
6
6
|
|
|
7
7
|
## Changes Verified
|
|
8
8
|
|
|
9
9
|
### 1. Community PR batch integrated
|
|
10
10
|
|
|
11
|
-
- **PR #
|
|
12
|
-
- **PR #
|
|
13
|
-
- **PR #
|
|
14
|
-
- **PR #
|
|
15
|
-
- **PR #
|
|
16
|
-
- **PR #
|
|
11
|
+
- **PR #333**: Repaired missing required frontmatter fields in `skill-anatomy` and `adapter-patterns`
|
|
12
|
+
- **PR #336**: Added `astro`, `hono`, `pydantic-ai`, and `sveltekit`
|
|
13
|
+
- **PR #338**: Repaired malformed markdown in `browser-extension-builder`
|
|
14
|
+
- **PR #343**: Added missing metadata labels to `devcontainer-setup`
|
|
15
|
+
- **PR #340**: Added `openclaw-github-repo-commander`
|
|
16
|
+
- **PR #334**: Added `goldrush-api`
|
|
17
|
+
- **PR #345**: Added `Wolfe-Jam/faf-skills` to README source attributions
|
|
17
18
|
|
|
18
19
|
### 2. Release-facing docs refreshed
|
|
19
20
|
|
|
20
21
|
- **README.md**:
|
|
21
|
-
- Current release updated to **v8.
|
|
22
|
+
- Current release updated to **v8.2.0**
|
|
22
23
|
- Release summary text aligned with the merged PR batch
|
|
23
24
|
- Contributor acknowledgements kept in sync with the latest merge set
|
|
24
25
|
- **docs/users/getting-started.md**:
|
|
25
|
-
- Version header updated to **v8.
|
|
26
|
-
- **docs/users/faq.md**:
|
|
27
|
-
- Active `skill-review` workflow guidance retained as the current contributor check
|
|
26
|
+
- Version header updated to **v8.2.0**
|
|
28
27
|
- **CHANGELOG.md**:
|
|
29
|
-
- Added the release notes section for **8.
|
|
28
|
+
- Added the release notes section for **8.2.0**
|
|
30
29
|
|
|
31
|
-
### 3.
|
|
30
|
+
### 3. Maintenance fixes verified
|
|
31
|
+
|
|
32
|
+
- **Issue #344**: Corrected `.claude-plugin/marketplace.json` to use `source: "./"` and added a regression test for the Claude Code marketplace entry
|
|
33
|
+
- **.github/MAINTENANCE.md**: Documented the maintainer flow for fork-gated workflows and stale PR metadata
|
|
34
|
+
|
|
35
|
+
### 4. Release protocol executed
|
|
32
36
|
|
|
33
37
|
- `npm run release:preflight`
|
|
34
38
|
- `npm run security:docs`
|
|
35
39
|
- `npm run validate:strict` (diagnostic, optional blocker)
|
|
36
|
-
- `npm run release:prepare -- 8.
|
|
37
|
-
- `npm run release:publish -- 8.
|
|
40
|
+
- `npm run release:prepare -- 8.2.0`
|
|
41
|
+
- `npm run release:publish -- 8.2.0`
|
|
38
42
|
|
|
39
43
|
## Expected Outcome
|
|
40
44
|
|
|
41
45
|
- Documentation, changelog, and generated metadata all agree on the release state.
|
|
42
|
-
- The repository
|
|
46
|
+
- The repository published the `v8.2.0` tag and GitHub release successfully.
|
|
@@ -793,7 +793,7 @@ public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>
|
|
|
793
793
|
|
|
794
794
|
## Resources
|
|
795
795
|
|
|
796
|
-
- **assets/service-template.cs**: Complete service implementation template
|
|
797
|
-
- **assets/repository-template.cs**: Repository pattern implementation
|
|
796
|
+
- **assets/service-template.cs.template**: Complete service implementation template
|
|
797
|
+
- **assets/repository-template.cs.template**: Repository pattern implementation
|
|
798
798
|
- **references/ef-core-best-practices.md**: EF Core optimization guide
|
|
799
799
|
- **references/dapper-patterns.md**: Advanced Dapper usage patterns
|
|
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import argparse
|
|
21
21
|
import asyncio
|
|
22
|
+
import html
|
|
22
23
|
import json
|
|
23
24
|
import os
|
|
24
25
|
import sys
|
|
@@ -47,6 +48,15 @@ db = Database()
|
|
|
47
48
|
db.init()
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
def _mask_secret(value: str, keep: int = 4) -> str:
|
|
52
|
+
"""Mask secret-like values before showing them in terminal output."""
|
|
53
|
+
if not value:
|
|
54
|
+
return "(hidden)"
|
|
55
|
+
if len(value) <= keep:
|
|
56
|
+
return "*" * len(value)
|
|
57
|
+
return f"{value[:keep]}...masked"
|
|
58
|
+
|
|
59
|
+
|
|
50
60
|
# ── OAuth Callback Server ────────────────────────────────────────────────────
|
|
51
61
|
|
|
52
62
|
class OAuthCallbackHandler(BaseHTTPRequestHandler):
|
|
@@ -71,7 +81,8 @@ class OAuthCallbackHandler(BaseHTTPRequestHandler):
|
|
|
71
81
|
self.send_response(400)
|
|
72
82
|
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
73
83
|
self.end_headers()
|
|
74
|
-
|
|
84
|
+
safe_error = html.escape(error, quote=True)
|
|
85
|
+
self.wfile.write(f"<html><body><h2>Erro: {safe_error}</h2></body></html>".encode())
|
|
75
86
|
else:
|
|
76
87
|
self.send_response(404)
|
|
77
88
|
self.end_headers()
|
|
@@ -84,7 +95,7 @@ def wait_for_oauth_code() -> Optional[str]:
|
|
|
84
95
|
"""Inicia servidor local e espera pelo código de autorização."""
|
|
85
96
|
server = HTTPServer(("localhost", OAUTH_REDIRECT_PORT), OAuthCallbackHandler)
|
|
86
97
|
server.timeout = 120 # 2 minutos
|
|
87
|
-
print(
|
|
98
|
+
print("Aguardando autorização no callback OAuth local...")
|
|
88
99
|
print("(Timeout: 2 minutos)\n")
|
|
89
100
|
|
|
90
101
|
while OAuthCallbackHandler.authorization_code is None:
|
|
@@ -285,10 +296,8 @@ async def setup() -> None:
|
|
|
285
296
|
f"response_type=code"
|
|
286
297
|
)
|
|
287
298
|
|
|
288
|
-
print(
|
|
289
|
-
|
|
290
|
-
masked_url = auth_url.replace(app_id, app_id[:4] + "...masked") if app_id else auth_url
|
|
291
|
-
print(f"URL: {masked_url}\n")
|
|
299
|
+
print("\nAbrindo browser para autorização...")
|
|
300
|
+
print("A URL de autorização e o App ID não serão exibidos para evitar vazamento de credenciais.\n")
|
|
292
301
|
webbrowser.open(auth_url)
|
|
293
302
|
|
|
294
303
|
# Esperar callback
|