@drafthq/draft 2.7.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/.claude-plugin/marketplace.json +38 -0
- package/.claude-plugin/plugin.json +26 -0
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/bin/README.md +49 -0
- package/cli/bin/draft.js +13 -0
- package/cli/src/cli.js +113 -0
- package/cli/src/hosts/claude-code.js +46 -0
- package/cli/src/hosts/codex.js +33 -0
- package/cli/src/hosts/cursor.js +50 -0
- package/cli/src/hosts/index.js +24 -0
- package/cli/src/hosts/opencode.js +39 -0
- package/cli/src/installer.js +61 -0
- package/cli/src/lib/fsx.js +34 -0
- package/cli/src/lib/graph.js +23 -0
- package/cli/src/lib/log.js +32 -0
- package/cli/src/lib/paths.js +14 -0
- package/core/agents/architect.md +338 -0
- package/core/agents/debugger.md +193 -0
- package/core/agents/ops.md +104 -0
- package/core/agents/planner.md +158 -0
- package/core/agents/rca.md +314 -0
- package/core/agents/reviewer.md +256 -0
- package/core/agents/writer.md +110 -0
- package/core/guardrails/README.md +4 -0
- package/core/guardrails/code-quality.md +4 -0
- package/core/guardrails/dependency-triage.md +4 -0
- package/core/guardrails/design-norms.md +4 -0
- package/core/guardrails/language-standards.md +4 -0
- package/core/guardrails/review-checks.md +4 -0
- package/core/guardrails/secure-patterns.md +4 -0
- package/core/guardrails/security.md +4 -0
- package/core/guardrails.md +22 -0
- package/core/knowledge-base.md +127 -0
- package/core/methodology.md +1221 -0
- package/core/shared/condensation.md +224 -0
- package/core/shared/context-verify.md +44 -0
- package/core/shared/cross-skill-dispatch.md +127 -0
- package/core/shared/discovery-schema.md +75 -0
- package/core/shared/draft-context-loading.md +282 -0
- package/core/shared/git-report-metadata.md +106 -0
- package/core/shared/graph-query.md +239 -0
- package/core/shared/graph-usage-report.md +22 -0
- package/core/shared/jira-sync.md +170 -0
- package/core/shared/parallel-analysis.md +386 -0
- package/core/shared/parallel-fanout.md +10 -0
- package/core/shared/pattern-learning.md +146 -0
- package/core/shared/red-flags.md +58 -0
- package/core/shared/template-contract.md +22 -0
- package/core/shared/template-hygiene.md +10 -0
- package/core/shared/tool-resolver.md +10 -0
- package/core/shared/vcs-commands.md +97 -0
- package/core/shared/verification-gates.md +47 -0
- package/core/templates/CHANGELOG.md +70 -0
- package/core/templates/ai-context-export.md +8 -0
- package/core/templates/ai-context.md +270 -0
- package/core/templates/ai-profile.md +41 -0
- package/core/templates/architecture.md +203 -0
- package/core/templates/dependency-graph.md +103 -0
- package/core/templates/discovery.md +79 -0
- package/core/templates/guardrails.md +143 -0
- package/core/templates/hld.md +327 -0
- package/core/templates/intake-questions.md +403 -0
- package/core/templates/jira.md +119 -0
- package/core/templates/lld.md +283 -0
- package/core/templates/metadata.json +66 -0
- package/core/templates/plan.md +130 -0
- package/core/templates/product.md +110 -0
- package/core/templates/rca.md +86 -0
- package/core/templates/root-architecture.md +127 -0
- package/core/templates/root-product.md +53 -0
- package/core/templates/root-tech-stack.md +117 -0
- package/core/templates/service-index.md +55 -0
- package/core/templates/session-summary.md +8 -0
- package/core/templates/spec.md +165 -0
- package/core/templates/tech-matrix.md +101 -0
- package/core/templates/tech-stack.md +169 -0
- package/core/templates/track-architecture.md +311 -0
- package/core/templates/workflow.md +187 -0
- package/integrations/agents/AGENTS.md +24384 -0
- package/integrations/copilot/.github/copilot-instructions.md +24384 -0
- package/integrations/gemini/.gemini.md +26 -0
- package/package.json +53 -0
- package/scripts/fetch-memory-engine.sh +116 -0
- package/scripts/lib.sh +256 -0
- package/scripts/tools/_lib.sh +220 -0
- package/scripts/tools/adr-index.sh +117 -0
- package/scripts/tools/check-graph-usage-report.sh +95 -0
- package/scripts/tools/check-scope-conflicts.sh +139 -0
- package/scripts/tools/check-skill-line-caps.sh +115 -0
- package/scripts/tools/check-template-noop.sh +87 -0
- package/scripts/tools/check-track-hygiene.sh +230 -0
- package/scripts/tools/classify-files.sh +231 -0
- package/scripts/tools/cycle-detect.sh +75 -0
- package/scripts/tools/detect-test-framework.sh +135 -0
- package/scripts/tools/diff-templates-vs-tracks.sh +176 -0
- package/scripts/tools/emit-skill-metrics.sh +71 -0
- package/scripts/tools/fix-whitespace.sh +192 -0
- package/scripts/tools/freshness-check.sh +143 -0
- package/scripts/tools/git-metadata.sh +203 -0
- package/scripts/tools/graph-callers.sh +74 -0
- package/scripts/tools/graph-impact.sh +93 -0
- package/scripts/tools/graph-snapshot.sh +102 -0
- package/scripts/tools/hotspot-rank.sh +75 -0
- package/scripts/tools/manage-symlinks.sh +85 -0
- package/scripts/tools/mermaid-from-graph.sh +92 -0
- package/scripts/tools/migrate-track-frontmatter.sh +241 -0
- package/scripts/tools/parse-git-log.sh +135 -0
- package/scripts/tools/parse-reports.sh +114 -0
- package/scripts/tools/render-track.sh +145 -0
- package/scripts/tools/run-coverage.sh +153 -0
- package/scripts/tools/scan-markers.sh +144 -0
- package/scripts/tools/skill-caps.conf +24 -0
- package/scripts/tools/validate-frontmatter.sh +125 -0
- package/scripts/tools/verify-citations.sh +250 -0
- package/scripts/tools/verify-doc-anchors.sh +204 -0
- package/scripts/tools/verify-graph-binary.sh +154 -0
- package/skills/GRAPH.md +332 -0
- package/skills/adr/SKILL.md +374 -0
- package/skills/assist-review/SKILL.md +49 -0
- package/skills/bughunt/SKILL.md +668 -0
- package/skills/bughunt/references/regression-tests.md +399 -0
- package/skills/change/SKILL.md +267 -0
- package/skills/coverage/SKILL.md +336 -0
- package/skills/debug/SKILL.md +201 -0
- package/skills/decompose/SKILL.md +656 -0
- package/skills/deep-review/SKILL.md +326 -0
- package/skills/deploy-checklist/SKILL.md +254 -0
- package/skills/discover/SKILL.md +66 -0
- package/skills/docs/SKILL.md +42 -0
- package/skills/documentation/SKILL.md +197 -0
- package/skills/draft/SKILL.md +177 -0
- package/skills/draft/context-files.md +57 -0
- package/skills/draft/intent-mapping.md +37 -0
- package/skills/draft/quality-guide.md +51 -0
- package/skills/graph/SKILL.md +107 -0
- package/skills/impact/SKILL.md +86 -0
- package/skills/implement/SKILL.md +794 -0
- package/skills/incident-response/SKILL.md +245 -0
- package/skills/index/SKILL.md +848 -0
- package/skills/init/SKILL.md +1784 -0
- package/skills/init/references/architecture-spec.md +1259 -0
- package/skills/integrations/SKILL.md +53 -0
- package/skills/jira/SKILL.md +577 -0
- package/skills/jira/references/review.md +1322 -0
- package/skills/learn/SKILL.md +478 -0
- package/skills/new-track/SKILL.md +841 -0
- package/skills/ops/SKILL.md +57 -0
- package/skills/plan/SKILL.md +60 -0
- package/skills/quick-review/SKILL.md +216 -0
- package/skills/revert/SKILL.md +178 -0
- package/skills/review/SKILL.md +1114 -0
- package/skills/standup/SKILL.md +183 -0
- package/skills/status/SKILL.md +183 -0
- package/skills/tech-debt/SKILL.md +318 -0
- package/skills/testing-strategy/SKILL.md +195 -0
- package/skills/tour/SKILL.md +38 -0
- package/skills/upload/SKILL.md +117 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adr-index.sh — index Architecture Decision Records.
|
|
3
|
+
#
|
|
4
|
+
# Walks --root and emits a JSON {adrs:[{id,title,date,status,path,related_tracks}]}.
|
|
5
|
+
# Default --root is <cwd>/draft/adrs. id is derived from filename prefix (NNN-…) when
|
|
6
|
+
# present, else filename without extension.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# scripts/tools/adr-index.sh [--root DIR]
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# shellcheck source=_lib.sh
|
|
13
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh"
|
|
14
|
+
|
|
15
|
+
ROOT=""
|
|
16
|
+
|
|
17
|
+
usage() {
|
|
18
|
+
cat <<'EOF'
|
|
19
|
+
adr-index.sh — emit a JSON index of ADR files.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
scripts/tools/adr-index.sh [--root DIR]
|
|
23
|
+
|
|
24
|
+
Flags:
|
|
25
|
+
--root DIR Directory containing ADR markdown files (default: draft/adrs).
|
|
26
|
+
--help Show this help.
|
|
27
|
+
|
|
28
|
+
Reads YAML frontmatter (title, date, status, related_tracks) from each ADR file.
|
|
29
|
+
Output: {adrs:[{id,title,date,status,path,related_tracks}]}
|
|
30
|
+
EOF
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
while [[ $# -gt 0 ]]; do
|
|
34
|
+
case "$1" in
|
|
35
|
+
--root) ROOT="$2"; shift 2;;
|
|
36
|
+
--help|-h) usage; exit 0;;
|
|
37
|
+
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
38
|
+
esac
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
if [[ -z "$ROOT" ]]; then
|
|
42
|
+
ROOT="draft/adrs"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [[ ! -d "$ROOT" ]]; then
|
|
46
|
+
printf '{"adrs":[]}\n'
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Related tracks: YAML list under "related_tracks:" — one item per line like " - XYZ".
|
|
51
|
+
get_related_tracks() {
|
|
52
|
+
local file="$1"
|
|
53
|
+
awk '
|
|
54
|
+
NR == 1 && /^---$/ { in_fm = 1; next }
|
|
55
|
+
in_fm && /^---$/ { exit }
|
|
56
|
+
in_fm {
|
|
57
|
+
if ($0 ~ /^related_tracks:/) { in_list = 1; next }
|
|
58
|
+
if (in_list) {
|
|
59
|
+
if ($0 ~ /^[[:space:]]*-[[:space:]]+/) {
|
|
60
|
+
v = $0
|
|
61
|
+
sub(/^[[:space:]]*-[[:space:]]+/, "", v)
|
|
62
|
+
gsub(/^"/, "", v); gsub(/"$/, "", v)
|
|
63
|
+
print v
|
|
64
|
+
} else if ($0 !~ /^[[:space:]]/) {
|
|
65
|
+
in_list = 0
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
' "$file"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
first=true
|
|
73
|
+
printf '{"adrs":['
|
|
74
|
+
while IFS= read -r -d '' file; do
|
|
75
|
+
base="$(basename "$file")"
|
|
76
|
+
id="${base%.md}"
|
|
77
|
+
if [[ "$base" =~ ^([0-9]+)- ]]; then
|
|
78
|
+
id="${BASH_REMATCH[1]}"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
title="$(get_yaml_field "$file" title)"
|
|
82
|
+
date_val="$(get_yaml_field "$file" date)"
|
|
83
|
+
status="$(get_yaml_field "$file" status)"
|
|
84
|
+
|
|
85
|
+
# If no title in frontmatter, fallback to first H1.
|
|
86
|
+
if [[ -z "$title" ]]; then
|
|
87
|
+
title="$(grep -m1 '^# ' "$file" 2>/dev/null | sed 's/^#\s*//' || true)"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
tracks=()
|
|
91
|
+
while IFS= read -r tr; do
|
|
92
|
+
[[ -n "$tr" ]] && tracks+=("$tr")
|
|
93
|
+
done < <(get_related_tracks "$file")
|
|
94
|
+
|
|
95
|
+
tr_json='['
|
|
96
|
+
tr_first=true
|
|
97
|
+
for t in "${tracks[@]+"${tracks[@]}"}"; do
|
|
98
|
+
if $tr_first; then tr_first=false; else tr_json+=','; fi
|
|
99
|
+
tr_json+="\"$(json_escape "$t")\""
|
|
100
|
+
done
|
|
101
|
+
tr_json+=']'
|
|
102
|
+
|
|
103
|
+
if $first; then first=false; else printf ','; fi
|
|
104
|
+
printf '\n {"id":"%s","title":"%s","date":"%s","status":"%s","path":"%s","related_tracks":%s}' \
|
|
105
|
+
"$(json_escape "$id")" \
|
|
106
|
+
"$(json_escape "$title")" \
|
|
107
|
+
"$(json_escape "$date_val")" \
|
|
108
|
+
"$(json_escape "$status")" \
|
|
109
|
+
"$(json_escape "$file")" \
|
|
110
|
+
"$tr_json"
|
|
111
|
+
done < <(find "$ROOT" -maxdepth 2 -type f -name '*.md' -print0 2>/dev/null | sort -z)
|
|
112
|
+
|
|
113
|
+
if $first; then
|
|
114
|
+
printf ']}\n'
|
|
115
|
+
else
|
|
116
|
+
printf '\n]}\n'
|
|
117
|
+
fi
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-graph-usage-report.sh — validate that a skill output / template contains
|
|
3
|
+
# the mandatory Graph Usage Report footer required by core/shared/graph-query.md.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# check-graph-usage-report.sh <file> [<file> ...]
|
|
7
|
+
#
|
|
8
|
+
# Behavior:
|
|
9
|
+
# - Exits 0 when every input file contains a well-formed `## Graph Usage Report`
|
|
10
|
+
# section with the five required bullets.
|
|
11
|
+
# - Exits 1 when any file is missing the section or any required bullet.
|
|
12
|
+
# - Exits 2 on usage error (no files supplied, missing argument).
|
|
13
|
+
#
|
|
14
|
+
# A file is exempt if the very first line matches `<!-- graph-usage-report:skip -->`
|
|
15
|
+
# (intended for non-code-touching templates that legitimately have no graph step).
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
20
|
+
cat <<'EOF'
|
|
21
|
+
check-graph-usage-report.sh — validate that a skill output / template contains
|
|
22
|
+
the mandatory Graph Usage Report footer required by core/shared/graph-query.md.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
check-graph-usage-report.sh <file> [<file> ...]
|
|
26
|
+
|
|
27
|
+
Required bullets under the `## Graph Usage Report` section:
|
|
28
|
+
- Graph files queried:
|
|
29
|
+
- Modules identified via graph:
|
|
30
|
+
- Files identified via graph:
|
|
31
|
+
- Filesystem grep fallbacks:
|
|
32
|
+
|
|
33
|
+
When `Graph files queried:` is `NONE`, a `- Justification:` line with a non-empty
|
|
34
|
+
value is also required.
|
|
35
|
+
|
|
36
|
+
A file is exempt if its first line is `<!-- graph-usage-report:skip -->`.
|
|
37
|
+
|
|
38
|
+
Exit codes:
|
|
39
|
+
0 every input file is valid
|
|
40
|
+
1 any input file fails validation
|
|
41
|
+
2 usage error (no files supplied)
|
|
42
|
+
EOF
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ "$#" -lt 1 ]; then
|
|
47
|
+
echo "usage: $0 <file> [<file> ...]" >&2
|
|
48
|
+
exit 2
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
REQUIRED_BULLETS=(
|
|
52
|
+
"- Graph files queried:"
|
|
53
|
+
"- Modules identified via graph:"
|
|
54
|
+
"- Files identified via graph:"
|
|
55
|
+
"- Filesystem grep fallbacks:"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
fail=0
|
|
59
|
+
|
|
60
|
+
for f in "$@"; do
|
|
61
|
+
if [ ! -f "$f" ]; then
|
|
62
|
+
echo "MISSING: $f does not exist" >&2
|
|
63
|
+
fail=1
|
|
64
|
+
continue
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if head -1 "$f" | grep -q '^<!-- graph-usage-report:skip -->'; then
|
|
68
|
+
continue
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if ! grep -q '^## Graph Usage Report' "$f"; then
|
|
72
|
+
echo "FAIL: $f — missing '## Graph Usage Report' section" >&2
|
|
73
|
+
fail=1
|
|
74
|
+
continue
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
section_start=$(grep -n '^## Graph Usage Report' "$f" | head -1 | cut -d: -f1)
|
|
78
|
+
section_body=$(awk -v start="$section_start" 'NR > start { if (/^## /) exit; print }' "$f")
|
|
79
|
+
|
|
80
|
+
for bullet in "${REQUIRED_BULLETS[@]}"; do
|
|
81
|
+
if ! printf '%s\n' "$section_body" | grep -qF -- "$bullet"; then
|
|
82
|
+
echo "FAIL: $f — Graph Usage Report missing bullet: '$bullet'" >&2
|
|
83
|
+
fail=1
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
|
|
87
|
+
if printf '%s\n' "$section_body" | grep -qE -- '^- Graph files queried:[[:space:]]*NONE'; then
|
|
88
|
+
if ! printf '%s\n' "$section_body" | grep -qE -- '^- Justification[^:]*:[[:space:]]*[^[:space:]]'; then
|
|
89
|
+
echo "FAIL: $f — 'Graph files queried: NONE' requires a populated Justification line" >&2
|
|
90
|
+
fail=1
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
exit "$fail"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-scope-conflicts.sh
|
|
3
|
+
#
|
|
4
|
+
# Walks every track in the repository (or those passed on the command line),
|
|
5
|
+
# reads `scope_includes` / `scope_excludes` from metadata.json (preferred) or
|
|
6
|
+
# the spec.md frontmatter (fallback), and flags any two tracks that share an
|
|
7
|
+
# included scope tag without one excluding the other's tag set.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# scripts/tools/check-scope-conflicts.sh # scan ./tracks/*
|
|
11
|
+
# scripts/tools/check-scope-conflicts.sh tracks/foo tracks/bar
|
|
12
|
+
# scripts/tools/check-scope-conflicts.sh --json ...
|
|
13
|
+
#
|
|
14
|
+
# Exit codes:
|
|
15
|
+
# 0 no conflicts
|
|
16
|
+
# 1 conflicts detected
|
|
17
|
+
# 2 usage / runtime error
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
22
|
+
echo "${0##*/} — Foundations quality tool (see core/ docs for full behavior)"
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
# shellcheck source=/dev/null
|
|
28
|
+
source "$SCRIPT_DIR/_lib.sh"
|
|
29
|
+
|
|
30
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
31
|
+
|
|
32
|
+
EMIT_JSON=0
|
|
33
|
+
TRACK_PATHS=()
|
|
34
|
+
|
|
35
|
+
usage() {
|
|
36
|
+
local stream=2 code=2
|
|
37
|
+
if [[ "${USAGE_HELP_MODE:-0}" == 1 ]]; then stream=1; code=0; fi
|
|
38
|
+
sed -n '2,17p' "$0" >&$stream
|
|
39
|
+
exit "$code"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
while (($#)); do
|
|
43
|
+
case "$1" in
|
|
44
|
+
-h|--help) USAGE_HELP_MODE=1 usage ;;
|
|
45
|
+
--json) EMIT_JSON=1; shift ;;
|
|
46
|
+
-*) printf 'Unknown flag: %s\n' "$1" >&2; usage ;;
|
|
47
|
+
*) TRACK_PATHS+=("$1"); shift ;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
if ((${#TRACK_PATHS[@]} == 0)); then
|
|
52
|
+
while IFS= read -r p; do TRACK_PATHS+=("$p"); done < <(discover_track_dirs "$REPO_ROOT")
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
conflict_count=0
|
|
56
|
+
declare -a conflicts=()
|
|
57
|
+
declare -a track_rels=()
|
|
58
|
+
declare -a track_includes=()
|
|
59
|
+
declare -a track_excludes=()
|
|
60
|
+
|
|
61
|
+
record() { conflicts+=("$1|$2|$3"); conflict_count=$((conflict_count + 1)); }
|
|
62
|
+
|
|
63
|
+
for t in "${TRACK_PATHS[@]}"; do
|
|
64
|
+
[[ -d "$t" ]] || { record "$t" "not-a-directory" ""; continue; }
|
|
65
|
+
track_dir="$(cd "$t" && pwd)"
|
|
66
|
+
rel="${track_dir#"$REPO_ROOT/"}"
|
|
67
|
+
inc=""; exc=""
|
|
68
|
+
[[ -f "$track_dir/metadata.json" ]] && {
|
|
69
|
+
inc="$(read_scope_array "$track_dir/metadata.json" scope_includes)"
|
|
70
|
+
exc="$(read_scope_array "$track_dir/metadata.json" scope_excludes)"
|
|
71
|
+
}
|
|
72
|
+
if [[ -z "$inc" && -f "$track_dir/spec.md" ]]; then
|
|
73
|
+
inc="$(read_scope_array "$track_dir/spec.md" scope_includes)"
|
|
74
|
+
exc="$(read_scope_array "$track_dir/spec.md" scope_excludes)"
|
|
75
|
+
fi
|
|
76
|
+
track_rels+=("$rel")
|
|
77
|
+
track_includes+=("$inc")
|
|
78
|
+
track_excludes+=("$exc")
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
for ((i=0; i<${#track_rels[@]}; i++)); do
|
|
82
|
+
for ((j=i+1; j<${#track_rels[@]}; j++)); do
|
|
83
|
+
a="${track_rels[$i]}"
|
|
84
|
+
b="${track_rels[$j]}"
|
|
85
|
+
a_inc="${track_includes[$i]}"
|
|
86
|
+
b_inc="${track_includes[$j]}"
|
|
87
|
+
a_exc="${track_excludes[$i]}"
|
|
88
|
+
b_exc="${track_excludes[$j]}"
|
|
89
|
+
for tag in $a_inc; do
|
|
90
|
+
[[ -z "$tag" ]] && continue
|
|
91
|
+
if [[ " $b_inc " == *" $tag "* ]]; then
|
|
92
|
+
conflict=1
|
|
93
|
+
for ex in $a_exc; do
|
|
94
|
+
[[ -z "$ex" ]] && continue
|
|
95
|
+
if [[ " $b_inc " == *" $ex "* ]]; then conflict=0; break; fi
|
|
96
|
+
done
|
|
97
|
+
if ((conflict)); then
|
|
98
|
+
for ex in $b_exc; do
|
|
99
|
+
[[ -z "$ex" ]] && continue
|
|
100
|
+
if [[ " $a_inc " == *" $ex "* ]]; then conflict=0; break; fi
|
|
101
|
+
done
|
|
102
|
+
fi
|
|
103
|
+
if ((conflict)); then
|
|
104
|
+
record "$a" "scope-conflict" "shares '$tag' with $b without mutual exclusion"
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
done
|
|
108
|
+
done
|
|
109
|
+
done
|
|
110
|
+
|
|
111
|
+
emit() {
|
|
112
|
+
if ((EMIT_JSON)); then
|
|
113
|
+
printf '{"conflict_count": %d, "conflicts": [\n' "$conflict_count"
|
|
114
|
+
local first=1 c track kind detail
|
|
115
|
+
for c in "${conflicts[@]}"; do
|
|
116
|
+
IFS='|' read -r track kind detail <<< "$c"
|
|
117
|
+
if ((first)); then first=0; else printf ',\n'; fi
|
|
118
|
+
printf ' {"track":"%s","kind":"%s","detail":"%s"}' \
|
|
119
|
+
"$(json_escape "$track")" "$(json_escape "$kind")" \
|
|
120
|
+
"$(json_escape "$detail")"
|
|
121
|
+
done
|
|
122
|
+
printf '\n]}\n'
|
|
123
|
+
else
|
|
124
|
+
if ((conflict_count == 0)); then
|
|
125
|
+
printf 'OK: no scope conflicts across %d track(s).\n' "${#TRACK_PATHS[@]}"
|
|
126
|
+
else
|
|
127
|
+
printf 'SCOPE: %d conflict(s) across %d track(s).\n' \
|
|
128
|
+
"$conflict_count" "${#TRACK_PATHS[@]}" >&2
|
|
129
|
+
local c track kind detail
|
|
130
|
+
for c in "${conflicts[@]}"; do
|
|
131
|
+
IFS='|' read -r track kind detail <<< "$c"
|
|
132
|
+
printf ' [%s] %s — %s\n' "$kind" "$track" "$detail" >&2
|
|
133
|
+
done
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
}
|
|
137
|
+
emit
|
|
138
|
+
|
|
139
|
+
((conflict_count == 0))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-skill-line-caps.sh
|
|
3
|
+
#
|
|
4
|
+
# Enforces per-skill line-count caps (WS-10 prompt-economy budget). Walks
|
|
5
|
+
# skills/**/SKILL.md and reports any file whose line count exceeds its cap.
|
|
6
|
+
# Caps are sourced from a config file (default scripts/tools/skill-caps.conf)
|
|
7
|
+
# or the SKILL_CAPS env. A skill not listed inherits the GLOBAL_CAP default.
|
|
8
|
+
#
|
|
9
|
+
# Modes:
|
|
10
|
+
# default warn-only (exit 0 regardless; report on stderr)
|
|
11
|
+
# --enforce strict (exit 1 on any over-cap skill)
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# scripts/tools/check-skill-line-caps.sh # warn-only
|
|
15
|
+
# scripts/tools/check-skill-line-caps.sh --enforce # strict
|
|
16
|
+
# scripts/tools/check-skill-line-caps.sh --json
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 clean (or warn-only mode regardless of findings)
|
|
20
|
+
# 1 --enforce and at least one skill exceeds its cap
|
|
21
|
+
# 2 usage / runtime error
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
26
|
+
echo "${0##*/} — Foundations quality tool (see core/ docs for full behavior)"
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
# shellcheck source=/dev/null
|
|
32
|
+
source "$SCRIPT_DIR/_lib.sh"
|
|
33
|
+
|
|
34
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
35
|
+
CAPS_CONF_DEFAULT="$SCRIPT_DIR/skill-caps.conf"
|
|
36
|
+
|
|
37
|
+
EMIT_JSON=0
|
|
38
|
+
ENFORCE=0
|
|
39
|
+
CAPS_CONF="$CAPS_CONF_DEFAULT"
|
|
40
|
+
SKILLS_DIR="$REPO_ROOT/skills"
|
|
41
|
+
|
|
42
|
+
usage() {
|
|
43
|
+
local stream=2 code=2
|
|
44
|
+
if [[ "${USAGE_HELP_MODE:-0}" == 1 ]]; then stream=1; code=0; fi
|
|
45
|
+
sed -n '2,21p' "$0" >&$stream
|
|
46
|
+
exit "$code"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
while (($#)); do
|
|
50
|
+
case "$1" in
|
|
51
|
+
-h|--help) USAGE_HELP_MODE=1 usage ;;
|
|
52
|
+
--enforce) ENFORCE=1; shift ;;
|
|
53
|
+
--json) EMIT_JSON=1; shift ;;
|
|
54
|
+
--caps) CAPS_CONF="$2"; shift 2 ;;
|
|
55
|
+
--skills-dir) SKILLS_DIR="$2"; shift 2 ;;
|
|
56
|
+
-*) printf 'Unknown flag: %s\n' "$1" >&2; usage ;;
|
|
57
|
+
*) printf 'Unexpected arg: %s\n' "$1" >&2; usage ;;
|
|
58
|
+
esac
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
GLOBAL_CAP=600
|
|
62
|
+
over_count=0
|
|
63
|
+
declare -a findings=()
|
|
64
|
+
record() { findings+=("$1|$2|$3"); over_count=$((over_count + 1)); }
|
|
65
|
+
|
|
66
|
+
while IFS= read -r path; do
|
|
67
|
+
rel="${path#"$REPO_ROOT/"}"
|
|
68
|
+
name="$(basename "$(dirname "$path")")"
|
|
69
|
+
lines="$(wc -l < "$path" | tr -d ' ')"
|
|
70
|
+
cap="$(skill_line_cap "$name" "$CAPS_CONF" "$GLOBAL_CAP")"
|
|
71
|
+
if (( lines > cap )); then
|
|
72
|
+
record "$name" "$lines" "$cap"
|
|
73
|
+
fi
|
|
74
|
+
done < <(find "$SKILLS_DIR" -mindepth 2 -maxdepth 2 -name 'SKILL.md' | sort)
|
|
75
|
+
|
|
76
|
+
emit() {
|
|
77
|
+
if ((EMIT_JSON)); then
|
|
78
|
+
printf '{"over_cap_count": %d, "global_cap": %d, "findings": [\n' \
|
|
79
|
+
"$over_count" "$GLOBAL_CAP"
|
|
80
|
+
local first=1 v name lines cap
|
|
81
|
+
for v in "${findings[@]}"; do
|
|
82
|
+
IFS='|' read -r name lines cap <<< "$v"
|
|
83
|
+
if ((first)); then first=0; else printf ',\n'; fi
|
|
84
|
+
printf ' {"skill":"%s","lines":%d,"cap":%d}' \
|
|
85
|
+
"$(json_escape "$name")" "$lines" "$cap"
|
|
86
|
+
done
|
|
87
|
+
printf '\n]}\n'
|
|
88
|
+
else
|
|
89
|
+
if ((over_count == 0)); then
|
|
90
|
+
printf 'OK: all skills within cap (default %d).\n' "$GLOBAL_CAP"
|
|
91
|
+
return
|
|
92
|
+
fi
|
|
93
|
+
local mode="warn-only" stream=stderr
|
|
94
|
+
((ENFORCE)) && mode="enforced"
|
|
95
|
+
if ((ENFORCE)); then
|
|
96
|
+
printf 'SKILL-CAPS: %d skill(s) over cap (%s):\n' \
|
|
97
|
+
"$over_count" "$mode" >&2
|
|
98
|
+
else
|
|
99
|
+
printf 'SKILL-CAPS: %d skill(s) over cap (%s):\n' \
|
|
100
|
+
"$over_count" "$mode"
|
|
101
|
+
fi
|
|
102
|
+
local v name lines cap
|
|
103
|
+
for v in "${findings[@]}"; do
|
|
104
|
+
IFS='|' read -r name lines cap <<< "$v"
|
|
105
|
+
if ((ENFORCE)); then
|
|
106
|
+
printf ' %s: %d lines (cap %d)\n' "$name" "$lines" "$cap" >&2
|
|
107
|
+
else
|
|
108
|
+
printf ' %s: %d lines (cap %d)\n' "$name" "$lines" "$cap"
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
fi
|
|
112
|
+
}
|
|
113
|
+
emit
|
|
114
|
+
|
|
115
|
+
if ((ENFORCE && over_count > 0)); then exit 1; fi
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-template-noop.sh
|
|
3
|
+
#
|
|
4
|
+
# CI gate enforcing WS-0 contract: any commit that touches skills/** or
|
|
5
|
+
# scripts/tools/** must also touch core/templates/**, or carry the literal
|
|
6
|
+
# tag [template-noop] in the commit message.
|
|
7
|
+
#
|
|
8
|
+
# Rationale: skill-only or validator-only schema changes silently drift from
|
|
9
|
+
# the templates. Forcing them to travel together (or explicitly opt out) keeps
|
|
10
|
+
# templates as the single canonical schema source.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# scripts/tools/check-template-noop.sh # HEAD vs HEAD~1
|
|
14
|
+
# scripts/tools/check-template-noop.sh <base> # HEAD vs base
|
|
15
|
+
# scripts/tools/check-template-noop.sh <base>..<head> # range form
|
|
16
|
+
#
|
|
17
|
+
# Exit codes:
|
|
18
|
+
# 0 OK (no relevant changes, or templates also touched, or [template-noop])
|
|
19
|
+
# 1 violation
|
|
20
|
+
# 2 usage / runtime error
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
25
|
+
echo "${0##*/} — Foundations quality tool (see core/ docs for full behavior)"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
BASE="${1:-HEAD~1}"
|
|
30
|
+
RANGE="$BASE..HEAD"
|
|
31
|
+
if [[ "$BASE" == *..* ]]; then
|
|
32
|
+
RANGE="$BASE"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
36
|
+
printf 'check-template-noop: not inside a git repository\n' >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Diff name-only across the range.
|
|
41
|
+
changed_files="$(git diff --name-only "$RANGE" 2>/dev/null || true)"
|
|
42
|
+
if [[ -z "$changed_files" ]]; then
|
|
43
|
+
printf 'check-template-noop: no changes in range %s\n' "$RANGE"
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
touches_skills_or_tools=0
|
|
48
|
+
touches_templates=0
|
|
49
|
+
|
|
50
|
+
while IFS= read -r f; do
|
|
51
|
+
[[ -z "$f" ]] && continue
|
|
52
|
+
case "$f" in
|
|
53
|
+
skills/*|scripts/tools/*)
|
|
54
|
+
# Skip changes to the templates themselves living under scripts.
|
|
55
|
+
touches_skills_or_tools=1 ;;
|
|
56
|
+
core/templates/*)
|
|
57
|
+
touches_templates=1 ;;
|
|
58
|
+
esac
|
|
59
|
+
done <<< "$changed_files"
|
|
60
|
+
|
|
61
|
+
if ((touches_skills_or_tools == 0)); then
|
|
62
|
+
printf 'check-template-noop: no skills/** or scripts/tools/** changes in range %s\n' "$RANGE"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if ((touches_templates == 1)); then
|
|
67
|
+
printf 'check-template-noop: OK — skills/tools changes accompanied by core/templates/** changes.\n'
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Look for [template-noop] tag in any commit message in the range.
|
|
72
|
+
commit_log="$(git log --format=%B "$RANGE" 2>/dev/null || true)"
|
|
73
|
+
if printf '%s' "$commit_log" | grep -Fq '[template-noop]'; then
|
|
74
|
+
printf 'check-template-noop: OK — [template-noop] tag present in commit message.\n'
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
cat >&2 <<EOF
|
|
79
|
+
check-template-noop: FAIL
|
|
80
|
+
Range: $RANGE
|
|
81
|
+
skills/** or scripts/tools/** changed without touching core/templates/**.
|
|
82
|
+
Either:
|
|
83
|
+
(a) update core/templates/* to reflect the schema change, or
|
|
84
|
+
(b) add [template-noop] to the commit message if this is intentionally a no-op
|
|
85
|
+
for the template schema (e.g. pure bug fix, refactor, perf improvement).
|
|
86
|
+
EOF
|
|
87
|
+
exit 1
|