@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,145 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# render-track.sh
|
|
3
|
+
#
|
|
4
|
+
# Render a Draft track's markdown set into a single HTML viewer artifact
|
|
5
|
+
# on demand. Replaces the pre-2.0 pattern of checking generated HTML into
|
|
6
|
+
# the track directory; viewer artifacts are now git-ignored and rebuilt
|
|
7
|
+
# whenever a reader wants them.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# scripts/tools/render-track.sh <track_dir> [--out <path>]
|
|
11
|
+
# scripts/tools/render-track.sh <track_dir> --stdout
|
|
12
|
+
#
|
|
13
|
+
# Default output: <track_dir>/track-reader.html (git-ignored).
|
|
14
|
+
#
|
|
15
|
+
# Implementation note: pandoc is preferred if present; otherwise a minimal
|
|
16
|
+
# markdown-to-HTML render is used via awk. No external network calls.
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
21
|
+
echo "${0##*/} — Foundations quality tool (see core/ docs for full behavior)"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
# shellcheck source=/dev/null
|
|
27
|
+
source "$SCRIPT_DIR/_lib.sh"
|
|
28
|
+
|
|
29
|
+
html_escape() {
|
|
30
|
+
local s="$1"
|
|
31
|
+
s="${s//&/&}"
|
|
32
|
+
s="${s//</<}"
|
|
33
|
+
s="${s//>/>}"
|
|
34
|
+
printf '%s' "$s"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
usage() {
|
|
38
|
+
local stream=2 code=2
|
|
39
|
+
if [[ "${USAGE_HELP_MODE:-0}" == 1 ]]; then stream=1; code=0; fi
|
|
40
|
+
sed -n '2,16p' "$0" >&$stream
|
|
41
|
+
exit "$code"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TRACK_DIR=""
|
|
45
|
+
OUT_PATH=""
|
|
46
|
+
TO_STDOUT=0
|
|
47
|
+
|
|
48
|
+
while (($#)); do
|
|
49
|
+
case "$1" in
|
|
50
|
+
-h|--help) USAGE_HELP_MODE=1 usage ;;
|
|
51
|
+
--out) OUT_PATH="$2"; shift 2 ;;
|
|
52
|
+
--stdout) TO_STDOUT=1; shift ;;
|
|
53
|
+
-*) printf 'Unknown flag: %s\n' "$1" >&2; usage ;;
|
|
54
|
+
*) TRACK_DIR="$1"; shift ;;
|
|
55
|
+
esac
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
[[ -z "$TRACK_DIR" ]] && usage
|
|
59
|
+
[[ ! -d "$TRACK_DIR" ]] && { printf 'Not a directory: %s\n' "$TRACK_DIR" >&2; exit 2; }
|
|
60
|
+
|
|
61
|
+
TRACK_DIR="$(cd "$TRACK_DIR" && pwd)"
|
|
62
|
+
track_id="$(basename "$TRACK_DIR")"
|
|
63
|
+
|
|
64
|
+
if (( ! TO_STDOUT )) && [[ -z "$OUT_PATH" ]]; then
|
|
65
|
+
OUT_PATH="$TRACK_DIR/track-reader.html"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Pre-2.0 cleanup: warn if a stale HTML artifact is checked in.
|
|
69
|
+
if (( ! TO_STDOUT )) && git -C "$TRACK_DIR" ls-files --error-unmatch \
|
|
70
|
+
"track-reader.html" >/dev/null 2>&1; then
|
|
71
|
+
printf 'render-track: WARNING — track-reader.html is checked into git.\n' >&2
|
|
72
|
+
printf ' Remove it: git rm tracks/.../track-reader.html\n' >&2
|
|
73
|
+
printf ' Draft 2.0 makes viewer artifacts git-ignored runtime output.\n' >&2
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
ORDER=(spec.md plan.md hld.md lld.md discovery.md)
|
|
77
|
+
ALL_MD=("${ORDER[@]}")
|
|
78
|
+
# Append any extras found that are not already in ORDER.
|
|
79
|
+
while IFS= read -r f; do
|
|
80
|
+
name="$(basename "$f")"
|
|
81
|
+
skip=0
|
|
82
|
+
for o in "${ORDER[@]}"; do [[ "$name" == "$o" ]] && skip=1 && break; done
|
|
83
|
+
(( skip )) || ALL_MD+=("$name")
|
|
84
|
+
done < <(find "$TRACK_DIR" -maxdepth 1 -type f -name '*.md' | sort)
|
|
85
|
+
|
|
86
|
+
render_with_pandoc() {
|
|
87
|
+
local title="$1"; shift
|
|
88
|
+
local files=()
|
|
89
|
+
for f in "$@"; do
|
|
90
|
+
[[ -f "$TRACK_DIR/$f" ]] && files+=("$TRACK_DIR/$f")
|
|
91
|
+
done
|
|
92
|
+
(( ${#files[@]} > 0 )) || return 1
|
|
93
|
+
pandoc -s --toc --metadata title="$title" "${files[@]}"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
render_minimal() {
|
|
97
|
+
local title="$1"; shift
|
|
98
|
+
local files=()
|
|
99
|
+
for f in "$@"; do
|
|
100
|
+
[[ -f "$TRACK_DIR/$f" ]] && files+=("$TRACK_DIR/$f")
|
|
101
|
+
done
|
|
102
|
+
(( ${#files[@]} > 0 )) || return 1
|
|
103
|
+
printf '<!doctype html>\n<html><head><meta charset="utf-8">'
|
|
104
|
+
printf '<title>%s</title>' "$(html_escape "$title")"
|
|
105
|
+
cat <<'CSS'
|
|
106
|
+
<style>
|
|
107
|
+
body{font:14px/1.5 system-ui,sans-serif;max-width:920px;margin:2em auto;padding:0 1em;color:#222}
|
|
108
|
+
h1,h2,h3{font-weight:600}h1{border-bottom:1px solid #ccc;padding-bottom:.3em}
|
|
109
|
+
code{background:#f4f4f4;padding:.1em .3em;border-radius:3px;font-size:.95em}
|
|
110
|
+
pre{background:#f8f8f8;padding:.8em;border-radius:5px;overflow-x:auto}
|
|
111
|
+
table{border-collapse:collapse}td,th{border:1px solid #ddd;padding:.4em .6em}
|
|
112
|
+
nav.toc{background:#fafafa;border:1px solid #eee;padding:.6em 1em;margin:1em 0}
|
|
113
|
+
hr.docsep{margin:3em 0;border:0;border-top:2px dashed #ccc}
|
|
114
|
+
.meta{color:#888;font-size:.9em}
|
|
115
|
+
</style></head><body>
|
|
116
|
+
CSS
|
|
117
|
+
printf '<h1>%s</h1>\n<nav class="toc"><strong>Documents</strong><ul>\n' "$(html_escape "$title")"
|
|
118
|
+
for f in "${files[@]}"; do
|
|
119
|
+
local n; n="$(basename "$f")"
|
|
120
|
+
printf '<li><a href="#%s">%s</a></li>\n' "$(html_escape "$n")" "$(html_escape "$n")"
|
|
121
|
+
done
|
|
122
|
+
printf '</ul></nav>\n'
|
|
123
|
+
for f in "${files[@]}"; do
|
|
124
|
+
local n; n="$(basename "$f")"
|
|
125
|
+
printf '<hr class="docsep"><section id="%s"><p class="meta">— %s —</p>\n<pre><code>' "$(html_escape "$n")" "$(html_escape "$n")"
|
|
126
|
+
sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g' "$f"
|
|
127
|
+
printf '</code></pre></section>\n'
|
|
128
|
+
done
|
|
129
|
+
printf '</body></html>\n'
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
OUTPUT=""
|
|
133
|
+
if command -v pandoc >/dev/null 2>&1; then
|
|
134
|
+
OUTPUT="$(render_with_pandoc "Draft Track: $track_id" "${ALL_MD[@]}" || true)"
|
|
135
|
+
fi
|
|
136
|
+
if [[ -z "$OUTPUT" ]]; then
|
|
137
|
+
OUTPUT="$(render_minimal "Draft Track: $track_id" "${ALL_MD[@]}")"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if (( TO_STDOUT )); then
|
|
141
|
+
printf '%s' "$OUTPUT"
|
|
142
|
+
else
|
|
143
|
+
printf '%s' "$OUTPUT" > "$OUT_PATH"
|
|
144
|
+
printf 'Wrote %s (%d bytes)\n' "$OUT_PATH" "${#OUTPUT}"
|
|
145
|
+
fi
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# run-coverage.sh — normalized coverage dispatcher.
|
|
3
|
+
#
|
|
4
|
+
# Dispatches to a language-specific coverage runner and emits a normalized
|
|
5
|
+
# JSON report:
|
|
6
|
+
# {language, tool, total:{lines,branches}, per_file:[{path,lines,branches,uncovered_lines}]}
|
|
7
|
+
#
|
|
8
|
+
# The actual coverage run is language-specific and may be slow. Use
|
|
9
|
+
# `--schema-check` in CI to validate output shape without running tests.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# scripts/tools/run-coverage.sh <language> [--path DIR]
|
|
13
|
+
# [--schema-check]
|
|
14
|
+
#
|
|
15
|
+
# Languages: python (pytest --cov), go (go test -coverprofile), javascript (nyc), shell (n/a → 2)
|
|
16
|
+
#
|
|
17
|
+
# Exit codes: 0 OK, 1 invocation error, 2 tool unavailable (emits empty-schema JSON).
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
LANGUAGE=""
|
|
21
|
+
COVERAGE_PATH="."
|
|
22
|
+
SCHEMA_CHECK="false"
|
|
23
|
+
|
|
24
|
+
usage() {
|
|
25
|
+
cat <<'EOF'
|
|
26
|
+
run-coverage.sh — normalized coverage report dispatcher.
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
scripts/tools/run-coverage.sh <language> [--path DIR] [--schema-check]
|
|
30
|
+
|
|
31
|
+
Languages:
|
|
32
|
+
python pytest --cov (requires pytest-cov)
|
|
33
|
+
go go test -coverprofile
|
|
34
|
+
javascript nyc/c8 (via package.json)
|
|
35
|
+
shell not supported — emits schema-valid empty report (exit 2)
|
|
36
|
+
|
|
37
|
+
Flags:
|
|
38
|
+
--path DIR Path to run coverage against (default: cwd).
|
|
39
|
+
--schema-check Emit schema-valid empty JSON without running any tests (for CI).
|
|
40
|
+
--help Show this help.
|
|
41
|
+
|
|
42
|
+
Output schema (always present, even on exit 2):
|
|
43
|
+
{
|
|
44
|
+
"language": "<lang>",
|
|
45
|
+
"tool": "<coverage-tool>",
|
|
46
|
+
"total": {"lines": <float 0..1>, "branches": <float 0..1|null>},
|
|
47
|
+
"per_file": [{"path","lines","branches","uncovered_lines":[<int>...]}]
|
|
48
|
+
}
|
|
49
|
+
EOF
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
while [[ $# -gt 0 ]]; do
|
|
53
|
+
case "$1" in
|
|
54
|
+
--path) COVERAGE_PATH="$2"; shift 2;;
|
|
55
|
+
--schema-check) SCHEMA_CHECK="true"; shift;;
|
|
56
|
+
--help|-h) usage; exit 0;;
|
|
57
|
+
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
58
|
+
*)
|
|
59
|
+
if [[ -z "$LANGUAGE" ]]; then
|
|
60
|
+
LANGUAGE="$1"; shift
|
|
61
|
+
else
|
|
62
|
+
echo "Unexpected positional arg: $1" >&2; exit 1
|
|
63
|
+
fi
|
|
64
|
+
;;
|
|
65
|
+
esac
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
if [[ -z "$LANGUAGE" ]]; then
|
|
69
|
+
echo "ERROR: language is required (see --help)" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
emit_empty() {
|
|
74
|
+
local lang="$1" tool="$2"
|
|
75
|
+
printf '{"language":"%s","tool":"%s","total":{"lines":null,"branches":null},"per_file":[]}\n' \
|
|
76
|
+
"$lang" "$tool"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Schema-only mode: never run the real coverage tool.
|
|
80
|
+
if [[ "$SCHEMA_CHECK" == "true" ]]; then
|
|
81
|
+
case "$LANGUAGE" in
|
|
82
|
+
python) tool="pytest-cov";;
|
|
83
|
+
go) tool="go-cover";;
|
|
84
|
+
javascript) tool="nyc";;
|
|
85
|
+
typescript) tool="nyc";;
|
|
86
|
+
shell) tool="none";;
|
|
87
|
+
*)
|
|
88
|
+
echo "ERROR: unsupported language '$LANGUAGE'" >&2
|
|
89
|
+
exit 1
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
emit_empty "$LANGUAGE" "$tool"
|
|
93
|
+
[[ "$LANGUAGE" == "shell" ]] && exit 2
|
|
94
|
+
exit 0
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
case "$LANGUAGE" in
|
|
98
|
+
python)
|
|
99
|
+
if ! command -v pytest >/dev/null 2>&1; then
|
|
100
|
+
emit_empty python pytest-cov
|
|
101
|
+
exit 2
|
|
102
|
+
fi
|
|
103
|
+
# Run, capture JSON if coverage.py is available.
|
|
104
|
+
tmp_json="$(mktemp)"
|
|
105
|
+
trap 'rm -f "$tmp_json"' EXIT
|
|
106
|
+
(cd "$COVERAGE_PATH" && pytest --cov --cov-report=json:"$tmp_json" >/dev/null 2>&1) || true
|
|
107
|
+
if [[ -s "$tmp_json" ]] && command -v jq >/dev/null 2>&1; then
|
|
108
|
+
jq '{language:"python", tool:"pytest-cov",
|
|
109
|
+
total:{lines: (.totals.percent_covered // 0) / 100, branches: null},
|
|
110
|
+
per_file: (.files | to_entries | map({
|
|
111
|
+
path: .key,
|
|
112
|
+
lines: (.value.summary.percent_covered // 0) / 100,
|
|
113
|
+
branches: null,
|
|
114
|
+
uncovered_lines: (.value.missing_lines // [])
|
|
115
|
+
}))}' "$tmp_json"
|
|
116
|
+
exit 0
|
|
117
|
+
fi
|
|
118
|
+
emit_empty python pytest-cov
|
|
119
|
+
exit 2
|
|
120
|
+
;;
|
|
121
|
+
go)
|
|
122
|
+
if ! command -v go >/dev/null 2>&1; then
|
|
123
|
+
emit_empty go go-cover
|
|
124
|
+
exit 2
|
|
125
|
+
fi
|
|
126
|
+
tmp_cov="$(mktemp)"
|
|
127
|
+
trap 'rm -f "$tmp_cov"' EXIT
|
|
128
|
+
(cd "$COVERAGE_PATH" && go test -coverprofile="$tmp_cov" ./... >/dev/null 2>&1) || true
|
|
129
|
+
if [[ -s "$tmp_cov" ]]; then
|
|
130
|
+
total=$(go tool cover -func="$tmp_cov" 2>/dev/null | awk '/^total:/ {gsub(/%/, "", $3); print $3/100}')
|
|
131
|
+
total="${total:-null}"
|
|
132
|
+
printf '{"language":"go","tool":"go-cover","total":{"lines":%s,"branches":null},"per_file":[]}\n' \
|
|
133
|
+
"$total"
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
emit_empty go go-cover
|
|
137
|
+
exit 2
|
|
138
|
+
;;
|
|
139
|
+
javascript|typescript)
|
|
140
|
+
# Real coverage via nyc/c8 is not yet wired — emit empty schema (exit 2)
|
|
141
|
+
# so consumers can degrade gracefully. See --schema-check for CI validation.
|
|
142
|
+
emit_empty "$LANGUAGE" nyc
|
|
143
|
+
exit 2
|
|
144
|
+
;;
|
|
145
|
+
shell)
|
|
146
|
+
emit_empty shell none
|
|
147
|
+
exit 2
|
|
148
|
+
;;
|
|
149
|
+
*)
|
|
150
|
+
echo "ERROR: unsupported language '$LANGUAGE'" >&2
|
|
151
|
+
exit 1
|
|
152
|
+
;;
|
|
153
|
+
esac
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scan-markers.sh — find TODO/FIXME/HACK/XXX/DEPRECATED markers with blame age.
|
|
3
|
+
#
|
|
4
|
+
# Emits a JSON array. Per entry:
|
|
5
|
+
# {path, line, marker, text, sha, author, introduced, age_days}
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# scripts/tools/scan-markers.sh [--root DIR] [--markers LIST]
|
|
9
|
+
# [--min-age-days N] [--include-untracked]
|
|
10
|
+
#
|
|
11
|
+
# Exit codes: 0 OK (even with zero hits), 1 invocation error, 2 not a git repo
|
|
12
|
+
# (emits [] on stdout so consumers can still parse).
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# shellcheck source=_lib.sh
|
|
16
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh"
|
|
17
|
+
|
|
18
|
+
ROOT="."
|
|
19
|
+
MARKERS="TODO,FIXME,HACK,XXX,DEPRECATED"
|
|
20
|
+
MIN_AGE=0
|
|
21
|
+
|
|
22
|
+
usage() {
|
|
23
|
+
cat <<'EOF'
|
|
24
|
+
scan-markers.sh — find code markers (TODO/FIXME/...) with git blame age.
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
scripts/tools/scan-markers.sh [--root DIR] [--markers LIST] [--min-age-days N]
|
|
28
|
+
|
|
29
|
+
Flags:
|
|
30
|
+
--root DIR Root directory to scan (default: cwd).
|
|
31
|
+
--markers LIST Comma-separated marker list (default: TODO,FIXME,HACK,XXX,DEPRECATED).
|
|
32
|
+
--min-age-days N Only emit markers older than N days (default: 0).
|
|
33
|
+
--help Show this help.
|
|
34
|
+
|
|
35
|
+
Output: JSON array of {path, line, marker, text, sha, author, introduced, age_days}.
|
|
36
|
+
EOF
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--root) ROOT="$2"; shift 2;;
|
|
42
|
+
--markers) MARKERS="$2"; shift 2;;
|
|
43
|
+
--min-age-days) MIN_AGE="$2"; shift 2;;
|
|
44
|
+
--help|-h) usage; exit 0;;
|
|
45
|
+
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
46
|
+
esac
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
if [[ ! -d "$ROOT" ]]; then
|
|
50
|
+
echo "ERROR: --root '$ROOT' is not a directory" >&2
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
cd "$ROOT"
|
|
55
|
+
|
|
56
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
57
|
+
echo "[]"
|
|
58
|
+
exit 2
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Build a regex like "\b(TODO|FIXME|HACK|XXX|DEPRECATED)\b"
|
|
62
|
+
IFS=',' read -ra MARK_ARRAY <<<"$MARKERS"
|
|
63
|
+
PATTERN_INNER="$(IFS='|'; echo "${MARK_ARRAY[*]}")"
|
|
64
|
+
|
|
65
|
+
TMP="$(mktemp)"
|
|
66
|
+
trap 'rm -f "$TMP"' EXIT
|
|
67
|
+
|
|
68
|
+
# Prefer ripgrep; fall back to git grep.
|
|
69
|
+
if command -v rg >/dev/null 2>&1; then
|
|
70
|
+
rg -n --no-heading -w -e "($PATTERN_INNER)" . >"$TMP" 2>/dev/null || true
|
|
71
|
+
else
|
|
72
|
+
git grep -n -w -E "$PATTERN_INNER" -- . >"$TMP" 2>/dev/null || true
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
NOW_SEC="$(date -u +%s)"
|
|
76
|
+
|
|
77
|
+
emit_array() {
|
|
78
|
+
local first=true
|
|
79
|
+
printf '['
|
|
80
|
+
while IFS= read -r _ln; do
|
|
81
|
+
path="${_ln%%:*}"; rest="${_ln#*:}"; linenum="${rest%%:*}"; rest="${rest#*:}"
|
|
82
|
+
[[ -z "$path" || -z "$linenum" ]] && continue
|
|
83
|
+
# Skip binaries or tool's own output directory.
|
|
84
|
+
[[ "$path" == */.git/* ]] && continue
|
|
85
|
+
|
|
86
|
+
# Identify which marker matched first in this line (pure-bash, no fork).
|
|
87
|
+
marker=""
|
|
88
|
+
for m in "${MARK_ARRAY[@]}"; do
|
|
89
|
+
if [[ "$rest" == *"$m"* ]]; then
|
|
90
|
+
marker="$m"
|
|
91
|
+
break
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
[[ -z "$marker" ]] && continue
|
|
95
|
+
|
|
96
|
+
# Trim whitespace from text
|
|
97
|
+
text="${rest#"${rest%%[![:space:]]*}"}"
|
|
98
|
+
text="${text%"${text##*[![:space:]]}"}"
|
|
99
|
+
# Limit text length
|
|
100
|
+
if [[ ${#text} -gt 300 ]]; then
|
|
101
|
+
text="${text:0:300}"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Blame info
|
|
105
|
+
sha="null"
|
|
106
|
+
author=""
|
|
107
|
+
introduced=""
|
|
108
|
+
age_days=0
|
|
109
|
+
if blame_line="$(git blame -L "$linenum,$linenum" --porcelain -- "$path" 2>/dev/null)"; then
|
|
110
|
+
blame_sha="$(echo "$blame_line" | head -1 | awk '{print $1}')"
|
|
111
|
+
if [[ -n "$blame_sha" && "$blame_sha" != "0000000000000000000000000000000000000000" ]]; then
|
|
112
|
+
sha="\"${blame_sha:0:7}\""
|
|
113
|
+
author="$(echo "$blame_line" | awk '/^author / {sub(/^author /,""); print; exit}')"
|
|
114
|
+
ts="$(echo "$blame_line" | awk '/^author-time / {print $2; exit}')"
|
|
115
|
+
if [[ -n "$ts" ]]; then
|
|
116
|
+
introduced="$(date -u -d "@$ts" +%Y-%m-%d 2>/dev/null || date -u -r "$ts" +%Y-%m-%d 2>/dev/null || echo "")"
|
|
117
|
+
age_days=$(( (NOW_SEC - ts) / 86400 ))
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [[ "$age_days" -lt "$MIN_AGE" ]]; then
|
|
123
|
+
continue
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if $first; then first=false; else printf ','; fi
|
|
127
|
+
printf '\n {"path":"%s","line":%s,"marker":"%s","text":"%s","sha":%s,"author":"%s","introduced":"%s","age_days":%s}' \
|
|
128
|
+
"$(json_escape "$path")" \
|
|
129
|
+
"$linenum" \
|
|
130
|
+
"$marker" \
|
|
131
|
+
"$(json_escape "$text")" \
|
|
132
|
+
"$sha" \
|
|
133
|
+
"$(json_escape "$author")" \
|
|
134
|
+
"$(json_escape "$introduced")" \
|
|
135
|
+
"$age_days"
|
|
136
|
+
done <"$TMP"
|
|
137
|
+
if $first; then
|
|
138
|
+
printf ']\n'
|
|
139
|
+
else
|
|
140
|
+
printf '\n]\n'
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
emit_array
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# scripts/tools/skill-caps.conf
|
|
2
|
+
#
|
|
3
|
+
# Per-skill max-line caps consumed by scripts/tools/check-skill-line-caps.sh.
|
|
4
|
+
# Format: <skill-name> <max-lines>
|
|
5
|
+
# Lines starting with '#' or blank lines are ignored.
|
|
6
|
+
# `*` sets the global default cap when a skill is not listed.
|
|
7
|
+
#
|
|
8
|
+
# Initial caps (2026-05-17): warn-only mode. These reflect the WS-10
|
|
9
|
+
# prompt-economy targets. Treat them as aspirational floors; tighten as
|
|
10
|
+
# core/shared/*.md extraction lands. The validator runs in --enforce mode
|
|
11
|
+
# only when invoked with the flag explicitly.
|
|
12
|
+
|
|
13
|
+
# Default for any unlisted skill.
|
|
14
|
+
* 600
|
|
15
|
+
|
|
16
|
+
# Large generators — known oversized today; extraction work continues.
|
|
17
|
+
init 3500
|
|
18
|
+
status 1300
|
|
19
|
+
bughunt 1200
|
|
20
|
+
new-track 1100
|
|
21
|
+
review 1000
|
|
22
|
+
index 900
|
|
23
|
+
implement 900
|
|
24
|
+
decompose 700
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-frontmatter.sh — validate YAML frontmatter of a Markdown file.
|
|
3
|
+
#
|
|
4
|
+
# Checks:
|
|
5
|
+
# 1. File starts with a "---" delimiter on line 1.
|
|
6
|
+
# 2. A closing "---" delimiter exists.
|
|
7
|
+
# 3. Every --require field is present as a top-level key.
|
|
8
|
+
# 4. In --mode project-doc: rejects forbidden ephemeral fields (git.*, synced_to_commit)
|
|
9
|
+
# that must live in draft/metadata.json instead of per-file frontmatter (WS-8).
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# scripts/tools/validate-frontmatter.sh <FILE> [--require FIELD[,FIELD...]]
|
|
13
|
+
# [--mode project-doc]
|
|
14
|
+
#
|
|
15
|
+
# Exit codes: 0 valid, 1 invalid, 2 file not found.
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
FILE=""
|
|
19
|
+
REQUIRED=""
|
|
20
|
+
MODE=""
|
|
21
|
+
|
|
22
|
+
usage() {
|
|
23
|
+
cat <<'EOF'
|
|
24
|
+
validate-frontmatter.sh — validate a Markdown file's YAML frontmatter.
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
scripts/tools/validate-frontmatter.sh <FILE> [--require FIELD[,FIELD...]]
|
|
28
|
+
[--mode project-doc]
|
|
29
|
+
|
|
30
|
+
Flags:
|
|
31
|
+
--require LIST Comma-separated required top-level keys (default: name,description).
|
|
32
|
+
--mode project-doc Enforce WS-8: reject git.*, synced_to_commit, and dirty fields
|
|
33
|
+
in per-file frontmatter. Use for draft/ project-level artifacts
|
|
34
|
+
(architecture.md, .ai-context.md, product.md, etc.) whose git
|
|
35
|
+
state must live in draft/metadata.json.
|
|
36
|
+
--help Show this help.
|
|
37
|
+
|
|
38
|
+
Exit 0 valid, 1 invalid (writes diagnostics to stderr), 2 file not found.
|
|
39
|
+
EOF
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
REQUIRED="name,description"
|
|
43
|
+
|
|
44
|
+
while [[ $# -gt 0 ]]; do
|
|
45
|
+
case "$1" in
|
|
46
|
+
--require) REQUIRED="$2"; shift 2;;
|
|
47
|
+
--mode) MODE="$2"; shift 2;;
|
|
48
|
+
--help|-h) usage; exit 0;;
|
|
49
|
+
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
50
|
+
*)
|
|
51
|
+
if [[ -z "$FILE" ]]; then FILE="$1"
|
|
52
|
+
else echo "Unexpected arg: $1" >&2; exit 1
|
|
53
|
+
fi
|
|
54
|
+
shift
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
if [[ -z "$FILE" ]]; then
|
|
60
|
+
usage >&2
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [[ ! -f "$FILE" ]]; then
|
|
65
|
+
echo "ERROR: file not found: $FILE" >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
IFS= read -r first_line <"$FILE" || true
|
|
70
|
+
if [[ "$first_line" != "---" ]]; then
|
|
71
|
+
echo "ERROR: $FILE — missing opening '---' delimiter on line 1" >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if ! awk 'NR > 1 && /^---$/ { found=1; exit } END { exit !found }' "$FILE"; then
|
|
76
|
+
echo "ERROR: $FILE — missing closing '---' delimiter" >&2
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
frontmatter="$(awk '
|
|
81
|
+
NR == 1 && /^---$/ { in_fm = 1; next }
|
|
82
|
+
in_fm && /^---$/ { exit }
|
|
83
|
+
in_fm { print }
|
|
84
|
+
' "$FILE")"
|
|
85
|
+
|
|
86
|
+
# Validate each required field is present at top-level (no indentation).
|
|
87
|
+
IFS=',' read -ra FIELDS <<<"$REQUIRED"
|
|
88
|
+
MISSING=()
|
|
89
|
+
for field in "${FIELDS[@]}"; do
|
|
90
|
+
field="${field// /}"
|
|
91
|
+
[[ -z "$field" ]] && continue
|
|
92
|
+
if ! printf '%s\n' "$frontmatter" | grep -qE "^${field}:"; then
|
|
93
|
+
MISSING+=("$field")
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
|
98
|
+
echo "ERROR: $FILE — missing required frontmatter fields: ${MISSING[*]}" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# --mode project-doc: enforce WS-8 — no ephemeral git fields in per-file frontmatter.
|
|
103
|
+
# These must live in draft/metadata.json. Any of the patterns below are violations.
|
|
104
|
+
if [[ "$MODE" == "project-doc" ]]; then
|
|
105
|
+
FORBIDDEN=()
|
|
106
|
+
# Top-level git: block
|
|
107
|
+
if printf '%s\n' "$frontmatter" | grep -qE "^git:"; then
|
|
108
|
+
FORBIDDEN+=("git: (use draft/metadata.json)")
|
|
109
|
+
fi
|
|
110
|
+
# Top-level synced_to_commit:
|
|
111
|
+
if printf '%s\n' "$frontmatter" | grep -qE "^synced_to_commit:"; then
|
|
112
|
+
FORBIDDEN+=("synced_to_commit: (use draft/metadata.json)")
|
|
113
|
+
fi
|
|
114
|
+
# Top-level dirty: (legacy field)
|
|
115
|
+
if printf '%s\n' "$frontmatter" | grep -qE "^dirty:"; then
|
|
116
|
+
FORBIDDEN+=("dirty: (use draft/metadata.json:git.dirty)")
|
|
117
|
+
fi
|
|
118
|
+
if [[ ${#FORBIDDEN[@]} -gt 0 ]]; then
|
|
119
|
+
echo "ERROR: $FILE — forbidden ephemeral fields in project-doc frontmatter (WS-8): ${FORBIDDEN[*]}" >&2
|
|
120
|
+
echo " These fields must live in draft/metadata.json, not in per-file YAML frontmatter." >&2
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
exit 0
|