@openhands/extensions 0.1.0 → 0.3.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/.agents/skills/custom-codereview-guide.md +25 -0
- package/.github/pull_request_template.md +38 -0
- package/.github/release.yml +14 -0
- package/.github/workflows/check-extensions.yml +72 -0
- package/.github/workflows/npm-publish.yml +89 -0
- package/.github/workflows/pr.yml +30 -0
- package/.github/workflows/release.yml +24 -0
- package/.github/workflows/tests.yml +25 -0
- package/.github/workflows/vulnerability-scan.yml +87 -0
- package/.release-please-manifest.json +3 -0
- package/AGENTS.md +132 -0
- package/README.md +10 -0
- package/analysis_results.md +162 -0
- package/index.d.ts +2 -0
- package/index.js +1 -0
- package/marketplaces/large-codebase.json +66 -0
- package/marketplaces/openhands-extensions.json +682 -0
- package/package.json +12 -10
- package/plugins/README.md +30 -0
- package/plugins/city-weather/.plugin/plugin.json +13 -0
- package/plugins/city-weather/README.md +145 -0
- package/plugins/city-weather/commands/now.md +56 -0
- package/plugins/cobol-modernization/.plugin/plugin.json +19 -0
- package/plugins/cobol-modernization/README.md +201 -0
- package/plugins/cobol-modernization/references/troubleshooting.md +18 -0
- package/plugins/cobol-modernization/skills/build-setup/SKILL.md +78 -0
- package/plugins/cobol-modernization/skills/build-setup/scripts/install-gnucobol.sh +32 -0
- package/plugins/cobol-modernization/skills/cobol-modernization-overview/SKILL.md +113 -0
- package/plugins/cobol-modernization/skills/mainfraime-removal/SKILL.md +62 -0
- package/plugins/cobol-modernization/skills/mainfraime-removal/references/cics-transformation-examples.md +45 -0
- package/plugins/cobol-modernization/skills/mainframe-planning/SKILL.md +78 -0
- package/plugins/cobol-modernization/skills/to-java-migration/SKILL.md +59 -0
- package/plugins/cobol-modernization/skills/to-java-migration/references/cobol-to-java-example.md +58 -0
- package/plugins/cobol-modernization/skills/to-java-migration/references/datatype-mappings.md +19 -0
- package/plugins/issue-duplicate-checker/.plugin/plugin.json +13 -0
- package/plugins/issue-duplicate-checker/README.md +51 -0
- package/plugins/issue-duplicate-checker/action.yml +349 -0
- package/plugins/issue-duplicate-checker/scripts/auto_close_duplicate_issues.py +569 -0
- package/plugins/issue-duplicate-checker/scripts/issue_duplicate_check_openhands.py +681 -0
- package/plugins/issue-duplicate-checker/scripts/post_duplicate_notice.js +220 -0
- package/plugins/issue-duplicate-checker/scripts/remove_duplicate_candidate_label.js +27 -0
- package/plugins/magic-test/.plugin/plugin.json +13 -0
- package/plugins/magic-test/skills/magic-word/SKILL.md +33 -0
- package/plugins/migration-scoring/.plugin/plugin.json +19 -0
- package/plugins/migration-scoring/README.md +244 -0
- package/plugins/migration-scoring/skills/migration-mapping/SKILL.md +72 -0
- package/plugins/migration-scoring/skills/migration-report/SKILL.md +118 -0
- package/plugins/migration-scoring/skills/migration-scoring-overview/SKILL.md +126 -0
- package/plugins/migration-scoring/skills/score-quality/SKILL.md +54 -0
- package/plugins/migration-scoring/skills/score-quality/references/scoring-criteria.md +30 -0
- package/plugins/migration-scoring/skills/score-style/SKILL.md +106 -0
- package/plugins/onboarding/.plugin/plugin.json +20 -0
- package/plugins/onboarding/README.md +30 -0
- package/plugins/onboarding/references/criteria.md +144 -0
- package/plugins/onboarding/skills/agent-readiness-report/README.md +23 -0
- package/plugins/onboarding/skills/agent-readiness-report/SKILL.md +122 -0
- package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_agent_instructions.sh +88 -0
- package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_build_env.sh +114 -0
- package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_feedback_loops.sh +133 -0
- package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_policy.sh +113 -0
- package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_workflows.sh +127 -0
- package/plugins/onboarding/skills/improve-agent-readiness/README.md +19 -0
- package/plugins/onboarding/skills/improve-agent-readiness/SKILL.md +167 -0
- package/plugins/onboarding/skills/setup-agents-md/README.md +15 -0
- package/plugins/onboarding/skills/setup-agents-md/SKILL.md +150 -0
- package/plugins/onboarding/skills/setup-openhands/README.md +20 -0
- package/plugins/onboarding/skills/setup-openhands/SKILL.md +56 -0
- package/plugins/onboarding/skills/setup-pr-review/README.md +23 -0
- package/plugins/onboarding/skills/setup-pr-review/SKILL.md +72 -0
- package/plugins/openhands/.plugin/plugin.json +13 -0
- package/plugins/openhands/README.md +52 -0
- package/plugins/openhands/SKILL.md +61 -0
- package/plugins/openhands/commands/create.md +55 -0
- package/plugins/openhands/commands/openhands-cloud.md +8 -0
- package/plugins/openhands/scripts/run.sh +69 -0
- package/plugins/pr-review/.plugin/plugin.json +13 -0
- package/plugins/pr-review/README.md +393 -0
- package/plugins/pr-review/action.yml +298 -0
- package/plugins/pr-review/scripts/agent_script.py +1282 -0
- package/plugins/pr-review/scripts/evaluate_review.py +655 -0
- package/plugins/pr-review/scripts/prompt.py +260 -0
- package/plugins/pr-review/workflows/pr-review-by-openhands.yml +51 -0
- package/plugins/pr-review/workflows/pr-review-evaluation.yml +85 -0
- package/plugins/qa-changes/.plugin/plugin.json +11 -0
- package/plugins/qa-changes/README.md +185 -0
- package/plugins/qa-changes/action.yml +181 -0
- package/plugins/qa-changes/scripts/agent_script.py +406 -0
- package/plugins/qa-changes/scripts/evaluate_qa_changes.py +385 -0
- package/plugins/qa-changes/scripts/prompt.py +174 -0
- package/plugins/qa-changes/workflows/qa-changes-by-openhands.yml +50 -0
- package/plugins/qa-changes/workflows/qa-changes-evaluation.yml +85 -0
- package/plugins/release-notes/.plugin/plugin.json +19 -0
- package/plugins/release-notes/README.md +283 -0
- package/plugins/release-notes/SKILL.md +83 -0
- package/plugins/release-notes/action.yml +117 -0
- package/plugins/release-notes/commands/release-notes.md +8 -0
- package/plugins/release-notes/scripts/agent_script.py +292 -0
- package/plugins/release-notes/scripts/generate_release_notes.py +733 -0
- package/plugins/release-notes/scripts/prompt.py +90 -0
- package/plugins/release-notes/scripts/validate_release_notes.py +328 -0
- package/plugins/release-notes/workflows/release-notes.yml +76 -0
- package/plugins/vulnerability-remediation/.plugin/plugin.json +19 -0
- package/plugins/vulnerability-remediation/README.md +217 -0
- package/plugins/vulnerability-remediation/action.yml +187 -0
- package/plugins/vulnerability-remediation/scripts/scan_and_remediate.py +561 -0
- package/plugins/vulnerability-remediation/workflows/vulnerability-scan.yml +87 -0
- package/pyproject.toml +12 -0
- package/release-please-config.json +16 -0
- package/scripts/build-skills-catalog.mjs +100 -0
- package/scripts/sync_extensions.py +494 -0
- package/scripts/sync_openhands_sdk_skill.py +264 -0
- package/skills/README.md +159 -0
- package/skills/add-javadoc/.plugin/plugin.json +18 -0
- package/skills/add-javadoc/README.md +40 -0
- package/skills/add-javadoc/SKILL.md +35 -0
- package/skills/add-javadoc/references/example.md +32 -0
- package/skills/add-skill/.plugin/plugin.json +18 -0
- package/skills/add-skill/README.md +67 -0
- package/skills/add-skill/SKILL.md +47 -0
- package/skills/add-skill/scripts/fetch_skill.py +259 -0
- package/skills/agent-creator/.plugin/plugin.json +20 -0
- package/skills/agent-creator/README.md +104 -0
- package/skills/agent-creator/SKILL.md +190 -0
- package/skills/agent-creator/commands/agent-creator.md +8 -0
- package/skills/agent-creator/references/fallback.md +117 -0
- package/skills/agent-memory/.plugin/plugin.json +18 -0
- package/skills/agent-memory/README.md +35 -0
- package/skills/agent-memory/SKILL.md +30 -0
- package/skills/agent-memory/commands/remember.md +8 -0
- package/skills/agent-sdk-builder/.plugin/plugin.json +18 -0
- package/skills/agent-sdk-builder/README.md +40 -0
- package/skills/agent-sdk-builder/SKILL.md +37 -0
- package/skills/agent-sdk-builder/commands/agent-builder.md +8 -0
- package/skills/azure-devops/.plugin/plugin.json +18 -0
- package/skills/azure-devops/README.md +55 -0
- package/skills/azure-devops/SKILL.md +50 -0
- package/skills/bitbucket/.plugin/plugin.json +17 -0
- package/skills/bitbucket/README.md +50 -0
- package/skills/bitbucket/SKILL.md +45 -0
- package/skills/code-review/.plugin/plugin.json +19 -0
- package/skills/code-review/README.md +18 -0
- package/skills/code-review/SKILL.md +208 -0
- package/skills/code-review/commands/codereview-roasted.md +8 -0
- package/skills/code-review/commands/codereview.md +8 -0
- package/skills/code-review/references/risk-evaluation.md +41 -0
- package/skills/code-review/references/supply-chain-security.md +31 -0
- package/skills/code-simplifier/.plugin/plugin.json +21 -0
- package/skills/code-simplifier/README.md +30 -0
- package/skills/code-simplifier/SKILL.md +91 -0
- package/skills/code-simplifier/commands/simplify.md +8 -0
- package/skills/code-simplifier/references/code-quality-review.md +86 -0
- package/skills/code-simplifier/references/code-reuse-review.md +63 -0
- package/skills/code-simplifier/references/efficiency-review.md +81 -0
- package/skills/datadog/.plugin/plugin.json +19 -0
- package/skills/datadog/README.md +100 -0
- package/skills/datadog/SKILL.md +95 -0
- package/skills/deno/.plugin/plugin.json +18 -0
- package/skills/deno/README.md +5 -0
- package/skills/deno/SKILL.md +99 -0
- package/skills/deno/references/README.md +6 -0
- package/skills/discord/.plugin/plugin.json +18 -0
- package/skills/discord/README.md +31 -0
- package/skills/discord/SKILL.md +109 -0
- package/skills/discord/__init__.py +0 -0
- package/skills/discord/references/REFERENCE.md +78 -0
- package/skills/discord/scripts/__init__.py +0 -0
- package/skills/discord/scripts/_http.py +127 -0
- package/skills/discord/scripts/post_webhook.py +106 -0
- package/skills/discord/scripts/send_message.py +102 -0
- package/skills/docker/.plugin/plugin.json +17 -0
- package/skills/docker/README.md +34 -0
- package/skills/docker/SKILL.md +29 -0
- package/skills/evidence-based-citations/.plugin/plugin.json +20 -0
- package/skills/evidence-based-citations/README.md +31 -0
- package/skills/evidence-based-citations/SKILL.md +59 -0
- package/skills/flarglebargle/.plugin/plugin.json +16 -0
- package/skills/flarglebargle/README.md +14 -0
- package/skills/flarglebargle/SKILL.md +9 -0
- package/skills/frontend-design/.plugin/plugin.json +21 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/README.md +42 -0
- package/skills/frontend-design/SKILL.md +42 -0
- package/skills/github/.plugin/plugin.json +19 -0
- package/skills/github/README.md +42 -0
- package/skills/github/SKILL.md +106 -0
- package/skills/github-pr-review/.plugin/plugin.json +18 -0
- package/skills/github-pr-review/README.md +145 -0
- package/skills/github-pr-review/SKILL.md +186 -0
- package/skills/github-pr-review/commands/github-pr-review.md +8 -0
- package/skills/github-pr-reviewer/.plugin/plugin.json +20 -0
- package/skills/github-pr-reviewer/README.md +34 -0
- package/skills/github-pr-reviewer/SKILL.md +89 -0
- package/skills/github-pr-reviewer/commands/pr-reviewer:setup.md +8 -0
- package/skills/github-repo-monitor/.plugin/plugin.json +22 -0
- package/skills/github-repo-monitor/README.md +70 -0
- package/skills/github-repo-monitor/SKILL.md +316 -0
- package/skills/github-repo-monitor/commands/github-monitor:poll.md +8 -0
- package/skills/github-repo-monitor/references/github-api.md +241 -0
- package/skills/github-repo-monitor/references/state-schema.md +160 -0
- package/skills/github-repo-monitor/scripts/main.py +915 -0
- package/skills/github-repo-monitor/tests/test_main.py +400 -0
- package/skills/gitlab/.plugin/plugin.json +17 -0
- package/skills/gitlab/README.md +37 -0
- package/skills/gitlab/SKILL.md +32 -0
- package/skills/incident-retrospective/.plugin/plugin.json +21 -0
- package/skills/incident-retrospective/README.md +34 -0
- package/skills/incident-retrospective/SKILL.md +98 -0
- package/skills/incident-retrospective/commands/incident-retro:setup.md +8 -0
- package/skills/index.d.ts +11 -0
- package/skills/index.js +465 -0
- package/skills/iterate/.plugin/plugin.json +13 -0
- package/skills/iterate/README.md +25 -0
- package/skills/iterate/SKILL.md +399 -0
- package/skills/iterate/commands/babysit.md +8 -0
- package/skills/iterate/commands/iterate.md +8 -0
- package/skills/iterate/commands/verify.md +8 -0
- package/skills/iterate/references/heuristics.md +58 -0
- package/skills/iterate/references/verification.md +96 -0
- package/skills/jupyter/.plugin/plugin.json +18 -0
- package/skills/jupyter/README.md +55 -0
- package/skills/jupyter/SKILL.md +50 -0
- package/skills/kubernetes/.plugin/plugin.json +18 -0
- package/skills/kubernetes/README.md +53 -0
- package/skills/kubernetes/SKILL.md +48 -0
- package/skills/learn-from-code-review/.plugin/plugin.json +19 -0
- package/skills/learn-from-code-review/README.md +64 -0
- package/skills/learn-from-code-review/SKILL.md +186 -0
- package/skills/learn-from-code-review/commands/learn-from-reviews.md +8 -0
- package/skills/linear/.plugin/plugin.json +19 -0
- package/skills/linear/README.md +58 -0
- package/skills/linear/SKILL.md +213 -0
- package/skills/linear-triage/.plugin/plugin.json +21 -0
- package/skills/linear-triage/README.md +34 -0
- package/skills/linear-triage/SKILL.md +91 -0
- package/skills/linear-triage/commands/linear-triage:setup.md +8 -0
- package/skills/notion/.plugin/plugin.json +17 -0
- package/skills/notion/README.md +114 -0
- package/skills/notion/SKILL.md +109 -0
- package/skills/npm/.plugin/plugin.json +17 -0
- package/skills/npm/README.md +14 -0
- package/skills/npm/SKILL.md +9 -0
- package/skills/openhands-api/.plugin/plugin.json +22 -0
- package/skills/openhands-api/README.md +48 -0
- package/skills/openhands-api/SKILL.md +399 -0
- package/skills/openhands-api/references/README.md +33 -0
- package/skills/openhands-api/references/TROUBLESHOOTING.md +81 -0
- package/skills/openhands-api/references/example_prompt.md +12 -0
- package/skills/openhands-api/scripts/openhands_api.py +606 -0
- package/skills/openhands-api/scripts/openhands_api.ts +252 -0
- package/skills/openhands-automation/.plugin/plugin.json +19 -0
- package/skills/openhands-automation/README.md +89 -0
- package/skills/openhands-automation/SKILL.md +875 -0
- package/skills/openhands-automation/commands/automation:create.md +8 -0
- package/skills/openhands-automation/references/ab-testing.md +185 -0
- package/skills/openhands-automation/references/custom-automation.md +644 -0
- package/skills/openhands-sdk/.plugin/plugin.json +20 -0
- package/skills/openhands-sdk/README.md +22 -0
- package/skills/openhands-sdk/SKILL.md +230 -0
- package/skills/openhands-sdk/commands/sdk.md +8 -0
- package/skills/pdflatex/.plugin/plugin.json +18 -0
- package/skills/pdflatex/README.md +39 -0
- package/skills/pdflatex/SKILL.md +34 -0
- package/skills/prd/.plugin/plugin.json +19 -0
- package/skills/prd/README.md +28 -0
- package/skills/prd/SKILL.md +237 -0
- package/skills/prd/commands/prd.md +8 -0
- package/skills/qa-changes/README.md +18 -0
- package/skills/qa-changes/SKILL.md +229 -0
- package/skills/qa-changes/commands/qa-changes.md +8 -0
- package/skills/release-notes/README.md +24 -0
- package/skills/release-notes/SKILL.md +19 -0
- package/skills/release-notes/commands/release-notes.md +8 -0
- package/skills/research-brief/.plugin/plugin.json +20 -0
- package/skills/research-brief/README.md +34 -0
- package/skills/research-brief/SKILL.md +99 -0
- package/skills/research-brief/commands/research-brief:setup.md +8 -0
- package/skills/security/.plugin/plugin.json +18 -0
- package/skills/security/README.md +38 -0
- package/skills/security/SKILL.md +33 -0
- package/skills/skill-creator/.plugin/plugin.json +17 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/README.md +182 -0
- package/skills/skill-creator/SKILL.md +545 -0
- package/skills/skill-creator/references/output-patterns.md +82 -0
- package/skills/skill-creator/references/workflows.md +28 -0
- package/skills/skill-creator/scripts/init_skill.py +303 -0
- package/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/skills/slack-channel-monitor/.plugin/plugin.json +21 -0
- package/skills/slack-channel-monitor/README.md +91 -0
- package/skills/slack-channel-monitor/SKILL.md +276 -0
- package/skills/slack-channel-monitor/commands/slack-monitor:poll.md +8 -0
- package/skills/slack-channel-monitor/references/slack-api.md +207 -0
- package/skills/slack-channel-monitor/references/state-schema.md +180 -0
- package/skills/slack-channel-monitor/scripts/main.py +962 -0
- package/skills/slack-standup-digest/.plugin/plugin.json +21 -0
- package/skills/slack-standup-digest/README.md +34 -0
- package/skills/slack-standup-digest/SKILL.md +92 -0
- package/skills/slack-standup-digest/commands/standup-digest:setup.md +8 -0
- package/skills/spark-version-upgrade/.plugin/plugin.json +20 -0
- package/skills/spark-version-upgrade/README.md +54 -0
- package/skills/spark-version-upgrade/SKILL.md +233 -0
- package/skills/ssh/.plugin/plugin.json +18 -0
- package/skills/ssh/README.md +140 -0
- package/skills/ssh/SKILL.md +135 -0
- package/skills/swift-linux/.plugin/plugin.json +17 -0
- package/skills/swift-linux/README.md +86 -0
- package/skills/swift-linux/SKILL.md +81 -0
- package/skills/theme-factory/.plugin/plugin.json +19 -0
- package/skills/theme-factory/LICENSE.txt +202 -0
- package/skills/theme-factory/README.md +58 -0
- package/skills/theme-factory/SKILL.md +59 -0
- package/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/skills/theme-factory/themes/desert-rose.md +19 -0
- package/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/skills/theme-factory/themes/golden-hour.md +19 -0
- package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/skills/uv/.plugin/plugin.json +18 -0
- package/skills/uv/README.md +5 -0
- package/skills/uv/SKILL.md +95 -0
- package/skills/uv/references/README.md +5 -0
- package/skills/vercel/.plugin/plugin.json +18 -0
- package/skills/vercel/README.md +108 -0
- package/skills/vercel/SKILL.md +103 -0
- package/tests/test_add_skill_installs_to_agents_dir.py +42 -0
- package/tests/test_catalogs.py +109 -0
- package/tests/test_code_review_risk_evaluation.py +94 -0
- package/tests/test_github_pr_review_skill.py +114 -0
- package/tests/test_issue_duplicate_checker.py +240 -0
- package/tests/test_openhands_api_python.py +152 -0
- package/tests/test_plugin_manifest.py +83 -0
- package/tests/test_pr_review_diff_payload.py +202 -0
- package/tests/test_pr_review_feedback.py +263 -0
- package/tests/test_pr_review_prompt.py +152 -0
- package/tests/test_pr_review_review_context.py +253 -0
- package/tests/test_qa_changes.py +232 -0
- package/tests/test_qa_changes_evaluation.py +259 -0
- package/tests/test_release_notes_generator.py +990 -0
- package/tests/test_sdk_loading.py +150 -0
- package/tests/test_skill_plugin_loading.py +149 -0
- package/tests/test_skills_catalog.py +336 -0
- package/tests/test_skills_have_readme.py +66 -0
- package/tests/test_sync_extensions.py +292 -0
- package/tests/test_workflow_sync.py +46 -0
- package/utils/analysis/README.md +7 -0
- package/utils/analysis/laminar_signals/README.md +211 -0
- package/utils/analysis/laminar_signals/analyze.py +780 -0
- package/utils/analysis/laminar_signals/templates/default.j2 +49 -0
- package/utils/analysis/laminar_signals/templates/pr_review.j2 +61 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Test that all skills have a README.md file in their directory."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_skills_directory():
|
|
8
|
+
"""Get the path to the skills directory."""
|
|
9
|
+
# Get the directory containing this test file
|
|
10
|
+
test_dir = Path(__file__).parent
|
|
11
|
+
# Go up one level to the repo root, then into skills/
|
|
12
|
+
return test_dir.parent / "skills"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_all_skills_have_readme():
|
|
16
|
+
"""Verify that every skill directory contains a README.md file."""
|
|
17
|
+
skills_dir = get_skills_directory()
|
|
18
|
+
|
|
19
|
+
# Get all subdirectories in the skills directory (excluding files)
|
|
20
|
+
skill_dirs = [
|
|
21
|
+
d for d in skills_dir.iterdir()
|
|
22
|
+
if d.is_dir() and not d.name.startswith('.')
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
assert len(skill_dirs) > 0, "No skill directories found"
|
|
26
|
+
|
|
27
|
+
missing_readmes = []
|
|
28
|
+
for skill_dir in skill_dirs:
|
|
29
|
+
readme_path = skill_dir / "README.md"
|
|
30
|
+
if not readme_path.exists():
|
|
31
|
+
missing_readmes.append(skill_dir.name)
|
|
32
|
+
|
|
33
|
+
assert len(missing_readmes) == 0, (
|
|
34
|
+
f"The following skills are missing README.md: {', '.join(missing_readmes)}"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_readme_is_readable():
|
|
39
|
+
"""Verify that README.md files are readable (symlinks resolve correctly)."""
|
|
40
|
+
skills_dir = get_skills_directory()
|
|
41
|
+
|
|
42
|
+
skill_dirs = [
|
|
43
|
+
d for d in skills_dir.iterdir()
|
|
44
|
+
if d.is_dir() and not d.name.startswith('.')
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
unreadable_readmes = []
|
|
48
|
+
for skill_dir in skill_dirs:
|
|
49
|
+
readme_path = skill_dir / "README.md"
|
|
50
|
+
if readme_path.exists():
|
|
51
|
+
try:
|
|
52
|
+
content = readme_path.read_text()
|
|
53
|
+
if len(content) == 0:
|
|
54
|
+
unreadable_readmes.append(f"{skill_dir.name} (empty)")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
unreadable_readmes.append(f"{skill_dir.name} ({e})")
|
|
57
|
+
|
|
58
|
+
assert len(unreadable_readmes) == 0, (
|
|
59
|
+
f"The following README.md files are unreadable: {', '.join(unreadable_readmes)}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
test_all_skills_have_readme()
|
|
65
|
+
test_readme_is_readable()
|
|
66
|
+
print("All tests passed!")
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"""Tests for scripts/sync_extensions.py core functions."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
|
|
9
|
+
from sync_extensions import (
|
|
10
|
+
CMD_MARKER,
|
|
11
|
+
CommandSpec,
|
|
12
|
+
REPO_ROOT,
|
|
13
|
+
_entry_type,
|
|
14
|
+
build_command_content,
|
|
15
|
+
collect_needed_commands,
|
|
16
|
+
generate_catalog,
|
|
17
|
+
load_marketplaces,
|
|
18
|
+
parse_frontmatter,
|
|
19
|
+
slash_triggers,
|
|
20
|
+
sync_commands,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ── parse_frontmatter ────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
class TestParseFrontmatter:
|
|
27
|
+
def test_basic(self):
|
|
28
|
+
text = (
|
|
29
|
+
"---\n"
|
|
30
|
+
"name: test-skill\n"
|
|
31
|
+
"description: A test skill\n"
|
|
32
|
+
"triggers:\n"
|
|
33
|
+
" - /test\n"
|
|
34
|
+
"---\n"
|
|
35
|
+
"Body"
|
|
36
|
+
)
|
|
37
|
+
meta = parse_frontmatter(text)
|
|
38
|
+
assert meta["name"] == "test-skill"
|
|
39
|
+
assert meta["description"] == "A test skill"
|
|
40
|
+
assert meta["triggers"] == ["/test"]
|
|
41
|
+
|
|
42
|
+
def test_no_frontmatter(self):
|
|
43
|
+
assert parse_frontmatter("No frontmatter here") == {}
|
|
44
|
+
|
|
45
|
+
def test_empty_frontmatter(self):
|
|
46
|
+
meta = parse_frontmatter("---\n---\nBody")
|
|
47
|
+
assert meta.get("triggers", []) == []
|
|
48
|
+
assert "name" not in meta
|
|
49
|
+
|
|
50
|
+
def test_multiple_triggers(self):
|
|
51
|
+
text = (
|
|
52
|
+
"---\n"
|
|
53
|
+
"name: multi\n"
|
|
54
|
+
"triggers:\n"
|
|
55
|
+
" - /foo\n"
|
|
56
|
+
" - /bar\n"
|
|
57
|
+
" - baz\n"
|
|
58
|
+
"---\n"
|
|
59
|
+
)
|
|
60
|
+
meta = parse_frontmatter(text)
|
|
61
|
+
assert meta["triggers"] == ["/foo", "/bar", "baz"]
|
|
62
|
+
|
|
63
|
+
def test_no_triggers_key(self):
|
|
64
|
+
text = "---\nname: no-triggers\ndescription: desc\n---\n"
|
|
65
|
+
meta = parse_frontmatter(text)
|
|
66
|
+
assert meta["triggers"] == []
|
|
67
|
+
|
|
68
|
+
def test_triggers_with_comments(self):
|
|
69
|
+
text = (
|
|
70
|
+
"---\n"
|
|
71
|
+
"triggers:\n"
|
|
72
|
+
" - /cmd\n"
|
|
73
|
+
" # this is a comment\n"
|
|
74
|
+
" - /other\n"
|
|
75
|
+
"---\n"
|
|
76
|
+
)
|
|
77
|
+
meta = parse_frontmatter(text)
|
|
78
|
+
assert "/cmd" in meta["triggers"]
|
|
79
|
+
assert "/other" in meta["triggers"]
|
|
80
|
+
|
|
81
|
+
def test_folded_block_scalar_description(self):
|
|
82
|
+
text = (
|
|
83
|
+
"---\n"
|
|
84
|
+
"name: test\n"
|
|
85
|
+
"description: >\n"
|
|
86
|
+
" This is a long\n"
|
|
87
|
+
" description text\n"
|
|
88
|
+
"other: value\n"
|
|
89
|
+
"---\n"
|
|
90
|
+
)
|
|
91
|
+
meta = parse_frontmatter(text)
|
|
92
|
+
assert meta["description"] == "This is a long description text"
|
|
93
|
+
|
|
94
|
+
def test_unicode(self):
|
|
95
|
+
text = (
|
|
96
|
+
"---\n"
|
|
97
|
+
"name: unic\u00f6de-skill\n"
|
|
98
|
+
"description: \u00dcn\u00efc\u00f6d\u00e9 description \U0001f680\n"
|
|
99
|
+
"---\n"
|
|
100
|
+
)
|
|
101
|
+
meta = parse_frontmatter(text)
|
|
102
|
+
assert meta["name"] == "unic\u00f6de-skill"
|
|
103
|
+
assert "\U0001f680" in meta["description"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TestParseFrontmatterEdgeCases:
|
|
107
|
+
"""Edge-case tests ensuring graceful degradation."""
|
|
108
|
+
|
|
109
|
+
def test_malformed_yaml_missing_close(self):
|
|
110
|
+
assert parse_frontmatter("---\nname: incomplete\n") == {}
|
|
111
|
+
|
|
112
|
+
def test_invalid_yaml_returns_empty(self):
|
|
113
|
+
assert parse_frontmatter("---\n: [invalid yaml{{\n---\n") == {}
|
|
114
|
+
|
|
115
|
+
def test_colon_in_description(self):
|
|
116
|
+
text = '---\nname: test\ndescription: "key: value pair"\n---\n'
|
|
117
|
+
meta = parse_frontmatter(text)
|
|
118
|
+
assert meta["description"] == "key: value pair"
|
|
119
|
+
|
|
120
|
+
def test_quoted_values_stripped(self):
|
|
121
|
+
text = "---\nname: \"quoted-name\"\ndescription: 'single-quoted'\n---\n"
|
|
122
|
+
meta = parse_frontmatter(text)
|
|
123
|
+
assert meta["name"] == "quoted-name"
|
|
124
|
+
assert meta["description"] == "single-quoted"
|
|
125
|
+
|
|
126
|
+
def test_triggers_end_at_next_key(self):
|
|
127
|
+
text = (
|
|
128
|
+
"---\n"
|
|
129
|
+
"triggers:\n"
|
|
130
|
+
" - /a\n"
|
|
131
|
+
" - /b\n"
|
|
132
|
+
"name: after-triggers\n"
|
|
133
|
+
"---\n"
|
|
134
|
+
)
|
|
135
|
+
meta = parse_frontmatter(text)
|
|
136
|
+
assert meta["triggers"] == ["/a", "/b"]
|
|
137
|
+
assert meta["name"] == "after-triggers"
|
|
138
|
+
|
|
139
|
+
def test_null_trigger_skipped(self):
|
|
140
|
+
text = (
|
|
141
|
+
"---\n"
|
|
142
|
+
"triggers:\n"
|
|
143
|
+
" - /cmd\n"
|
|
144
|
+
" -\n"
|
|
145
|
+
" - /other\n"
|
|
146
|
+
"---\n"
|
|
147
|
+
)
|
|
148
|
+
meta = parse_frontmatter(text)
|
|
149
|
+
assert "/cmd" in meta["triggers"]
|
|
150
|
+
assert "/other" in meta["triggers"]
|
|
151
|
+
|
|
152
|
+
def test_frontmatter_only_block(self):
|
|
153
|
+
meta = parse_frontmatter("---\nname: only\n---")
|
|
154
|
+
assert meta["name"] == "only"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ── slash_triggers ────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
class TestSlashTriggers:
|
|
160
|
+
def test_filters_slash_only(self):
|
|
161
|
+
meta = {"triggers": ["/init", "keyword", "/build"]}
|
|
162
|
+
assert slash_triggers(meta) == ["/init", "/build"]
|
|
163
|
+
|
|
164
|
+
def test_empty(self):
|
|
165
|
+
assert slash_triggers({}) == []
|
|
166
|
+
assert slash_triggers({"triggers": []}) == []
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ── _entry_type ───────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
class TestEntryType:
|
|
172
|
+
def test_skills_path(self):
|
|
173
|
+
assert _entry_type("./skills/foo") == "skill"
|
|
174
|
+
|
|
175
|
+
def test_plugins_path(self):
|
|
176
|
+
assert _entry_type("./plugins/bar") == "plugin"
|
|
177
|
+
|
|
178
|
+
def test_relative_plugins(self):
|
|
179
|
+
assert _entry_type("../plugins/baz") == "plugin"
|
|
180
|
+
|
|
181
|
+
def test_relative_skills(self):
|
|
182
|
+
assert _entry_type("../skills/qux") == "skill"
|
|
183
|
+
|
|
184
|
+
def test_fallback_is_skill(self):
|
|
185
|
+
assert _entry_type("./nonexistent-xyz") == "skill"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ── collect_needed_commands ───────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
class TestCollectNeededCommands:
|
|
191
|
+
def test_returns_command_specs(self):
|
|
192
|
+
specs = collect_needed_commands()
|
|
193
|
+
assert all(isinstance(s, CommandSpec) for s in specs)
|
|
194
|
+
assert len(specs) > 0
|
|
195
|
+
|
|
196
|
+
def test_paths_are_under_commands_dir(self):
|
|
197
|
+
for spec in collect_needed_commands():
|
|
198
|
+
assert spec.path.parent.name == "commands"
|
|
199
|
+
assert spec.path.suffix == ".md"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ── load_marketplaces ────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
class TestLoadMarketplaces:
|
|
205
|
+
def test_loads_at_least_one(self):
|
|
206
|
+
mps = load_marketplaces()
|
|
207
|
+
assert len(mps) > 0
|
|
208
|
+
|
|
209
|
+
def test_marketplaces_have_required_fields(self):
|
|
210
|
+
for mp in load_marketplaces():
|
|
211
|
+
assert "plugins" in mp
|
|
212
|
+
assert "_file" in mp
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ── generate_catalog ─────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
class TestGenerateCatalog:
|
|
218
|
+
def test_catalog_contains_marketplace_names(self):
|
|
219
|
+
catalog = generate_catalog()
|
|
220
|
+
assert "openhands-extensions" in catalog
|
|
221
|
+
|
|
222
|
+
def test_catalog_has_table_header(self):
|
|
223
|
+
catalog = generate_catalog()
|
|
224
|
+
assert "| Name | Type | Description | Commands |" in catalog
|
|
225
|
+
|
|
226
|
+
def test_catalog_counts_are_consistent(self):
|
|
227
|
+
catalog = generate_catalog()
|
|
228
|
+
assert "marketplace(s)" in catalog
|
|
229
|
+
assert "extensions" in catalog
|
|
230
|
+
|
|
231
|
+
def test_catalog_only_has_skill_or_plugin_types(self):
|
|
232
|
+
"""Every table row's Type column must be 'skill' or 'plugin'."""
|
|
233
|
+
catalog = generate_catalog()
|
|
234
|
+
types_found: list[str] = []
|
|
235
|
+
for line in catalog.splitlines():
|
|
236
|
+
# Skip non-table lines, header, and separator
|
|
237
|
+
if not line.startswith("|") or line.startswith("|---") or "Type" in line:
|
|
238
|
+
continue
|
|
239
|
+
cols = [c.strip() for c in line.split("|")]
|
|
240
|
+
# cols[0] is empty (before first |), cols[1]=Name, cols[2]=Type
|
|
241
|
+
if len(cols) >= 3:
|
|
242
|
+
types_found.append(cols[2])
|
|
243
|
+
assert len(types_found) > 0, "Expected at least one table entry"
|
|
244
|
+
invalid = [t for t in types_found if t not in ("skill", "plugin")]
|
|
245
|
+
assert not invalid, f"Found invalid types in catalog: {invalid}"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ── sync_commands ────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
class TestSyncCommands:
|
|
251
|
+
def test_check_mode_reports_no_problems_when_in_sync(self):
|
|
252
|
+
problems = sync_commands(check=True)
|
|
253
|
+
assert problems == [], f"Unexpected problems: {problems}"
|
|
254
|
+
|
|
255
|
+
def test_manually_edited_file_detected_in_check_mode(self, tmp_path, monkeypatch):
|
|
256
|
+
"""A command file without the header should be flagged in --check mode."""
|
|
257
|
+
# Create a skill with a slash trigger
|
|
258
|
+
skill_dir = tmp_path / "skills" / "test-skill"
|
|
259
|
+
skill_dir.mkdir(parents=True)
|
|
260
|
+
skill_md = skill_dir / "SKILL.md"
|
|
261
|
+
skill_md.write_text(
|
|
262
|
+
"---\nname: test-skill\ndescription: Test\ntriggers:\n - /test-cmd\n---\nBody\n"
|
|
263
|
+
)
|
|
264
|
+
# Create a manually-edited command file (no header)
|
|
265
|
+
cmd_dir = skill_dir / "commands"
|
|
266
|
+
cmd_dir.mkdir()
|
|
267
|
+
(cmd_dir / "test-cmd.md").write_text("Custom content, no header\n")
|
|
268
|
+
|
|
269
|
+
monkeypatch.setattr("sync_extensions.REPO_ROOT", tmp_path)
|
|
270
|
+
monkeypatch.setattr("sync_extensions.SKILL_DIRS", [tmp_path / "skills"])
|
|
271
|
+
monkeypatch.setattr("sync_extensions._slash_cmd_cache", {})
|
|
272
|
+
|
|
273
|
+
problems = sync_commands(check=True)
|
|
274
|
+
assert any("manually-edited" in p for p in problems)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# ── marketplace source paths ─────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
class TestMarketplaceSourcePaths:
|
|
280
|
+
def test_all_source_paths_exist(self):
|
|
281
|
+
"""Every source path in every marketplace should resolve on disk."""
|
|
282
|
+
import json
|
|
283
|
+
|
|
284
|
+
for mp in load_marketplaces():
|
|
285
|
+
mp_file = mp["_file"]
|
|
286
|
+
for plugin in mp["plugins"]:
|
|
287
|
+
src = plugin.get("source", "")
|
|
288
|
+
resolved = REPO_ROOT / src.lstrip("./")
|
|
289
|
+
assert resolved.exists(), (
|
|
290
|
+
f"{mp_file.name}: {plugin.get('name', '?')} source "
|
|
291
|
+
f"'{src}' does not exist at {resolved}"
|
|
292
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Test that workflow files in .github/workflows/ match copies in plugins/*/workflows/."""
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_workflow_files_are_in_sync():
|
|
8
|
+
"""Ensure workflow files in .github/workflows/ are identical to copies in plugin dirs."""
|
|
9
|
+
repo_root = Path(__file__).parent.parent
|
|
10
|
+
workflows_dir = repo_root / ".github" / "workflows"
|
|
11
|
+
|
|
12
|
+
if not workflows_dir.exists():
|
|
13
|
+
return # No workflows directory
|
|
14
|
+
|
|
15
|
+
mismatches = []
|
|
16
|
+
# Check all plugins/*/workflows/ directories
|
|
17
|
+
for copy_path in repo_root.glob("plugins/*/workflows"):
|
|
18
|
+
if not copy_path.is_dir():
|
|
19
|
+
continue
|
|
20
|
+
|
|
21
|
+
for copy_file in copy_path.glob("*.yml"):
|
|
22
|
+
canonical_file = workflows_dir / copy_file.name
|
|
23
|
+
if not canonical_file.exists():
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
canonical_content = canonical_file.read_text()
|
|
27
|
+
copy_content = copy_file.read_text()
|
|
28
|
+
|
|
29
|
+
if canonical_content != copy_content:
|
|
30
|
+
rel_path = copy_path.relative_to(repo_root)
|
|
31
|
+
mismatches.append(
|
|
32
|
+
f"{rel_path}/{copy_file.name} differs from "
|
|
33
|
+
f".github/workflows/{copy_file.name}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if mismatches:
|
|
37
|
+
raise AssertionError(
|
|
38
|
+
"Workflow files are out of sync:\n"
|
|
39
|
+
+ "\n".join(f" - {m}" for m in mismatches)
|
|
40
|
+
+ "\n\nRun: cp .github/workflows/<file> <destination> to sync"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
test_workflow_files_are_in_sync()
|
|
46
|
+
print("All workflow files are in sync!")
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Signal Analysis Utilities
|
|
2
|
+
|
|
3
|
+
This directory contains utilities for analyzing signals collected from Laminar to understand AI agent behavior patterns.
|
|
4
|
+
|
|
5
|
+
## Available Utilities
|
|
6
|
+
|
|
7
|
+
- **[laminar_signals/](laminar_signals/)** - Analyze Laminar signal events using LLMs with customizable Jinja2 prompt templates
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Laminar Signal Analysis
|
|
2
|
+
|
|
3
|
+
Analyze Laminar signal events using LLMs to identify issues and areas for improvement. Uses function calling to get structured output with clickable trace URLs.
|
|
4
|
+
|
|
5
|
+
**Primary Focus**: Identifying problems and generating actionable recommendations to improve AI agent behavior.
|
|
6
|
+
|
|
7
|
+
## Directory Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
laminar_signals/
|
|
11
|
+
├── analyze.py # Main analysis script
|
|
12
|
+
├── README.md # This file
|
|
13
|
+
└── templates/
|
|
14
|
+
├── default.j2 # Generic template for any signal type
|
|
15
|
+
└── pr_review.j2 # Specialized template for PR review signals
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- Python 3.9+
|
|
21
|
+
- `jinja2` package
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install jinja2
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Environment Variables
|
|
28
|
+
|
|
29
|
+
| Variable | Required | Default | Description |
|
|
30
|
+
|----------|----------|---------|-------------|
|
|
31
|
+
| `LMNR_PROJECT_API_KEY` | Yes | - | Laminar project API key |
|
|
32
|
+
| `LMNR_PROJECT_ID` | No | - | Laminar project ID (for generating clickable trace URLs) |
|
|
33
|
+
| `LLM_API_KEY` | Yes | - | API key for the LLM |
|
|
34
|
+
| `LLM_MODEL` | No | `gemini-3-pro-preview` | Model to use |
|
|
35
|
+
| `LLM_BASE_URL` | No | `https://llm-proxy.app.all-hands.dev` | Base URL for LLM API |
|
|
36
|
+
|
|
37
|
+
**Note:** To enable clickable trace URLs in the output, set `LMNR_PROJECT_ID` to your Laminar project ID.
|
|
38
|
+
You can find this in your Laminar dashboard URL: `https://laminar.sh/project/{PROJECT_ID}/...`
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# List available signals
|
|
44
|
+
python analyze.py --list-signals
|
|
45
|
+
|
|
46
|
+
# Analyze PR review signals (outputs markdown by default)
|
|
47
|
+
python analyze.py --signal "pr review suggestion and analysis"
|
|
48
|
+
|
|
49
|
+
# Analyze with skill context for grounded recommendations
|
|
50
|
+
python analyze.py --signal "pr review suggestion and analysis" \
|
|
51
|
+
--skill-dir ../../../plugins/pr-review
|
|
52
|
+
|
|
53
|
+
# Output as JSON (structured data with trace URLs)
|
|
54
|
+
python analyze.py --signal "pr review suggestion and analysis" --format json
|
|
55
|
+
|
|
56
|
+
# Analyze with custom prompt template
|
|
57
|
+
python analyze.py --signal "my-signal" --prompt-file my_prompt.j2
|
|
58
|
+
|
|
59
|
+
# Analyze last 30 days and save to file
|
|
60
|
+
python analyze.py --signal "my-signal" --days 30 --output results.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## How It Works
|
|
64
|
+
|
|
65
|
+
1. **Fetches Signals**: Queries the Laminar SQL API to retrieve signal events with trace IDs.
|
|
66
|
+
|
|
67
|
+
2. **Generates Prompt**: Uses a Jinja2 template focused on identifying issues and improvements.
|
|
68
|
+
|
|
69
|
+
3. **Loads Skill Context** (optional): If `--skill-dir` is provided, loads the skill/plugin content so recommendations can reference specific prompt verbiage.
|
|
70
|
+
|
|
71
|
+
4. **LLM Analysis**: Uses **function calling** to get structured output with:
|
|
72
|
+
- `issues`: Problems and failures with severity, frequency, and clickable trace URLs
|
|
73
|
+
- `recommendations`: Specific fixes with priority levels (grounded in skill content when provided)
|
|
74
|
+
- `metrics`: Quantitative statistics (issue rate, etc.)
|
|
75
|
+
- `strengths`: Brief note on what's working (secondary focus)
|
|
76
|
+
|
|
77
|
+
5. **Output**: Markdown report (default) or JSON for programmatic use.
|
|
78
|
+
|
|
79
|
+
## Grounding Recommendations in Skills
|
|
80
|
+
|
|
81
|
+
Use `--skill-dir` to provide the current skill/plugin configuration. This allows the LLM to:
|
|
82
|
+
- Reference specific sections of the prompt that need changes
|
|
83
|
+
- Suggest exact wording modifications
|
|
84
|
+
- Ground recommendations in the actual instructions the agent follows
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Use a skill directory
|
|
88
|
+
python analyze.py --signal "..." --skill-dir path/to/skills/code-review
|
|
89
|
+
|
|
90
|
+
# Use a plugin directory (loads nested skills and prompt.py)
|
|
91
|
+
python analyze.py --signal "..." --skill-dir path/to/plugins/pr-review
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The script automatically loads:
|
|
95
|
+
- `SKILL.md` from the directory
|
|
96
|
+
- `scripts/prompt.py` if present (for plugins)
|
|
97
|
+
- Any nested skills in `skills/` subdirectory
|
|
98
|
+
|
|
99
|
+
## Output Structure
|
|
100
|
+
|
|
101
|
+
### Issues (Primary Focus)
|
|
102
|
+
|
|
103
|
+
Each issue includes:
|
|
104
|
+
- **Title**: Short description
|
|
105
|
+
- **Description**: Detailed explanation of the problem
|
|
106
|
+
- **Severity**: critical / high / medium / low
|
|
107
|
+
- **Frequency**: How often this occurs
|
|
108
|
+
- **Trace URLs**: Clickable links to example traces in Laminar
|
|
109
|
+
|
|
110
|
+
### Recommendations
|
|
111
|
+
|
|
112
|
+
Each recommendation includes:
|
|
113
|
+
- **Title**: Short description
|
|
114
|
+
- **Description**: How to implement the fix
|
|
115
|
+
- **Addresses**: Which issues this fixes
|
|
116
|
+
- **Priority**: high / medium / low
|
|
117
|
+
|
|
118
|
+
### Example JSON Output
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"issues": [
|
|
123
|
+
{
|
|
124
|
+
"title": "Premature Approval of Functional Defects",
|
|
125
|
+
"description": "The AI approves code with known issues...",
|
|
126
|
+
"severity": "critical",
|
|
127
|
+
"frequency": "10-15% of traces",
|
|
128
|
+
"trace_urls": [
|
|
129
|
+
"https://www.lmnr.ai/traces/a0dbd0c7-116d-776b-9c4b-c7f5f4d0a97e",
|
|
130
|
+
"https://www.lmnr.ai/traces/5de277ff-38f2-e173-4b03-c7332301ea58"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"recommendations": [
|
|
135
|
+
{
|
|
136
|
+
"title": "Enforce Logic Verification Over Style",
|
|
137
|
+
"description": "Modify the prompt to separate style from logic analysis...",
|
|
138
|
+
"addresses": ["Premature Approval of Functional Defects"],
|
|
139
|
+
"priority": "high"
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"metrics": {
|
|
143
|
+
"total_signals": 50,
|
|
144
|
+
"issue_rate": "30%",
|
|
145
|
+
"key_statistics": [...]
|
|
146
|
+
},
|
|
147
|
+
"strengths": [
|
|
148
|
+
{"title": "Security Analysis", "description": "..."}
|
|
149
|
+
],
|
|
150
|
+
"summary": "Executive summary of critical improvements needed..."
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Example Markdown Output
|
|
155
|
+
|
|
156
|
+
```markdown
|
|
157
|
+
# Agent Improvement Report
|
|
158
|
+
|
|
159
|
+
**Signal:** `pr review suggestion and analysis`
|
|
160
|
+
|
|
161
|
+
## Executive Summary
|
|
162
|
+
|
|
163
|
+
The AI agent demonstrates strong security awareness but suffers from...
|
|
164
|
+
|
|
165
|
+
## Issues Requiring Attention
|
|
166
|
+
|
|
167
|
+
### 1. [CRITICAL] Premature Approval of Functional Defects
|
|
168
|
+
|
|
169
|
+
The AI approves code with known issues...
|
|
170
|
+
|
|
171
|
+
**Frequency:** 10-15% of traces
|
|
172
|
+
|
|
173
|
+
**Example traces:**
|
|
174
|
+
- https://www.lmnr.ai/traces/a0dbd0c7-116d-776b-9c4b-c7f5f4d0a97e
|
|
175
|
+
- https://www.lmnr.ai/traces/5de277ff-38f2-e173-4b03-c7332301ea58
|
|
176
|
+
|
|
177
|
+
## Recommended Fixes
|
|
178
|
+
|
|
179
|
+
### 1. [HIGH PRIORITY] Enforce Logic Verification Over Style
|
|
180
|
+
|
|
181
|
+
Modify the prompt to separate style from logic analysis...
|
|
182
|
+
|
|
183
|
+
*Fixes: Premature Approval of Functional Defects*
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Custom Prompt Templates
|
|
187
|
+
|
|
188
|
+
Create a Jinja2 template (`.j2`) with access to:
|
|
189
|
+
|
|
190
|
+
| Variable | Type | Description |
|
|
191
|
+
|----------|------|-------------|
|
|
192
|
+
| `signals` | `list[dict]` | List of parsed signal objects |
|
|
193
|
+
| `num_signals` | `int` | Number of signals |
|
|
194
|
+
| `signal_name` | `str` | Name of the signal |
|
|
195
|
+
|
|
196
|
+
Each signal object contains:
|
|
197
|
+
- `trace_url`: Full URL to view the trace in Laminar
|
|
198
|
+
- `trace_id`: Trace UUID
|
|
199
|
+
- `timestamp`: When the signal was created
|
|
200
|
+
- `payload`: Parsed payload as dict
|
|
201
|
+
- `payload_json`: Formatted JSON string
|
|
202
|
+
- All payload fields flattened to top level
|
|
203
|
+
|
|
204
|
+
## Laminar SQL API Reference
|
|
205
|
+
|
|
206
|
+
The script uses Laminar's SQL API at `/v1/sql/query`. Key tables:
|
|
207
|
+
|
|
208
|
+
- `signal_events`: Signal events with trace_id and payload
|
|
209
|
+
- `traces`: Trace-level aggregates
|
|
210
|
+
|
|
211
|
+
See [Laminar SQL Editor documentation](https://docs.laminar.sh/platform/sql-editor) for details.
|