@sentry/warden 0.22.0 → 0.24.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/.oxlintrc.json +169 -0
- package/CHANGELOG.md +427 -0
- package/README.md +4 -1
- package/dist/cli/args.d.ts +6 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +175 -207
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/add.js +16 -5
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/commands/build.d.ts +13 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +384 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +13 -5
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/runs.d.ts.map +1 -1
- package/dist/cli/commands/runs.js +12 -6
- package/dist/cli/commands/runs.js.map +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +4 -1
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/context.d.ts.map +1 -1
- package/dist/cli/context.js +8 -1
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/files.d.ts.map +1 -1
- package/dist/cli/files.js +27 -9
- package/dist/cli/files.js.map +1 -1
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +448 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/index.js +2 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/input.d.ts +12 -0
- package/dist/cli/input.d.ts.map +1 -1
- package/dist/cli/input.js +60 -0
- package/dist/cli/input.js.map +1 -1
- package/dist/cli/main.d.ts +41 -2
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +294 -75
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output/box.js +1 -1
- package/dist/cli/output/formatters.d.ts +10 -2
- package/dist/cli/output/formatters.d.ts.map +1 -1
- package/dist/cli/output/formatters.js +32 -9
- package/dist/cli/output/formatters.js.map +1 -1
- package/dist/cli/output/index.d.ts +1 -1
- package/dist/cli/output/index.d.ts.map +1 -1
- package/dist/cli/output/index.js +1 -1
- package/dist/cli/output/index.js.map +1 -1
- package/dist/cli/output/ink-runner.d.ts +3 -1
- package/dist/cli/output/ink-runner.d.ts.map +1 -1
- package/dist/cli/output/ink-runner.js +64 -28
- package/dist/cli/output/ink-runner.js.map +1 -1
- package/dist/cli/output/jsonl.d.ts +37 -0
- package/dist/cli/output/jsonl.d.ts.map +1 -1
- package/dist/cli/output/jsonl.js +6 -0
- package/dist/cli/output/jsonl.js.map +1 -1
- package/dist/cli/output/live-status.d.ts +48 -0
- package/dist/cli/output/live-status.d.ts.map +1 -0
- package/dist/cli/output/live-status.js +173 -0
- package/dist/cli/output/live-status.js.map +1 -0
- package/dist/cli/output/reporter.d.ts +4 -0
- package/dist/cli/output/reporter.d.ts.map +1 -1
- package/dist/cli/output/reporter.js +20 -3
- package/dist/cli/output/reporter.js.map +1 -1
- package/dist/cli/output/tasks.d.ts +8 -5
- package/dist/cli/output/tasks.d.ts.map +1 -1
- package/dist/cli/output/tasks.js +113 -71
- package/dist/cli/output/tasks.js.map +1 -1
- package/dist/cli/signals.d.ts +15 -0
- package/dist/cli/signals.d.ts.map +1 -0
- package/dist/cli/signals.js +26 -0
- package/dist/cli/signals.js.map +1 -0
- package/dist/config/loader.d.ts +48 -7
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +228 -32
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +55 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +34 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/diff/context.d.ts +9 -2
- package/dist/diff/context.d.ts.map +1 -1
- package/dist/diff/context.js +65 -17
- package/dist/diff/context.js.map +1 -1
- package/dist/evals/runner.d.ts.map +1 -1
- package/dist/evals/runner.js +16 -0
- package/dist/evals/runner.js.map +1 -1
- package/dist/event/context.d.ts.map +1 -1
- package/dist/event/context.js +1 -0
- package/dist/event/context.js.map +1 -1
- package/dist/event/schedule-context.d.ts.map +1 -1
- package/dist/event/schedule-context.js +1 -0
- package/dist/event/schedule-context.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/output/dedup.d.ts +8 -11
- package/dist/output/dedup.d.ts.map +1 -1
- package/dist/output/dedup.js +39 -37
- package/dist/output/dedup.js.map +1 -1
- package/dist/output/github-checks.d.ts +1 -0
- package/dist/output/github-checks.d.ts.map +1 -1
- package/dist/output/github-checks.js +9 -7
- package/dist/output/github-checks.js.map +1 -1
- package/dist/sdk/analyze.d.ts.map +1 -1
- package/dist/sdk/analyze.js +147 -277
- package/dist/sdk/analyze.js.map +1 -1
- package/dist/sdk/circuit-breaker.d.ts +26 -0
- package/dist/sdk/circuit-breaker.d.ts.map +1 -0
- package/dist/sdk/circuit-breaker.js +53 -0
- package/dist/sdk/circuit-breaker.js.map +1 -0
- package/dist/sdk/errors.d.ts +5 -0
- package/dist/sdk/errors.d.ts.map +1 -1
- package/dist/sdk/errors.js +27 -0
- package/dist/sdk/errors.js.map +1 -1
- package/dist/sdk/extract.d.ts +18 -9
- package/dist/sdk/extract.d.ts.map +1 -1
- package/dist/sdk/extract.js +95 -87
- package/dist/sdk/extract.js.map +1 -1
- package/dist/sdk/fix-quality.d.ts +5 -0
- package/dist/sdk/fix-quality.d.ts.map +1 -1
- package/dist/sdk/fix-quality.js +42 -23
- package/dist/sdk/fix-quality.js.map +1 -1
- package/dist/sdk/haiku.d.ts +6 -0
- package/dist/sdk/haiku.d.ts.map +1 -1
- package/dist/sdk/haiku.js +75 -49
- package/dist/sdk/haiku.js.map +1 -1
- package/dist/sdk/json-output.d.ts +31 -0
- package/dist/sdk/json-output.d.ts.map +1 -0
- package/dist/sdk/json-output.js +98 -0
- package/dist/sdk/json-output.js.map +1 -0
- package/dist/sdk/model-pricing.json +56 -16
- package/dist/sdk/post-process.d.ts +29 -0
- package/dist/sdk/post-process.d.ts.map +1 -0
- package/dist/sdk/post-process.js +65 -0
- package/dist/sdk/post-process.js.map +1 -0
- package/dist/sdk/prepare.d.ts.map +1 -1
- package/dist/sdk/prepare.js +4 -1
- package/dist/sdk/prepare.js.map +1 -1
- package/dist/sdk/pricing.d.ts +18 -0
- package/dist/sdk/pricing.d.ts.map +1 -1
- package/dist/sdk/pricing.js +108 -3
- package/dist/sdk/pricing.js.map +1 -1
- package/dist/sdk/prompt-sections.d.ts +56 -0
- package/dist/sdk/prompt-sections.d.ts.map +1 -0
- package/dist/sdk/prompt-sections.js +118 -0
- package/dist/sdk/prompt-sections.js.map +1 -0
- package/dist/sdk/prompt.d.ts +2 -17
- package/dist/sdk/prompt.d.ts.map +1 -1
- package/dist/sdk/prompt.js +15 -37
- package/dist/sdk/prompt.js.map +1 -1
- package/dist/sdk/report-files.d.ts +15 -0
- package/dist/sdk/report-files.d.ts.map +1 -0
- package/dist/sdk/report-files.js +20 -0
- package/dist/sdk/report-files.js.map +1 -0
- package/dist/sdk/runner.d.ts +9 -1
- package/dist/sdk/runner.d.ts.map +1 -1
- package/dist/sdk/runner.js +6 -0
- package/dist/sdk/runner.js.map +1 -1
- package/dist/sdk/runtimes/claude.d.ts +3 -0
- package/dist/sdk/runtimes/claude.d.ts.map +1 -0
- package/dist/sdk/runtimes/claude.js +376 -0
- package/dist/sdk/runtimes/claude.js.map +1 -0
- package/dist/sdk/runtimes/index.d.ts +12 -0
- package/dist/sdk/runtimes/index.d.ts.map +1 -0
- package/dist/sdk/runtimes/index.js +22 -0
- package/dist/sdk/runtimes/index.js.map +1 -0
- package/dist/sdk/runtimes/types.d.ts +116 -0
- package/dist/sdk/runtimes/types.d.ts.map +1 -0
- package/dist/sdk/runtimes/types.js +17 -0
- package/dist/sdk/runtimes/types.js.map +1 -0
- package/dist/sdk/types.d.ts +23 -2
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk/types.js.map +1 -1
- package/dist/sdk/usage.d.ts +21 -6
- package/dist/sdk/usage.d.ts.map +1 -1
- package/dist/sdk/usage.js +36 -23
- package/dist/sdk/usage.js.map +1 -1
- package/dist/sdk/verify.d.ts +25 -0
- package/dist/sdk/verify.d.ts.map +1 -0
- package/dist/sdk/verify.js +226 -0
- package/dist/sdk/verify.js.map +1 -0
- package/dist/skill-builder/agentic.d.ts +33 -0
- package/dist/skill-builder/agentic.d.ts.map +1 -0
- package/dist/skill-builder/agentic.js +240 -0
- package/dist/skill-builder/agentic.js.map +1 -0
- package/dist/skill-builder/authoring-provider.d.ts +5 -0
- package/dist/skill-builder/authoring-provider.d.ts.map +1 -0
- package/dist/skill-builder/authoring-provider.js +70 -0
- package/dist/skill-builder/authoring-provider.js.map +1 -0
- package/dist/skill-builder/definition.d.ts +46 -0
- package/dist/skill-builder/definition.d.ts.map +1 -0
- package/dist/skill-builder/definition.js +171 -0
- package/dist/skill-builder/definition.js.map +1 -0
- package/dist/skill-builder/outline-contract.d.ts +113 -0
- package/dist/skill-builder/outline-contract.d.ts.map +1 -0
- package/dist/skill-builder/outline-contract.js +73 -0
- package/dist/skill-builder/outline-contract.js.map +1 -0
- package/dist/skill-builder/outline-state.d.ts +158 -0
- package/dist/skill-builder/outline-state.d.ts.map +1 -0
- package/dist/skill-builder/outline-state.js +87 -0
- package/dist/skill-builder/outline-state.js.map +1 -0
- package/dist/skill-builder/outline.d.ts +36 -0
- package/dist/skill-builder/outline.d.ts.map +1 -0
- package/dist/skill-builder/outline.js +345 -0
- package/dist/skill-builder/outline.js.map +1 -0
- package/dist/skill-builder/skill-contract.d.ts +97 -0
- package/dist/skill-builder/skill-contract.d.ts.map +1 -0
- package/dist/skill-builder/skill-contract.js +58 -0
- package/dist/skill-builder/skill-contract.js.map +1 -0
- package/dist/skill-builder/skill-prompts.d.ts +61 -0
- package/dist/skill-builder/skill-prompts.d.ts.map +1 -0
- package/dist/skill-builder/skill-prompts.js +285 -0
- package/dist/skill-builder/skill-prompts.js.map +1 -0
- package/dist/skill-builder/skill.d.ts +23 -0
- package/dist/skill-builder/skill.d.ts.map +1 -0
- package/dist/skill-builder/skill.js +563 -0
- package/dist/skill-builder/skill.js.map +1 -0
- package/dist/skills/index.d.ts +2 -2
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/loader.d.ts +20 -6
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +88 -141
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/remote.d.ts +11 -7
- package/dist/skills/remote.d.ts.map +1 -1
- package/dist/skills/remote.js +62 -162
- package/dist/skills/remote.js.map +1 -1
- package/dist/triggers/matcher.d.ts.map +1 -1
- package/dist/triggers/matcher.js +28 -15
- package/dist/triggers/matcher.js.map +1 -1
- package/dist/types/index.d.ts +40 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/path.d.ts +17 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +36 -0
- package/dist/utils/path.js.map +1 -0
- package/package.json +8 -8
- package/policies/README.md +22 -0
- package/policies/code-comments.md +20 -0
- package/policies/policy-template.md +14 -0
- package/skills/warden/SPEC.md +112 -0
- package/skills/warden/references/cli-reference.md +2 -2
- package/skills/warden/references/config-schema.md +29 -13
- package/skills/warden/references/configuration.md +20 -8
- package/skills/warden/references/creating-skills.md +3 -0
- package/skills/warden-sweep/SPEC.md +126 -0
- package/src/builtin-skills/security-review/SKILL.md +81 -0
- package/src/builtin-skills/security-review/SPEC.md +77 -0
- package/src/builtin-skills/security-review/references/github-workflows.md +186 -0
- package/src/builtin-skills/security-review/references/javascript-typescript.md +69 -0
- package/src/builtin-skills/security-review/references/python.md +70 -0
- package/src/internal-skills/skill-writer/EVAL.md +124 -0
- package/src/internal-skills/skill-writer/SKILL.md +159 -0
- package/src/internal-skills/skill-writer/SOURCES.md +94 -0
- package/src/internal-skills/skill-writer/SPEC.md +153 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/argument-driven-skill-layout.md +32 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/asset-template-skill-layout.md +30 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/inline-skill-layout.md +28 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/reference-backed-skill-layout.md +35 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/script-backed-skill-layout.md +31 -0
- package/src/internal-skills/skill-writer/references/authoring-path.md +91 -0
- package/src/internal-skills/skill-writer/references/claude-code/argument-substitutions.md +20 -0
- package/src/internal-skills/skill-writer/references/claude-code/dynamic-context.md +16 -0
- package/src/internal-skills/skill-writer/references/claude-code/frontmatter-and-invocation.md +33 -0
- package/src/internal-skills/skill-writer/references/claude-code/hook-backed-skills.md +28 -0
- package/src/internal-skills/skill-writer/references/claude-code/subagent-fork-skills.md +22 -0
- package/src/internal-skills/skill-writer/references/description-optimization.md +28 -0
- package/src/internal-skills/skill-writer/references/design-principles.md +74 -0
- package/src/internal-skills/skill-writer/references/evaluation-path.md +73 -0
- package/src/internal-skills/skill-writer/references/examples/documentation-skill.md +70 -0
- package/src/internal-skills/skill-writer/references/examples/evaluator-loop-skill.md +38 -0
- package/src/internal-skills/skill-writer/references/examples/hook-backed-skill.md +39 -0
- package/src/internal-skills/skill-writer/references/examples/router-skill.md +41 -0
- package/src/internal-skills/skill-writer/references/examples/security-review-skill.md +69 -0
- package/src/internal-skills/skill-writer/references/examples/subagent-fork-skill.md +39 -0
- package/src/internal-skills/skill-writer/references/examples/workflow-process-skill.md +65 -0
- package/src/internal-skills/skill-writer/references/execution-shapes.md +68 -0
- package/src/internal-skills/skill-writer/references/iteration-evidence.md +82 -0
- package/src/internal-skills/skill-writer/references/iteration-path.md +40 -0
- package/src/internal-skills/skill-writer/references/mode-selection.md +67 -0
- package/src/internal-skills/skill-writer/references/output-contracts.md +68 -0
- package/src/internal-skills/skill-writer/references/reference-architecture.md +71 -0
- package/src/internal-skills/skill-writer/references/registration-validation.md +67 -0
- package/src/internal-skills/skill-writer/references/source-discovery.md +60 -0
- package/src/internal-skills/skill-writer/references/spec-template.md +118 -0
- package/src/internal-skills/skill-writer/references/structure-troubleshooting.md +111 -0
- package/src/internal-skills/skill-writer/references/synthesis-path.md +119 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/evaluator-loops.md +25 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/orchestrator-workers.md +25 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/parallel-workflows.md +21 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/plan-validate-execute.md +26 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/prompt-chaining.md +24 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/routing-workflows.md +28 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/validation-loops.md +26 -0
- package/src/internal-skills/skill-writer/scripts/quick_validate.py +158 -0
- package/src/internal-skills/skill-writer/scripts/quick_validate_test.py +79 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.12"
|
|
3
|
+
# dependencies = ["pyyaml"]
|
|
4
|
+
# ///
|
|
5
|
+
"""
|
|
6
|
+
Quick validation script for Agent Skills.
|
|
7
|
+
|
|
8
|
+
Validates only mechanical SKILL.md frontmatter and identity requirements.
|
|
9
|
+
Qualitative content, reference depth, routing, and prose structure belong to
|
|
10
|
+
human/agent review, not this script.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
uv run quick_validate.py <skill_directory> [--skill-class <class>] [--strict-depth]
|
|
14
|
+
|
|
15
|
+
Returns exit code 0 on success, 1 on failure. Outputs JSON with validation results.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
MAX_NAME_LENGTH = 64
|
|
27
|
+
MAX_DESCRIPTION_LENGTH = 1024
|
|
28
|
+
|
|
29
|
+
SKILL_CLASSES = {
|
|
30
|
+
"auto",
|
|
31
|
+
"workflow-process",
|
|
32
|
+
"integration-documentation",
|
|
33
|
+
"security-review",
|
|
34
|
+
"skill-authoring",
|
|
35
|
+
"generic",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description="Validate agent skill frontmatter and identity fields.",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument("skill_directory")
|
|
44
|
+
parser.add_argument("--skill-class", choices=sorted(SKILL_CLASSES), default="auto")
|
|
45
|
+
parser.add_argument("--strict-depth", action="store_true")
|
|
46
|
+
return parser.parse_args(argv)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def validate_skill(
|
|
50
|
+
skill_path: Path,
|
|
51
|
+
selected_skill_class: str = "auto",
|
|
52
|
+
strict_depth: bool = False,
|
|
53
|
+
) -> tuple[bool, list[str], list[str], str]:
|
|
54
|
+
"""Validate a skill directory. Returns (valid, errors, warnings, resolved_skill_class)."""
|
|
55
|
+
# Keep the flag for CLI compatibility; depth/content checks belong to review.
|
|
56
|
+
_ = strict_depth
|
|
57
|
+
errors: list[str] = []
|
|
58
|
+
warnings: list[str] = []
|
|
59
|
+
|
|
60
|
+
# Check SKILL.md exists.
|
|
61
|
+
skill_md = skill_path / "SKILL.md"
|
|
62
|
+
if not skill_md.exists():
|
|
63
|
+
return False, ["SKILL.md not found"], [], "generic"
|
|
64
|
+
|
|
65
|
+
content = skill_md.read_text()
|
|
66
|
+
|
|
67
|
+
# Check frontmatter exists and is first.
|
|
68
|
+
if not content.startswith("---"):
|
|
69
|
+
errors.append("No YAML frontmatter found (file must start with ---)")
|
|
70
|
+
return False, errors, warnings, "generic"
|
|
71
|
+
|
|
72
|
+
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
|
|
73
|
+
if not match:
|
|
74
|
+
errors.append("Invalid frontmatter format (missing closing ---)")
|
|
75
|
+
return False, errors, warnings, "generic"
|
|
76
|
+
|
|
77
|
+
# Parse frontmatter.
|
|
78
|
+
frontmatter_text = match.group(1)
|
|
79
|
+
try:
|
|
80
|
+
frontmatter = yaml.safe_load(frontmatter_text)
|
|
81
|
+
if not isinstance(frontmatter, dict):
|
|
82
|
+
errors.append("Frontmatter must be a YAML mapping")
|
|
83
|
+
return False, errors, warnings, "generic"
|
|
84
|
+
except yaml.YAMLError as exc:
|
|
85
|
+
errors.append(f"Invalid YAML in frontmatter: {exc}")
|
|
86
|
+
return False, errors, warnings, "generic"
|
|
87
|
+
|
|
88
|
+
# Validate frontmatter keys without hardcoding provider-specific optional fields.
|
|
89
|
+
invalid_keys = [key for key in frontmatter.keys() if not isinstance(key, str) or not key.strip()]
|
|
90
|
+
if invalid_keys:
|
|
91
|
+
errors.append("Frontmatter keys must be non-empty strings")
|
|
92
|
+
|
|
93
|
+
# Validate name.
|
|
94
|
+
if "name" not in frontmatter:
|
|
95
|
+
errors.append("Missing required field: name")
|
|
96
|
+
else:
|
|
97
|
+
name = frontmatter["name"]
|
|
98
|
+
if not isinstance(name, str):
|
|
99
|
+
errors.append(f"name must be a string, got {type(name).__name__}")
|
|
100
|
+
else:
|
|
101
|
+
name = name.strip()
|
|
102
|
+
if not name:
|
|
103
|
+
errors.append("name must not be empty")
|
|
104
|
+
elif len(name) > MAX_NAME_LENGTH:
|
|
105
|
+
errors.append(f"name is too long ({len(name)} chars, max {MAX_NAME_LENGTH})")
|
|
106
|
+
elif not re.match(r"^[a-z0-9-]+$", name):
|
|
107
|
+
errors.append(f"name '{name}' must contain only lowercase letters, digits, and hyphens")
|
|
108
|
+
elif name.startswith("-") or name.endswith("-"):
|
|
109
|
+
errors.append(f"name '{name}' must not start or end with a hyphen")
|
|
110
|
+
elif "--" in name:
|
|
111
|
+
errors.append(f"name '{name}' must not contain consecutive hyphens")
|
|
112
|
+
elif name != skill_path.name:
|
|
113
|
+
errors.append(f"name '{name}' does not match directory name '{skill_path.name}'")
|
|
114
|
+
|
|
115
|
+
# Validate description.
|
|
116
|
+
if "description" not in frontmatter:
|
|
117
|
+
errors.append("Missing required field: description")
|
|
118
|
+
else:
|
|
119
|
+
description = frontmatter["description"]
|
|
120
|
+
if not isinstance(description, str):
|
|
121
|
+
errors.append(f"description must be a string, got {type(description).__name__}")
|
|
122
|
+
else:
|
|
123
|
+
description = description.strip()
|
|
124
|
+
if not description:
|
|
125
|
+
errors.append("description must not be empty")
|
|
126
|
+
elif len(description) > MAX_DESCRIPTION_LENGTH:
|
|
127
|
+
errors.append(f"description is too long ({len(description)} chars, max {MAX_DESCRIPTION_LENGTH})")
|
|
128
|
+
|
|
129
|
+
resolved_skill_class = selected_skill_class if selected_skill_class != "auto" else "generic"
|
|
130
|
+
|
|
131
|
+
return len(errors) == 0, errors, warnings, resolved_skill_class
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def main() -> None:
|
|
135
|
+
args = parse_args(sys.argv[1:])
|
|
136
|
+
skill_path = Path(args.skill_directory).resolve()
|
|
137
|
+
if not skill_path.is_dir():
|
|
138
|
+
print(json.dumps({"valid": False, "errors": [f"Not a directory: {skill_path}"]}))
|
|
139
|
+
sys.exit(1)
|
|
140
|
+
|
|
141
|
+
valid, errors, warnings, resolved_skill_class = validate_skill(
|
|
142
|
+
skill_path,
|
|
143
|
+
selected_skill_class=args.skill_class,
|
|
144
|
+
strict_depth=args.strict_depth,
|
|
145
|
+
)
|
|
146
|
+
result = {
|
|
147
|
+
"valid": valid,
|
|
148
|
+
"skill_class": resolved_skill_class,
|
|
149
|
+
"strict_depth": args.strict_depth,
|
|
150
|
+
"errors": errors,
|
|
151
|
+
"warnings": warnings,
|
|
152
|
+
}
|
|
153
|
+
print(json.dumps(result, indent=2))
|
|
154
|
+
sys.exit(0 if valid else 1)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
main()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
SCRIPT_PATH = Path(__file__).with_name("quick_validate.py")
|
|
8
|
+
SPEC = importlib.util.spec_from_file_location("quick_validate", SCRIPT_PATH)
|
|
9
|
+
assert SPEC is not None
|
|
10
|
+
quick_validate = importlib.util.module_from_spec(SPEC)
|
|
11
|
+
assert SPEC.loader is not None
|
|
12
|
+
SPEC.loader.exec_module(quick_validate)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class QuickValidateTest(unittest.TestCase):
|
|
16
|
+
def test_ignores_markdown_content_and_supporting_files(self) -> None:
|
|
17
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
18
|
+
skill_path = Path(temp_dir) / "example-skill"
|
|
19
|
+
skill_path.mkdir()
|
|
20
|
+
(skill_path / "SKILL.md").write_text(
|
|
21
|
+
"""---
|
|
22
|
+
name: example-skill
|
|
23
|
+
description: I can review <example> content.
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
This intentionally references missing content that review may care about:
|
|
27
|
+
|
|
28
|
+
- references/missing.md
|
|
29
|
+
- scripts/missing.py
|
|
30
|
+
- SOURCES.md
|
|
31
|
+
|
|
32
|
+
/Users/example/private/path should not be script-validated.
|
|
33
|
+
""",
|
|
34
|
+
encoding="utf-8",
|
|
35
|
+
)
|
|
36
|
+
(skill_path / "SPEC.md").write_text(
|
|
37
|
+
"""# Example Skill Spec
|
|
38
|
+
|
|
39
|
+
## Evaluation
|
|
40
|
+
|
|
41
|
+
Run the local checks for this skill.
|
|
42
|
+
""",
|
|
43
|
+
encoding="utf-8",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
valid, errors, warnings, _skill_class = quick_validate.validate_skill(
|
|
47
|
+
skill_path,
|
|
48
|
+
selected_skill_class="integration-documentation",
|
|
49
|
+
strict_depth=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.assertTrue(valid)
|
|
53
|
+
self.assertEqual(errors, [])
|
|
54
|
+
self.assertEqual(warnings, [])
|
|
55
|
+
|
|
56
|
+
def test_rejects_invalid_frontmatter_yaml(self) -> None:
|
|
57
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
58
|
+
skill_path = Path(temp_dir) / "example-skill"
|
|
59
|
+
skill_path.mkdir()
|
|
60
|
+
(skill_path / "SKILL.md").write_text(
|
|
61
|
+
"""---
|
|
62
|
+
name: [example-skill
|
|
63
|
+
description: Use when reviewing example code.
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Review example code.
|
|
67
|
+
""",
|
|
68
|
+
encoding="utf-8",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
valid, errors, warnings, _skill_class = quick_validate.validate_skill(skill_path)
|
|
72
|
+
|
|
73
|
+
self.assertFalse(valid)
|
|
74
|
+
self.assertEqual(warnings, [])
|
|
75
|
+
self.assertTrue(any("Invalid YAML in frontmatter" in message for message in errors), errors)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
unittest.main()
|