@iamnishankhan/opencode-kit-grok 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/bin/cli.js +46 -0
- package/kit/.opencode/AGENTS.md +392 -0
- package/kit/.opencode/README.md +71 -0
- package/kit/.opencode/USAGE.md +405 -0
- package/kit/.opencode/agents/backend-specialist.md +278 -0
- package/kit/.opencode/agents/code-archaeologist.md +114 -0
- package/kit/.opencode/agents/database-architect.md +233 -0
- package/kit/.opencode/agents/debugger.md +234 -0
- package/kit/.opencode/agents/devops-engineer.md +252 -0
- package/kit/.opencode/agents/documentation-writer.md +111 -0
- package/kit/.opencode/agents/explorer-agent.md +83 -0
- package/kit/.opencode/agents/frontend-specialist.md +606 -0
- package/kit/.opencode/agents/game-developer.md +169 -0
- package/kit/.opencode/agents/mobile-developer.md +385 -0
- package/kit/.opencode/agents/orchestrator.md +213 -0
- package/kit/.opencode/agents/penetration-tester.md +197 -0
- package/kit/.opencode/agents/performance-optimizer.md +194 -0
- package/kit/.opencode/agents/product-manager.md +120 -0
- package/kit/.opencode/agents/product-owner.md +103 -0
- package/kit/.opencode/agents/project-planner.md +418 -0
- package/kit/.opencode/agents/qa-automation-engineer.md +113 -0
- package/kit/.opencode/agents/security-auditor.md +179 -0
- package/kit/.opencode/agents/seo-specialist.md +119 -0
- package/kit/.opencode/agents/test-engineer.md +169 -0
- package/kit/.opencode/commands/brainstorm.md +113 -0
- package/kit/.opencode/commands/coordinate.md +72 -0
- package/kit/.opencode/commands/create.md +63 -0
- package/kit/.opencode/commands/debug.md +102 -0
- package/kit/.opencode/commands/deploy.md +175 -0
- package/kit/.opencode/commands/enhance.md +62 -0
- package/kit/.opencode/commands/orchestrate.md +241 -0
- package/kit/.opencode/commands/plan.md +89 -0
- package/kit/.opencode/commands/preview.md +80 -0
- package/kit/.opencode/commands/remember.md +67 -0
- package/kit/.opencode/commands/status.md +86 -0
- package/kit/.opencode/commands/test.md +143 -0
- package/kit/.opencode/commands/verify.md +71 -0
- package/kit/.opencode/memory/MEMORY.md +6 -0
- package/kit/.opencode/memory/feedback-history.md +9 -0
- package/kit/.opencode/memory/project-conventions.md +16 -0
- package/kit/.opencode/memory/tech-decisions.md +10 -0
- package/kit/.opencode/memory/user-preferences.md +9 -0
- package/kit/.opencode/opencode.json +15 -0
- package/kit/.opencode/scripts/README.md +95 -0
- package/kit/.opencode/scripts/auto_preview.py +149 -0
- package/kit/.opencode/scripts/checklist.py +75 -0
- package/kit/.opencode/scripts/component_registry.py +238 -0
- package/kit/.opencode/scripts/dependency_graph.py +73 -0
- package/kit/.opencode/scripts/generate_manifest.py +44 -0
- package/kit/.opencode/scripts/session_manager.py +120 -0
- package/kit/.opencode/scripts/tests/test_toolkit.py +238 -0
- package/kit/.opencode/scripts/validate_kit.py +397 -0
- package/kit/.opencode/scripts/validation_runner.py +216 -0
- package/kit/.opencode/scripts/verify_all.py +85 -0
- package/kit/.opencode/skills/api-patterns/SKILL.md +83 -0
- package/kit/.opencode/skills/api-patterns/api-style.md +42 -0
- package/kit/.opencode/skills/api-patterns/auth.md +24 -0
- package/kit/.opencode/skills/api-patterns/documentation.md +26 -0
- package/kit/.opencode/skills/api-patterns/graphql.md +41 -0
- package/kit/.opencode/skills/api-patterns/rate-limiting.md +31 -0
- package/kit/.opencode/skills/api-patterns/response.md +37 -0
- package/kit/.opencode/skills/api-patterns/rest.md +40 -0
- package/kit/.opencode/skills/api-patterns/scripts/api_validator.py +211 -0
- package/kit/.opencode/skills/api-patterns/security-testing.md +122 -0
- package/kit/.opencode/skills/api-patterns/trpc.md +41 -0
- package/kit/.opencode/skills/api-patterns/versioning.md +22 -0
- package/kit/.opencode/skills/app-builder/SKILL.md +78 -0
- package/kit/.opencode/skills/app-builder/agent-coordination.md +71 -0
- package/kit/.opencode/skills/app-builder/feature-building.md +53 -0
- package/kit/.opencode/skills/app-builder/project-detection.md +45 -0
- package/kit/.opencode/skills/app-builder/scaffolding.md +110 -0
- package/kit/.opencode/skills/app-builder/tech-stack.md +41 -0
- package/kit/.opencode/skills/app-builder/templates/SKILL.md +39 -0
- package/kit/.opencode/skills/app-builder/templates/astro-static/TEMPLATE.md +78 -0
- package/kit/.opencode/skills/app-builder/templates/chrome-extension/TEMPLATE.md +96 -0
- package/kit/.opencode/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/kit/.opencode/skills/app-builder/templates/electron-desktop/TEMPLATE.md +97 -0
- package/kit/.opencode/skills/app-builder/templates/express-api/TEMPLATE.md +89 -0
- package/kit/.opencode/skills/app-builder/templates/flutter-app/TEMPLATE.md +93 -0
- package/kit/.opencode/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +97 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +126 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +125 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-static/TEMPLATE.md +174 -0
- package/kit/.opencode/skills/app-builder/templates/nuxt-app/TEMPLATE.md +127 -0
- package/kit/.opencode/skills/app-builder/templates/python-fastapi/TEMPLATE.md +94 -0
- package/kit/.opencode/skills/app-builder/templates/react-native-app/TEMPLATE.md +121 -0
- package/kit/.opencode/skills/architecture/SKILL.md +57 -0
- package/kit/.opencode/skills/architecture/context-discovery.md +43 -0
- package/kit/.opencode/skills/architecture/examples.md +94 -0
- package/kit/.opencode/skills/architecture/pattern-selection.md +68 -0
- package/kit/.opencode/skills/architecture/patterns-reference.md +50 -0
- package/kit/.opencode/skills/architecture/trade-off-analysis.md +77 -0
- package/kit/.opencode/skills/bash-linux/SKILL.md +201 -0
- package/kit/.opencode/skills/batch-operations/SKILL.md +105 -0
- package/kit/.opencode/skills/behavioral-modes/SKILL.md +253 -0
- package/kit/.opencode/skills/brainstorming/SKILL.md +178 -0
- package/kit/.opencode/skills/brainstorming/dynamic-questioning.md +350 -0
- package/kit/.opencode/skills/clean-code/SKILL.md +201 -0
- package/kit/.opencode/skills/code-review-checklist/SKILL.md +111 -0
- package/kit/.opencode/skills/code-review-graph/SKILL.md +305 -0
- package/kit/.opencode/skills/context-compression/SKILL.md +145 -0
- package/kit/.opencode/skills/coordinator-mode/SKILL.md +208 -0
- package/kit/.opencode/skills/database-design/SKILL.md +54 -0
- package/kit/.opencode/skills/database-design/database-selection.md +43 -0
- package/kit/.opencode/skills/database-design/indexing.md +39 -0
- package/kit/.opencode/skills/database-design/migrations.md +48 -0
- package/kit/.opencode/skills/database-design/optimization.md +36 -0
- package/kit/.opencode/skills/database-design/orm-selection.md +30 -0
- package/kit/.opencode/skills/database-design/schema-design.md +56 -0
- package/kit/.opencode/skills/database-design/scripts/schema_validator.py +172 -0
- package/kit/.opencode/skills/deployment-procedures/SKILL.md +243 -0
- package/kit/.opencode/skills/design-spec/SKILL.md +185 -0
- package/kit/.opencode/skills/design-spec/collection.md +106 -0
- package/kit/.opencode/skills/documentation-templates/SKILL.md +196 -0
- package/kit/.opencode/skills/frontend-architecture/SKILL.md +245 -0
- package/kit/.opencode/skills/frontend-design/SKILL.md +1222 -0
- package/kit/.opencode/skills/frontend-design/redesign.md +178 -0
- package/kit/.opencode/skills/frontend-design/scripts/accessibility_checker.py +145 -0
- package/kit/.opencode/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/kit/.opencode/skills/frontend-design/style-brutalist.md +92 -0
- package/kit/.opencode/skills/frontend-design/style-minimalist.md +85 -0
- package/kit/.opencode/skills/game-development/2d-games/SKILL.md +118 -0
- package/kit/.opencode/skills/game-development/3d-games/SKILL.md +134 -0
- package/kit/.opencode/skills/game-development/SKILL.md +169 -0
- package/kit/.opencode/skills/game-development/game-art/SKILL.md +184 -0
- package/kit/.opencode/skills/game-development/game-audio/SKILL.md +189 -0
- package/kit/.opencode/skills/game-development/game-design/SKILL.md +128 -0
- package/kit/.opencode/skills/game-development/mobile-games/SKILL.md +107 -0
- package/kit/.opencode/skills/game-development/multiplayer/SKILL.md +131 -0
- package/kit/.opencode/skills/game-development/pc-games/SKILL.md +143 -0
- package/kit/.opencode/skills/game-development/vr-ar/SKILL.md +122 -0
- package/kit/.opencode/skills/game-development/web-games/SKILL.md +149 -0
- package/kit/.opencode/skills/geo-fundamentals/SKILL.md +158 -0
- package/kit/.opencode/skills/geo-fundamentals/scripts/geo_checker.py +273 -0
- package/kit/.opencode/skills/i18n-localization/SKILL.md +156 -0
- package/kit/.opencode/skills/i18n-localization/scripts/i18n_checker.py +223 -0
- package/kit/.opencode/skills/intelligent-routing/SKILL.md +337 -0
- package/kit/.opencode/skills/lint-and-validate/SKILL.md +47 -0
- package/kit/.opencode/skills/lint-and-validate/scripts/lint_runner.py +184 -0
- package/kit/.opencode/skills/lint-and-validate/scripts/type_coverage.py +245 -0
- package/kit/.opencode/skills/mcp-builder/SKILL.md +205 -0
- package/kit/.opencode/skills/memory-system/SKILL.md +181 -0
- package/kit/.opencode/skills/mobile-design/SKILL.md +396 -0
- package/kit/.opencode/skills/mobile-design/decision-trees.md +516 -0
- package/kit/.opencode/skills/mobile-design/mobile-backend.md +491 -0
- package/kit/.opencode/skills/mobile-design/mobile-color-system.md +420 -0
- package/kit/.opencode/skills/mobile-design/mobile-debugging.md +122 -0
- package/kit/.opencode/skills/mobile-design/mobile-design-thinking.md +357 -0
- package/kit/.opencode/skills/mobile-design/mobile-navigation.md +458 -0
- package/kit/.opencode/skills/mobile-design/mobile-performance.md +767 -0
- package/kit/.opencode/skills/mobile-design/mobile-testing.md +356 -0
- package/kit/.opencode/skills/mobile-design/mobile-typography.md +433 -0
- package/kit/.opencode/skills/mobile-design/platform-android.md +666 -0
- package/kit/.opencode/skills/mobile-design/platform-ios.md +561 -0
- package/kit/.opencode/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/kit/.opencode/skills/mobile-design/touch-psychology.md +537 -0
- package/kit/.opencode/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +351 -0
- package/kit/.opencode/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -0
- package/kit/.opencode/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -0
- package/kit/.opencode/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -0
- package/kit/.opencode/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/kit/.opencode/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -0
- package/kit/.opencode/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -0
- package/kit/.opencode/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -0
- package/kit/.opencode/skills/nextjs-react-expert/9-cache-components.md +103 -0
- package/kit/.opencode/skills/nextjs-react-expert/SKILL.md +293 -0
- package/kit/.opencode/skills/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/kit/.opencode/skills/nextjs-react-expert/scripts/react_performance_checker.py +281 -0
- package/kit/.opencode/skills/nodejs-best-practices/SKILL.md +336 -0
- package/kit/.opencode/skills/parallel-agents/SKILL.md +192 -0
- package/kit/.opencode/skills/performance-profiling/SKILL.md +145 -0
- package/kit/.opencode/skills/performance-profiling/scripts/bundle_analyzer.py +120 -0
- package/kit/.opencode/skills/performance-profiling/scripts/lighthouse_audit.py +108 -0
- package/kit/.opencode/skills/plan-writing/SKILL.md +153 -0
- package/kit/.opencode/skills/powershell-windows/SKILL.md +169 -0
- package/kit/.opencode/skills/python-patterns/SKILL.md +443 -0
- package/kit/.opencode/skills/red-team-tactics/SKILL.md +201 -0
- package/kit/.opencode/skills/rust-pro/SKILL.md +177 -0
- package/kit/.opencode/skills/seo-fundamentals/SKILL.md +131 -0
- package/kit/.opencode/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/kit/.opencode/skills/server-management/SKILL.md +163 -0
- package/kit/.opencode/skills/simplify-code/SKILL.md +131 -0
- package/kit/.opencode/skills/skillify/SKILL.md +115 -0
- package/kit/.opencode/skills/systematic-debugging/SKILL.md +111 -0
- package/kit/.opencode/skills/tailwind-patterns/SKILL.md +271 -0
- package/kit/.opencode/skills/tdd-workflow/SKILL.md +151 -0
- package/kit/.opencode/skills/testing-patterns/SKILL.md +180 -0
- package/kit/.opencode/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/kit/.opencode/skills/verify-changes/SKILL.md +127 -0
- package/kit/.opencode/skills/vulnerability-scanner/SKILL.md +278 -0
- package/kit/.opencode/skills/vulnerability-scanner/checklists.md +124 -0
- package/kit/.opencode/skills/vulnerability-scanner/scripts/dependency_analyzer.py +144 -0
- package/kit/.opencode/skills/vulnerability-scanner/scripts/security_scan.py +410 -0
- package/kit/.opencode/skills/web-design-guidelines/SKILL.md +56 -0
- package/kit/.opencode/skills/webapp-testing/SKILL.md +189 -0
- package/kit/.opencode/skills/webapp-testing/scripts/playwright_runner.py +123 -0
- package/lib/init.js +61 -0
- package/package.json +21 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Auto Preview - OpenCode kit
|
|
4
|
+
==============================
|
|
5
|
+
Manages (start/stop/status) the local development server for previewing the application.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python .opencode/scripts/auto_preview.py start [port]
|
|
9
|
+
python .opencode/scripts/auto_preview.py stop
|
|
10
|
+
python .opencode/scripts/auto_preview.py status
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import json
|
|
17
|
+
import signal
|
|
18
|
+
import argparse
|
|
19
|
+
import subprocess
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
# Detect agent directory name dynamically
|
|
23
|
+
AGENT_DIR = Path(".agents") if Path(".agents").exists() else Path(".agents")
|
|
24
|
+
PID_FILE = AGENT_DIR / "preview.pid"
|
|
25
|
+
LOG_FILE = AGENT_DIR / "preview.log"
|
|
26
|
+
|
|
27
|
+
def get_project_root():
|
|
28
|
+
return Path(".").resolve()
|
|
29
|
+
|
|
30
|
+
def is_running(pid):
|
|
31
|
+
try:
|
|
32
|
+
os.kill(pid, 0)
|
|
33
|
+
return True
|
|
34
|
+
except OSError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def get_start_command(root):
|
|
38
|
+
pkg_file = root / "package.json"
|
|
39
|
+
if not pkg_file.exists():
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
with open(pkg_file, 'r') as f:
|
|
43
|
+
data = json.load(f)
|
|
44
|
+
|
|
45
|
+
scripts = data.get("scripts", {})
|
|
46
|
+
if "dev" in scripts:
|
|
47
|
+
return ["npm", "run", "dev"]
|
|
48
|
+
elif "start" in scripts:
|
|
49
|
+
return ["npm", "start"]
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def start_server(port=3000):
|
|
53
|
+
if PID_FILE.exists():
|
|
54
|
+
try:
|
|
55
|
+
pid = int(PID_FILE.read_text().strip())
|
|
56
|
+
if is_running(pid):
|
|
57
|
+
print(f"⚠️ Preview already running (PID: {pid})")
|
|
58
|
+
return
|
|
59
|
+
except:
|
|
60
|
+
pass # Invalid PID file
|
|
61
|
+
|
|
62
|
+
root = get_project_root()
|
|
63
|
+
cmd = get_start_command(root)
|
|
64
|
+
|
|
65
|
+
if not cmd:
|
|
66
|
+
print("❌ No 'dev' or 'start' script found in package.json")
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
# Add port env var if needed (simple heuristic)
|
|
70
|
+
env = os.environ.copy()
|
|
71
|
+
env["PORT"] = str(port)
|
|
72
|
+
|
|
73
|
+
print(f"🚀 Starting preview on port {port}...")
|
|
74
|
+
|
|
75
|
+
with open(LOG_FILE, "w") as log:
|
|
76
|
+
process = subprocess.Popen(
|
|
77
|
+
cmd,
|
|
78
|
+
cwd=str(root),
|
|
79
|
+
stdout=log,
|
|
80
|
+
stderr=log,
|
|
81
|
+
env=env,
|
|
82
|
+
shell=True # Required for npm on windows often, or consistent path handling
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
PID_FILE.write_text(str(process.pid))
|
|
86
|
+
print(f"✅ Preview started! (PID: {process.pid})")
|
|
87
|
+
print(f" Logs: {LOG_FILE}")
|
|
88
|
+
print(f" URL: http://localhost:{port}")
|
|
89
|
+
|
|
90
|
+
def stop_server():
|
|
91
|
+
if not PID_FILE.exists():
|
|
92
|
+
print("ℹ️ No preview server found.")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
pid = int(PID_FILE.read_text().strip())
|
|
97
|
+
if is_running(pid):
|
|
98
|
+
# Try gentle kill first
|
|
99
|
+
os.kill(pid, signal.SIGTERM) if sys.platform != 'win32' else subprocess.call(['taskkill', '/F', '/T', '/PID', str(pid)])
|
|
100
|
+
print(f"🛑 Preview stopped (PID: {pid})")
|
|
101
|
+
else:
|
|
102
|
+
print("ℹ️ Process was not running.")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"❌ Error stopping server: {e}")
|
|
105
|
+
finally:
|
|
106
|
+
if PID_FILE.exists():
|
|
107
|
+
PID_FILE.unlink()
|
|
108
|
+
|
|
109
|
+
def status_server():
|
|
110
|
+
running = False
|
|
111
|
+
pid = None
|
|
112
|
+
url = "Unknown"
|
|
113
|
+
|
|
114
|
+
if PID_FILE.exists():
|
|
115
|
+
try:
|
|
116
|
+
pid = int(PID_FILE.read_text().strip())
|
|
117
|
+
if is_running(pid):
|
|
118
|
+
running = True
|
|
119
|
+
# Heuristic for URL, strictly we should save it
|
|
120
|
+
url = "http://localhost:3000"
|
|
121
|
+
except:
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
print("\n=== Preview Status ===")
|
|
125
|
+
if running:
|
|
126
|
+
print(f"✅ Status: Running")
|
|
127
|
+
print(f"🔢 PID: {pid}")
|
|
128
|
+
print(f"🌐 URL: {url} (Likely)")
|
|
129
|
+
print(f"📝 Logs: {LOG_FILE}")
|
|
130
|
+
else:
|
|
131
|
+
print("⚪ Status: Stopped")
|
|
132
|
+
print("===================\n")
|
|
133
|
+
|
|
134
|
+
def main():
|
|
135
|
+
parser = argparse.ArgumentParser()
|
|
136
|
+
parser.add_argument("action", choices=["start", "stop", "status"])
|
|
137
|
+
parser.add_argument("port", nargs="?", default="3000")
|
|
138
|
+
|
|
139
|
+
args = parser.parse_args()
|
|
140
|
+
|
|
141
|
+
if args.action == "start":
|
|
142
|
+
start_server(int(args.port))
|
|
143
|
+
elif args.action == "stop":
|
|
144
|
+
stop_server()
|
|
145
|
+
elif args.action == "status":
|
|
146
|
+
status_server()
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
main()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Run the fast OpenCode kit validation checklist."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from validation_runner import (
|
|
10
|
+
CONSOLE,
|
|
11
|
+
CheckSpec,
|
|
12
|
+
execute_suite,
|
|
13
|
+
locate_toolkit_root,
|
|
14
|
+
print_summary,
|
|
15
|
+
write_report,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
CORE_CHECKS = (
|
|
19
|
+
CheckSpec(
|
|
20
|
+
"Security Scan",
|
|
21
|
+
"skills/vulnerability-scanner/scripts/security_scan.py",
|
|
22
|
+
"P0 Security",
|
|
23
|
+
required=True,
|
|
24
|
+
args=("--output", "summary", "--fail-on", "high"),
|
|
25
|
+
),
|
|
26
|
+
CheckSpec("Lint Check", "skills/lint-and-validate/scripts/lint_runner.py", "P1 Code Quality", required=True),
|
|
27
|
+
CheckSpec("Type Coverage", "skills/lint-and-validate/scripts/type_coverage.py", "P1 Code Quality"),
|
|
28
|
+
CheckSpec("Schema Validation", "skills/database-design/scripts/schema_validator.py", "P2 Data Layer"),
|
|
29
|
+
CheckSpec("Test Runner", "skills/testing-patterns/scripts/test_runner.py", "P3 Testing"),
|
|
30
|
+
CheckSpec("UX Audit", "skills/frontend-design/scripts/ux_audit.py", "P4 UX & Accessibility"),
|
|
31
|
+
CheckSpec("Accessibility Check", "skills/frontend-design/scripts/accessibility_checker.py", "P4 UX & Accessibility"),
|
|
32
|
+
CheckSpec("SEO Check", "skills/seo-fundamentals/scripts/seo_checker.py", "P5 SEO"),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
URL_CHECKS = (
|
|
36
|
+
CheckSpec("Lighthouse Audit", "skills/performance-profiling/scripts/lighthouse_audit.py", "P6 Runtime Checks", target="url", required=True, timeout=180),
|
|
37
|
+
CheckSpec("Playwright Smoke Test", "skills/webapp-testing/scripts/playwright_runner.py", "P6 Runtime Checks", target="url", timeout=120),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main() -> int:
|
|
42
|
+
parser = argparse.ArgumentParser(description="Run the fast OpenCode kit validation checklist")
|
|
43
|
+
parser.add_argument("project", nargs="?", default=".", help="Project path to validate")
|
|
44
|
+
parser.add_argument("--url", help="Running application URL for Lighthouse and Playwright")
|
|
45
|
+
parser.add_argument("--skip-runtime", action="store_true", help="Skip URL-based runtime checks")
|
|
46
|
+
parser.add_argument("--stop-on-fail", action="store_true", help="Stop after the first failed check")
|
|
47
|
+
parser.add_argument("--report", type=Path, help="Write a machine-readable JSON report")
|
|
48
|
+
args = parser.parse_args()
|
|
49
|
+
|
|
50
|
+
project = Path(args.project).resolve()
|
|
51
|
+
if not project.is_dir():
|
|
52
|
+
parser.error(f"Project directory does not exist: {project}")
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
toolkit_root = locate_toolkit_root(project, __file__)
|
|
56
|
+
except FileNotFoundError as exc:
|
|
57
|
+
parser.error(str(exc))
|
|
58
|
+
|
|
59
|
+
started = datetime.now(timezone.utc)
|
|
60
|
+
CONSOLE.header("AG KIT - FAST CHECKLIST")
|
|
61
|
+
print(f"Project: {project}\nToolkit: {toolkit_root}\nURL: {args.url or 'not provided'}")
|
|
62
|
+
|
|
63
|
+
specs = list(CORE_CHECKS)
|
|
64
|
+
if not args.skip_runtime:
|
|
65
|
+
specs.extend(URL_CHECKS)
|
|
66
|
+
results = execute_suite(specs, toolkit_root, project, args.url, args.stop_on_fail)
|
|
67
|
+
success = print_summary("CHECKLIST SUMMARY", results, started)
|
|
68
|
+
if args.report:
|
|
69
|
+
write_report(args.report.resolve(), project, toolkit_root, results, started)
|
|
70
|
+
print(f"Report: {args.report.resolve()}")
|
|
71
|
+
return 0 if success else 1
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Build deterministic AG Kit component and runtime metadata."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import yaml # type: ignore
|
|
13
|
+
except ImportError: # pragma: no cover
|
|
14
|
+
yaml = None
|
|
15
|
+
|
|
16
|
+
SEMVER_RE = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def extract_frontmatter(path: Path) -> str:
|
|
20
|
+
text = path.read_text("utf-8", errors="replace")
|
|
21
|
+
if not text.startswith("---\n"):
|
|
22
|
+
raise ValueError(f"Missing YAML frontmatter: {path}")
|
|
23
|
+
end = text.find("\n---\n", 4)
|
|
24
|
+
if end < 0:
|
|
25
|
+
raise ValueError(f"Unterminated YAML frontmatter: {path}")
|
|
26
|
+
return text[4:end]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _fallback_frontmatter(raw: str) -> dict[str, Any]:
|
|
30
|
+
data: dict[str, Any] = {}
|
|
31
|
+
for line in raw.splitlines():
|
|
32
|
+
if not line or line[0].isspace() or line.lstrip().startswith("#"):
|
|
33
|
+
continue
|
|
34
|
+
match = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
|
|
35
|
+
if match:
|
|
36
|
+
key, value = match.groups()
|
|
37
|
+
data[key] = value.strip().strip('"\'')
|
|
38
|
+
return data
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_frontmatter(path: Path) -> dict[str, Any]:
|
|
42
|
+
raw = extract_frontmatter(path)
|
|
43
|
+
if yaml is not None:
|
|
44
|
+
loaded = yaml.safe_load(raw)
|
|
45
|
+
if not isinstance(loaded, dict):
|
|
46
|
+
raise ValueError(f"Frontmatter must be a mapping: {path}")
|
|
47
|
+
return dict(loaded)
|
|
48
|
+
return _fallback_frontmatter(raw)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def normalize_list(value: object) -> list[str]:
|
|
52
|
+
if isinstance(value, list):
|
|
53
|
+
return [str(item).strip() for item in value if str(item).strip()]
|
|
54
|
+
if isinstance(value, str):
|
|
55
|
+
return [item.strip() for item in value.split(",") if item.strip()]
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def is_semver(value: object) -> bool:
|
|
60
|
+
return isinstance(value, str) and SEMVER_RE.fullmatch(value) is not None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def compatible_range(version: str) -> str:
|
|
64
|
+
match = SEMVER_RE.fullmatch(version)
|
|
65
|
+
if not match:
|
|
66
|
+
raise ValueError(f"Invalid SemVer: {version}")
|
|
67
|
+
return version if int(match.group(1)) == 0 else f"^{version}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def version_satisfies(version: str, constraint: str) -> bool:
|
|
71
|
+
match = SEMVER_RE.fullmatch(version)
|
|
72
|
+
if not match:
|
|
73
|
+
return False
|
|
74
|
+
current = tuple(int(match.group(i)) for i in range(1, 4))
|
|
75
|
+
if constraint.startswith("^"):
|
|
76
|
+
base_match = SEMVER_RE.fullmatch(constraint[1:])
|
|
77
|
+
if not base_match:
|
|
78
|
+
return False
|
|
79
|
+
base = tuple(int(base_match.group(i)) for i in range(1, 4))
|
|
80
|
+
if base[0] > 0:
|
|
81
|
+
return current >= base and current[0] == base[0]
|
|
82
|
+
if base[1] > 0:
|
|
83
|
+
return current >= base and current[:2] == base[:2]
|
|
84
|
+
return current == base
|
|
85
|
+
return constraint == version
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _relative(root: Path, path: Path) -> str:
|
|
89
|
+
return path.relative_to(root).as_posix()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _runtime_manifest(root: Path) -> dict[str, Any]:
|
|
93
|
+
path = root / "antigravity.json"
|
|
94
|
+
if not path.is_file():
|
|
95
|
+
raise ValueError("Missing Antigravity runtime contract: antigravity.json")
|
|
96
|
+
data = json.loads(path.read_text("utf-8"))
|
|
97
|
+
if data.get("runtime") != "antigravity":
|
|
98
|
+
raise ValueError("antigravity.json runtime must be 'antigravity'")
|
|
99
|
+
return {
|
|
100
|
+
"path": "antigravity.json",
|
|
101
|
+
"schemaVersion": data.get("schemaVersion"),
|
|
102
|
+
"requiredCliCommands": data.get("requiredCliCommands", []),
|
|
103
|
+
"phases": data.get("phases", {}),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def build_manifest(root: Path) -> dict[str, Any]:
|
|
108
|
+
root = root.resolve()
|
|
109
|
+
kit_version = (root / "VERSION").read_text("utf-8").strip()
|
|
110
|
+
|
|
111
|
+
skills: dict[str, dict[str, Any]] = {}
|
|
112
|
+
for path in sorted((root / "skills").glob("*/SKILL.md")):
|
|
113
|
+
data = load_frontmatter(path)
|
|
114
|
+
name = path.parent.name
|
|
115
|
+
version = str(data.get("version", ""))
|
|
116
|
+
skills[name] = {
|
|
117
|
+
"path": _relative(root, path),
|
|
118
|
+
"version": version,
|
|
119
|
+
"allowedTools": normalize_list(data.get("allowed-tools")),
|
|
120
|
+
"scripts": sorted(_relative(root, item) for item in path.parent.glob("scripts/*.py")),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
agents: dict[str, dict[str, Any]] = {}
|
|
124
|
+
for path in sorted((root / "agent").glob("*.md")):
|
|
125
|
+
data = load_frontmatter(path)
|
|
126
|
+
dependencies: dict[str, str] = {}
|
|
127
|
+
for skill_name in normalize_list(data.get("skills")):
|
|
128
|
+
skill_version = str(skills.get(skill_name, {}).get("version", "0.0.0"))
|
|
129
|
+
dependencies[skill_name] = compatible_range(skill_version) if is_semver(skill_version) else skill_version
|
|
130
|
+
agents[path.stem] = {
|
|
131
|
+
"path": _relative(root, path),
|
|
132
|
+
"version": str(data.get("version", "")),
|
|
133
|
+
"model": str(data.get("model", "inherit")),
|
|
134
|
+
"tools": normalize_list(data.get("tools")),
|
|
135
|
+
"requires": {"skills": dependencies},
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
workflows: dict[str, dict[str, Any]] = {}
|
|
139
|
+
for path in sorted((root / "workflows").glob("*.md")):
|
|
140
|
+
data = load_frontmatter(path)
|
|
141
|
+
workflows[path.stem] = {
|
|
142
|
+
"path": _relative(root, path),
|
|
143
|
+
"version": str(data.get("version", "")),
|
|
144
|
+
"requires": {
|
|
145
|
+
"agents": normalize_list(data.get("requires_agents")),
|
|
146
|
+
"skills": normalize_list(data.get("requires_skills")),
|
|
147
|
+
},
|
|
148
|
+
"artifactOutputs": normalize_list(data.get("artifact_outputs")),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
rules: dict[str, dict[str, Any]] = {}
|
|
152
|
+
for path in sorted((root / "rules").glob("*.md")):
|
|
153
|
+
data = load_frontmatter(path)
|
|
154
|
+
rules[path.stem] = {
|
|
155
|
+
"path": _relative(root, path),
|
|
156
|
+
"version": str(data.get("version", "")),
|
|
157
|
+
"priority": str(data.get("priority", "")),
|
|
158
|
+
"trigger": str(data.get("trigger", "")),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
"$schema": "schemas/manifest.schema.json",
|
|
163
|
+
"schemaVersion": "1.0.0",
|
|
164
|
+
"kitVersion": kit_version,
|
|
165
|
+
"contracts": {
|
|
166
|
+
"antigravityRuntime": "1.0.0",
|
|
167
|
+
"componentApi": "1.0.0",
|
|
168
|
+
"memorySchema": "1.0.0",
|
|
169
|
+
"rulesApi": "1.0.0",
|
|
170
|
+
"workflowApi": "1.0.0",
|
|
171
|
+
},
|
|
172
|
+
"support": {
|
|
173
|
+
"primary": "Google Antigravity",
|
|
174
|
+
"official": ["Google Antigravity"],
|
|
175
|
+
"bestEffort": ["Gemini CLI", "other Markdown-compatible agent tools"],
|
|
176
|
+
"portableFormat": True,
|
|
177
|
+
},
|
|
178
|
+
"runtimes": {"antigravity": _runtime_manifest(root)},
|
|
179
|
+
"agents": agents,
|
|
180
|
+
"skills": skills,
|
|
181
|
+
"workflows": workflows,
|
|
182
|
+
"rules": rules,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def canonical_json(data: object) -> str:
|
|
187
|
+
return json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True) + "\n"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def component_files(root: Path) -> list[Path]:
|
|
191
|
+
"""Return every managed source file covered by the integrity lock."""
|
|
192
|
+
root = root.resolve()
|
|
193
|
+
files: list[Path] = []
|
|
194
|
+
for name in (
|
|
195
|
+
"VERSION",
|
|
196
|
+
"README.md",
|
|
197
|
+
"ARCHITECTURE.md",
|
|
198
|
+
"CHANGELOG.md",
|
|
199
|
+
"antigravity.json",
|
|
200
|
+
"hooks.json",
|
|
201
|
+
"mcp_config.json",
|
|
202
|
+
):
|
|
203
|
+
path = root / name
|
|
204
|
+
if path.is_file():
|
|
205
|
+
files.append(path)
|
|
206
|
+
for directory in ("agent", "skills", "workflows", "rules", "memory", "hooks", "schemas", "scripts"):
|
|
207
|
+
base = root / directory
|
|
208
|
+
if not base.is_dir():
|
|
209
|
+
continue
|
|
210
|
+
for path in base.rglob("*"):
|
|
211
|
+
if not path.is_file() or "__pycache__" in path.parts or path.suffix == ".pyc":
|
|
212
|
+
continue
|
|
213
|
+
files.append(path)
|
|
214
|
+
return sorted({path.resolve() for path in files})
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def sha256_file(path: Path) -> str:
|
|
218
|
+
digest = hashlib.sha256()
|
|
219
|
+
with path.open("rb") as handle:
|
|
220
|
+
for chunk in iter(lambda: handle.read(65536), b""):
|
|
221
|
+
digest.update(chunk)
|
|
222
|
+
return digest.hexdigest()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def build_lock(root: Path, manifest: dict[str, Any]) -> dict[str, Any]:
|
|
226
|
+
root = root.resolve()
|
|
227
|
+
hashes = {
|
|
228
|
+
path.relative_to(root).as_posix(): sha256_file(path)
|
|
229
|
+
for path in component_files(root)
|
|
230
|
+
if path.name != "manifest.lock.json"
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
"$schema": "schemas/manifest-lock.schema.json",
|
|
234
|
+
"schemaVersion": "1.0.0",
|
|
235
|
+
"kitVersion": manifest["kitVersion"],
|
|
236
|
+
"manifestSha256": hashlib.sha256(canonical_json(manifest).encode("utf-8")).hexdigest(),
|
|
237
|
+
"components": hashes,
|
|
238
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate a maintainable Mermaid dependency graph from manifest.json."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from component_registry import build_manifest
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def node_id(prefix: str, name: str) -> str:
|
|
12
|
+
return prefix + "_" + name.replace("-", "_")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def render(root: Path) -> str:
|
|
16
|
+
manifest = build_manifest(root)
|
|
17
|
+
lines = [
|
|
18
|
+
"# AG Kit Dependency Graph",
|
|
19
|
+
"",
|
|
20
|
+
"> Generated by `.opencode/scripts/dependency_graph.py`. Do not edit manually.",
|
|
21
|
+
"",
|
|
22
|
+
f"Kit version: `{manifest['kitVersion']}` · {len(manifest['agents'])} agents · {len(manifest['skills'])} skills · {len(manifest['workflows'])} workflows",
|
|
23
|
+
"",
|
|
24
|
+
"```mermaid",
|
|
25
|
+
"flowchart LR",
|
|
26
|
+
" subgraph Workflows",
|
|
27
|
+
]
|
|
28
|
+
for name in manifest["workflows"]:
|
|
29
|
+
lines.append(f' {node_id("W", name)}["/{name}"]')
|
|
30
|
+
lines.extend([" end", " subgraph Agents"])
|
|
31
|
+
for name in manifest["agents"]:
|
|
32
|
+
lines.append(f' {node_id("A", name)}["{name}"]')
|
|
33
|
+
lines.extend([" end", " subgraph Skills"])
|
|
34
|
+
for name in manifest["skills"]:
|
|
35
|
+
lines.append(f' {node_id("S", name)}["{name}"]')
|
|
36
|
+
lines.append(" end")
|
|
37
|
+
|
|
38
|
+
for workflow, data in manifest["workflows"].items():
|
|
39
|
+
for agent in data["requires"]["agents"]:
|
|
40
|
+
lines.append(f" {node_id('W', workflow)} --> {node_id('A', agent)}")
|
|
41
|
+
for skill in data["requires"]["skills"]:
|
|
42
|
+
lines.append(f" {node_id('W', workflow)} -.-> {node_id('S', skill)}")
|
|
43
|
+
for agent, data in manifest["agents"].items():
|
|
44
|
+
for skill in data["requires"]["skills"]:
|
|
45
|
+
lines.append(f" {node_id('A', agent)} --> {node_id('S', skill)}")
|
|
46
|
+
lines.extend(["```", "", "## Contracts", ""])
|
|
47
|
+
for key, value in manifest["contracts"].items():
|
|
48
|
+
lines.append(f"- `{key}`: `{value}`")
|
|
49
|
+
lines.append("")
|
|
50
|
+
return "\n".join(lines)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main() -> int:
|
|
54
|
+
parser = argparse.ArgumentParser(description="Generate AG Kit dependency graph")
|
|
55
|
+
parser.add_argument("path", nargs="?", default=None, help="Path to .agents")
|
|
56
|
+
parser.add_argument("--check", action="store_true", help="Fail when DEPENDENCY_GRAPH.md is stale")
|
|
57
|
+
args = parser.parse_args()
|
|
58
|
+
root = Path(args.path).resolve() if args.path else Path(__file__).resolve().parents[1]
|
|
59
|
+
output = root / "DEPENDENCY_GRAPH.md"
|
|
60
|
+
expected = render(root)
|
|
61
|
+
if args.check:
|
|
62
|
+
if not output.is_file() or output.read_text("utf-8") != expected:
|
|
63
|
+
print("DEPENDENCY_GRAPH.md is stale")
|
|
64
|
+
return 1
|
|
65
|
+
print("Dependency graph is synchronized.")
|
|
66
|
+
return 0
|
|
67
|
+
output.write_text(expected, "utf-8")
|
|
68
|
+
print(f"Wrote {output}")
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate or verify the AG Kit component registry and lock file."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from component_registry import build_lock, build_manifest, canonical_json
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def check_file(path: Path, expected: str) -> bool:
|
|
13
|
+
return path.is_file() and path.read_text("utf-8") == expected
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> int:
|
|
17
|
+
parser = argparse.ArgumentParser(description="Generate AG Kit manifest.json and manifest.lock.json")
|
|
18
|
+
parser.add_argument("path", nargs="?", default=None, help="Path to .agents")
|
|
19
|
+
parser.add_argument("--check", action="store_true", help="Fail when generated files are stale")
|
|
20
|
+
args = parser.parse_args()
|
|
21
|
+
|
|
22
|
+
root = Path(args.path).resolve() if args.path else Path(__file__).resolve().parents[1]
|
|
23
|
+
manifest = build_manifest(root)
|
|
24
|
+
manifest_text = canonical_json(manifest)
|
|
25
|
+
lock = build_lock(root, manifest)
|
|
26
|
+
lock_text = canonical_json(lock)
|
|
27
|
+
outputs = [(root / "manifest.json", manifest_text), (root / "manifest.lock.json", lock_text)]
|
|
28
|
+
|
|
29
|
+
if args.check:
|
|
30
|
+
stale = [path.name for path, expected in outputs if not check_file(path, expected)]
|
|
31
|
+
if stale:
|
|
32
|
+
print("Stale generated registry files: " + ", ".join(stale))
|
|
33
|
+
return 1
|
|
34
|
+
print("Component registry is synchronized.")
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
for path, content in outputs:
|
|
38
|
+
path.write_text(content, "utf-8")
|
|
39
|
+
print(f"Wrote {path}")
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
raise SystemExit(main())
|