@drafthq/draft 3.5.3 → 3.7.1
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +28 -49
- package/bin/README.md +47 -1
- package/cli/src/hosts/cursor.js +3 -0
- package/cli/src/installer.js +8 -4
- package/cli/src/lib/cursor-registry.js +10 -0
- package/cli/src/lib/fsx.js +3 -0
- package/cli/src/lib/marker.js +8 -3
- package/core/agents/architect.md +21 -9
- package/core/agents/debugger.md +5 -0
- package/core/agents/ops.md +7 -2
- package/core/agents/planner.md +7 -0
- package/core/agents/rca.md +14 -3
- package/core/agents/reviewer.md +1 -0
- package/core/agents/writer.md +6 -0
- package/core/knowledge-base.md +22 -3
- package/core/methodology.md +30 -5
- package/core/shared/condensation.md +28 -18
- package/core/shared/context-verify.md +3 -1
- package/core/shared/cross-skill-dispatch.md +7 -3
- package/core/shared/draft-context-loading.md +3 -0
- package/core/shared/git-report-metadata.md +2 -1
- package/core/shared/graph-query.md +24 -26
- package/core/shared/graph-usage-report.md +1 -1
- package/core/shared/jira-sync.md +10 -6
- package/core/shared/okf-retrieval.md +1 -1
- package/core/shared/parallel-analysis.md +14 -7
- package/core/shared/pattern-learning.md +2 -0
- package/core/shared/template-contract.md +1 -1
- package/core/shared/tool-resolver.md +20 -15
- package/core/shared/vcs-commands.md +5 -2
- package/core/templates/ai-context.md +3 -2
- package/core/templates/ai-profile.md +7 -0
- package/core/templates/architecture.md +6 -2
- package/core/templates/discovery.md +1 -1
- package/core/templates/guardrails.md +9 -0
- package/core/templates/hld.md +5 -4
- package/core/templates/intake-questions.md +45 -0
- package/core/templates/jira.md +11 -2
- package/core/templates/lld.md +8 -5
- package/core/templates/okf/concept.md +0 -1
- package/core/templates/plan.md +1 -1
- package/core/templates/product.md +11 -2
- package/core/templates/rca.md +6 -0
- package/core/templates/service-index.md +1 -0
- package/core/templates/spec.md +15 -0
- package/core/templates/tech-stack.md +12 -0
- package/core/templates/workflow.md +20 -0
- package/integrations/agents/AGENTS.md +1449 -817
- package/integrations/copilot/.github/copilot-instructions.md +1449 -817
- package/package.json +1 -1
- package/scripts/fetch-memory-engine.sh +25 -7
- package/scripts/lib.sh +90 -6
- package/scripts/tools/_graph_queries.sh +22 -8
- package/scripts/tools/_lib.sh +81 -11
- package/scripts/tools/adr-index.sh +1 -1
- package/scripts/tools/check-repo-size.sh +125 -0
- package/scripts/tools/check-skill-line-caps.sh +3 -4
- package/scripts/tools/check-template-noop.sh +0 -1
- package/scripts/tools/check-track-hygiene.sh +41 -21
- package/scripts/tools/classify-files.sh +3 -3
- package/scripts/tools/cycle-detect.sh +18 -17
- package/scripts/tools/detect-test-framework.sh +1 -1
- package/scripts/tools/emit-skill-metrics.sh +7 -2
- package/scripts/tools/fix-whitespace.sh +3 -0
- package/scripts/tools/freshness-check.sh +2 -2
- package/scripts/tools/git-metadata.sh +12 -6
- package/scripts/tools/graph-arch.sh +5 -11
- package/scripts/tools/graph-callers.sh +10 -15
- package/scripts/tools/graph-deps.sh +3 -10
- package/scripts/tools/graph-errors.sh +4 -11
- package/scripts/tools/graph-hierarchy.sh +4 -11
- package/scripts/tools/graph-impact.sh +21 -19
- package/scripts/tools/graph-init.sh +13 -9
- package/scripts/tools/graph-preflight.sh +4 -1
- package/scripts/tools/graph-query.sh +74 -18
- package/scripts/tools/graph-risk.sh +3 -10
- package/scripts/tools/graph-search.sh +4 -11
- package/scripts/tools/graph-snapshot.sh +38 -9
- package/scripts/tools/graph-snippet.sh +3 -10
- package/scripts/tools/graph-tests.sh +3 -10
- package/scripts/tools/graph-traces.sh +3 -10
- package/scripts/tools/hotspot-rank.sh +6 -12
- package/scripts/tools/install-smoke-test.sh +223 -0
- package/scripts/tools/mermaid-from-graph.sh +5 -11
- package/scripts/tools/migrate-track-frontmatter.sh +11 -10
- package/scripts/tools/okf-coverage-check.sh +8 -7
- package/scripts/tools/okf-emit-catalog.sh +256 -0
- package/scripts/tools/okf-fix-links.sh +251 -0
- package/scripts/tools/okf-plan-concepts.sh +242 -14
- package/scripts/tools/okf-render-views.sh +63 -21
- package/scripts/tools/okf-validate-all.sh +20 -9
- package/scripts/tools/okf-validate-quality.sh +15 -10
- package/scripts/tools/okf-validate.sh +20 -6
- package/scripts/tools/parse-git-log.sh +23 -5
- package/scripts/tools/parse-reports.sh +1 -1
- package/scripts/tools/render-track.sh +1 -1
- package/scripts/tools/resolve-tools.sh +24 -8
- package/scripts/tools/run-coverage.sh +1 -1
- package/scripts/tools/scan-markers.sh +4 -4
- package/scripts/tools/validate-frontmatter.sh +2 -2
- package/scripts/tools/verify-citations.sh +3 -4
- package/scripts/tools/verify-doc-anchors.sh +14 -34
- package/scripts/tools/verify-graph-binary.sh +15 -5
- package/skills/GRAPH.md +21 -10
- package/skills/adr/SKILL.md +25 -7
- package/skills/assist-review/SKILL.md +2 -1
- package/skills/bughunt/SKILL.md +55 -12
- package/skills/bughunt/references/regression-tests.md +14 -4
- package/skills/change/SKILL.md +19 -9
- package/skills/coverage/SKILL.md +28 -12
- package/skills/debug/SKILL.md +13 -3
- package/skills/decompose/SKILL.md +42 -12
- package/skills/deep-review/SKILL.md +19 -6
- package/skills/deploy-checklist/SKILL.md +10 -3
- package/skills/documentation/SKILL.md +12 -1
- package/skills/draft/SKILL.md +40 -29
- package/skills/draft/context-files.md +8 -1
- package/skills/draft/quality-guide.md +10 -2
- package/skills/graph/SKILL.md +2 -2
- package/skills/impact/SKILL.md +4 -2
- package/skills/implement/SKILL.md +62 -20
- package/skills/incident-response/SKILL.md +5 -2
- package/skills/init/SKILL.md +112 -30
- package/skills/init/references/architecture-spec.md +32 -8
- package/skills/init/references/okf-emitter.md +42 -78
- package/skills/jira/SKILL.md +33 -13
- package/skills/jira/references/review.md +61 -19
- package/skills/learn/SKILL.md +30 -7
- package/skills/new-track/SKILL.md +73 -18
- package/skills/quick-review/SKILL.md +8 -3
- package/skills/revert/SKILL.md +11 -5
- package/skills/review/SKILL.md +126 -27
- package/skills/review/references/zero-setup-mode.md +117 -0
- package/skills/standup/SKILL.md +10 -2
- package/skills/status/SKILL.md +7 -7
- package/skills/tech-debt/SKILL.md +9 -3
- package/skills/testing-strategy/SKILL.md +10 -2
- package/skills/tour/SKILL.md +2 -1
- package/skills/upload/SKILL.md +4 -3
- package/core/templates/track-architecture.md +0 -311
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# which scripts/tools/_lib.sh:find_memory_bin resolves.
|
|
9
9
|
#
|
|
10
10
|
# Pinned by default for reproducibility; override with CMM_VERSION (a tag, e.g.
|
|
11
|
-
# "v0.
|
|
11
|
+
# "v0.9.0", or "latest").
|
|
12
12
|
#
|
|
13
13
|
# Usage:
|
|
14
14
|
# scripts/fetch-memory-engine.sh [--dest DIR] [--force]
|
|
@@ -16,17 +16,23 @@
|
|
|
16
16
|
# Env:
|
|
17
17
|
# CMM_VERSION Release tag to fetch (default: pinned DEFAULT_VERSION).
|
|
18
18
|
# CMM_DOWNLOAD_URL Override the release base URL (testing).
|
|
19
|
+
# DRAFT_STRICT_VERIFY 1 = refuse to install when the checksum cannot be
|
|
20
|
+
# verified (missing checksums.txt or unlisted archive).
|
|
21
|
+
# Default is warn-and-continue, which keeps installs working
|
|
22
|
+
# on releases that ship no checksums file — acceptable for
|
|
23
|
+
# individuals, not for anyone who must attest to what runs
|
|
24
|
+
# on their machine.
|
|
19
25
|
#
|
|
20
26
|
# Exit codes: 0 installed/already-present, 1 invocation error, 2 fetch/verify failure.
|
|
21
27
|
set -euo pipefail
|
|
22
28
|
|
|
23
29
|
REPO="DeusData/codebase-memory-mcp"
|
|
24
|
-
DEFAULT_VERSION="v0.
|
|
30
|
+
DEFAULT_VERSION="v0.9.0" # pinned; bump deliberately. NOTE: tag must carry the leading "v" AND have published assets (0.7.0 had none → 404).
|
|
25
31
|
VERSION="${CMM_VERSION:-$DEFAULT_VERSION}"
|
|
26
32
|
DEST="$HOME/.cache/draft/bin"
|
|
27
33
|
FORCE=0
|
|
28
34
|
|
|
29
|
-
usage() { sed -n '2,
|
|
35
|
+
usage() { sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; }
|
|
30
36
|
|
|
31
37
|
while [[ $# -gt 0 ]]; do
|
|
32
38
|
case "$1" in
|
|
@@ -79,7 +85,19 @@ if ! curl -fSL --max-time 300 -o "$TMP/$ARCHIVE" "$BASE/$ARCHIVE"; then
|
|
|
79
85
|
exit 2
|
|
80
86
|
fi
|
|
81
87
|
|
|
82
|
-
# --- Verify checksum
|
|
88
|
+
# --- Verify checksum ---
|
|
89
|
+
# A mismatch is always fatal. An *absent* checksum is fatal only under
|
|
90
|
+
# DRAFT_STRICT_VERIFY=1 — otherwise it warns, so that a release without a
|
|
91
|
+
# checksums.txt does not brick the install for everyone.
|
|
92
|
+
STRICT="${DRAFT_STRICT_VERIFY:-0}"
|
|
93
|
+
unverified() {
|
|
94
|
+
if [[ "$STRICT" == "1" ]]; then
|
|
95
|
+
echo "error: $1 (DRAFT_STRICT_VERIFY=1 refuses unverified binaries)" >&2
|
|
96
|
+
exit 2
|
|
97
|
+
fi
|
|
98
|
+
echo " warning: $1 — skipping verification (set DRAFT_STRICT_VERIFY=1 to make this fatal)" >&2
|
|
99
|
+
}
|
|
100
|
+
|
|
83
101
|
if curl -fsSL --max-time 60 -o "$TMP/checksums.txt" "$BASE/checksums.txt" 2>/dev/null; then
|
|
84
102
|
expected="$(grep " $ARCHIVE\$" "$TMP/checksums.txt" 2>/dev/null | awk '{print $1}' | head -1 || true)"
|
|
85
103
|
if [[ -n "$expected" ]]; then
|
|
@@ -92,12 +110,12 @@ if curl -fsSL --max-time 60 -o "$TMP/checksums.txt" "$BASE/checksums.txt" 2>/dev
|
|
|
92
110
|
echo "error: checksum mismatch for $ARCHIVE (expected $expected, got $actual)" >&2
|
|
93
111
|
exit 2
|
|
94
112
|
fi
|
|
95
|
-
echo " checksum OK"
|
|
113
|
+
echo " checksum OK ($expected)"
|
|
96
114
|
else
|
|
97
|
-
|
|
115
|
+
unverified "$ARCHIVE not listed in checksums.txt"
|
|
98
116
|
fi
|
|
99
117
|
else
|
|
100
|
-
|
|
118
|
+
unverified "checksums.txt unavailable at $BASE"
|
|
101
119
|
fi
|
|
102
120
|
|
|
103
121
|
# --- Extract and install ---
|
package/scripts/lib.sh
CHANGED
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Shared validation library for Draft skill files.
|
|
4
4
|
#
|
|
5
|
-
# Sourced by test suites. Defines constants and validation
|
|
6
|
-
#
|
|
5
|
+
# Sourced by test suites and the build script. Defines constants and validation
|
|
6
|
+
# functions only — no side effects at source time, shell options included. Every
|
|
7
|
+
# consumer sets its own `set -euo pipefail`; doing it here would silently change
|
|
8
|
+
# the behaviour of whatever sourced us.
|
|
7
9
|
#
|
|
8
10
|
# Usage:
|
|
9
11
|
# source scripts/lib.sh
|
|
10
12
|
#
|
|
11
13
|
|
|
12
|
-
set -euo pipefail
|
|
13
|
-
|
|
14
14
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
15
|
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
16
16
|
SKILLS_DIR="$ROOT_DIR/skills"
|
|
17
17
|
CORE_DIR="$ROOT_DIR/core"
|
|
18
18
|
TOOLS_DIR="$ROOT_DIR/scripts/tools"
|
|
19
19
|
|
|
20
|
+
# Shared shell helpers (json_escape, apply_dest_mode, …). Side-effect free at
|
|
21
|
+
# source time, so this is safe for every consumer of lib.sh.
|
|
22
|
+
# shellcheck source=tools/_lib.sh
|
|
23
|
+
source "$TOOLS_DIR/_lib.sh"
|
|
24
|
+
|
|
20
25
|
# ─────────────────────────────────────────────────────────
|
|
21
26
|
# Skill ordering (canonical order for all references)
|
|
22
27
|
# ─────────────────────────────────────────────────────────
|
|
@@ -57,6 +62,76 @@ SKILL_ORDER=(
|
|
|
57
62
|
assist-review
|
|
58
63
|
)
|
|
59
64
|
|
|
65
|
+
# ─────────────────────────────────────────────────────────
|
|
66
|
+
# Skill metadata: one row per skill — "<name>|<display header>|<copilot trigger>"
|
|
67
|
+
# The name and header never contain "|"; the trigger may (e.g. "[file|pr N]"),
|
|
68
|
+
# so lookups split on the FIRST TWO pipes only. Coverage against SKILL_ORDER is
|
|
69
|
+
# enforced by tests/test-trigger-functions.sh.
|
|
70
|
+
# ─────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
SKILL_META=(
|
|
73
|
+
'draft|Draft Overview|"help" or "draft"'
|
|
74
|
+
'init|Init Command|"init draft", "build the code graph", or "draft init [refresh] [--graph-only] [--module-only]"'
|
|
75
|
+
'graph|Graph Command|"build graph", "refresh graph", or "draft graph [path]"'
|
|
76
|
+
'new-track|New Track Command|"new feature" or "draft new-track <description>"'
|
|
77
|
+
'decompose|Decompose Command|"break into modules" or "draft decompose"'
|
|
78
|
+
'implement|Implement Command|"implement" or "draft implement"'
|
|
79
|
+
'coverage|Coverage Command|"check coverage" or "draft coverage"'
|
|
80
|
+
'deploy-checklist|Deploy Checklist Command|"deploy checklist" or "draft deploy-checklist [track <id>]"'
|
|
81
|
+
'bughunt|Bug Hunt Command|"hunt bugs" or "draft bughunt [--track <id>]"'
|
|
82
|
+
'review|Review Command|"review code" or "draft review [--track <id>] [--full]"'
|
|
83
|
+
'upload|Upload Command|"upload for review" or "draft upload [track <id>]"'
|
|
84
|
+
'plan|Plan Router|"plan feature" or "draft plan <intent>" (new-track, decompose, adr, tech-debt, change)'
|
|
85
|
+
'ops|Ops Router|"ops deploy" or "draft ops <intent>" (deploy-checklist, incident, standup, status, revert)'
|
|
86
|
+
'docs|Docs Router|"write docs" or "draft docs <intent>" (documentation)'
|
|
87
|
+
'discover|Discover Router|"discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, index, etc.)'
|
|
88
|
+
'jira|Jira Router|"jira preview", "jira create", or "jira review <ID>"'
|
|
89
|
+
'integrations|Integrations Router|"integrations", "integrations jira-preview", or "integrations jira-create"'
|
|
90
|
+
'quick-review|Quick Review Command|"quick review" or "draft quick-review [file|pr <number>]"'
|
|
91
|
+
'deep-review|Deep Review Command|"deep review" or "draft deep-review [module]"'
|
|
92
|
+
'testing-strategy|Testing Strategy Command|"test strategy" or "draft testing-strategy [track <id>|path]"'
|
|
93
|
+
'learn|Learn Command|"learn patterns" or "draft learn [promote|migrate|path]"'
|
|
94
|
+
'adr|ADR Command|"document decision" or "draft adr [title]"'
|
|
95
|
+
'debug|Debug Command|"debug bug" or "draft debug [description|track <id>]"'
|
|
96
|
+
'standup|Standup Command|"standup" or "draft standup [date|week|save]"'
|
|
97
|
+
'tech-debt|Tech Debt Command|"tech debt" or "draft tech-debt [path|track <id>]"'
|
|
98
|
+
'incident-response|Incident Response Command|"incident" or "draft incident-response [new|update|postmortem]"'
|
|
99
|
+
'documentation|Documentation Command|"write docs" or "draft documentation [readme|runbook|api|onboarding]"'
|
|
100
|
+
'status|Status Command|"status" or "draft status"'
|
|
101
|
+
'revert|Revert Command|"revert" or "draft revert"'
|
|
102
|
+
'change|Change Command|"handle change" or "draft change <description>"'
|
|
103
|
+
'tour|Tour Command|"tour" or "draft tour"'
|
|
104
|
+
'impact|Impact Command|"impact" or "draft impact"'
|
|
105
|
+
'assist-review|Assist Review Command|"assist review" or "draft assist-review"'
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# get_skill_header <skill> — display header for integration sections.
|
|
109
|
+
get_skill_header() {
|
|
110
|
+
local skill="$1" row rest
|
|
111
|
+
for row in "${SKILL_META[@]}"; do
|
|
112
|
+
if [[ "${row%%|*}" == "$skill" ]]; then
|
|
113
|
+
rest="${row#*|}"
|
|
114
|
+
printf '%s\n' "${rest%%|*}"
|
|
115
|
+
return 0
|
|
116
|
+
fi
|
|
117
|
+
done
|
|
118
|
+
printf '%s\n' "$(echo "${skill:0:1}" | tr '[:lower:]' '[:upper:]')${skill:1} Command"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# get_copilot_trigger <skill> — natural-language trigger for the Copilot header.
|
|
122
|
+
get_copilot_trigger() {
|
|
123
|
+
local skill="$1" row rest
|
|
124
|
+
for row in "${SKILL_META[@]}"; do
|
|
125
|
+
if [[ "${row%%|*}" == "$skill" ]]; then
|
|
126
|
+
rest="${row#*|}"
|
|
127
|
+
printf '%s\n' "${rest#*|}"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
done
|
|
131
|
+
printf '"draft %s"\n' "$skill"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
60
135
|
# ─────────────────────────────────────────────────────────
|
|
61
136
|
# Core reference files (inlined by Claude plugin at runtime)
|
|
62
137
|
# ─────────────────────────────────────────────────────────
|
|
@@ -91,7 +166,6 @@ CORE_FILES=(
|
|
|
91
166
|
"templates/ai-context.md"
|
|
92
167
|
"templates/ai-profile.md"
|
|
93
168
|
"templates/architecture.md"
|
|
94
|
-
"templates/track-architecture.md"
|
|
95
169
|
"templates/jira.md"
|
|
96
170
|
"templates/product.md"
|
|
97
171
|
"templates/tech-stack.md"
|
|
@@ -181,6 +255,7 @@ TOOLS=(
|
|
|
181
255
|
"validate-frontmatter.sh"
|
|
182
256
|
# Foundations hygiene/verification tools
|
|
183
257
|
"check-graph-usage-report.sh"
|
|
258
|
+
"check-repo-size.sh"
|
|
184
259
|
"check-scope-conflicts.sh"
|
|
185
260
|
"check-skill-line-caps.sh"
|
|
186
261
|
"check-template-noop.sh"
|
|
@@ -188,6 +263,7 @@ TOOLS=(
|
|
|
188
263
|
"diff-templates-vs-tracks.sh"
|
|
189
264
|
"emit-skill-metrics.sh"
|
|
190
265
|
"fix-whitespace.sh"
|
|
266
|
+
"install-smoke-test.sh"
|
|
191
267
|
"migrate-track-frontmatter.sh"
|
|
192
268
|
"render-track.sh"
|
|
193
269
|
"verify-citations.sh"
|
|
@@ -202,6 +278,8 @@ TOOLS=(
|
|
|
202
278
|
"okf-validate-quality.sh"
|
|
203
279
|
"okf-coverage-check.sh"
|
|
204
280
|
"okf-validate-all.sh"
|
|
281
|
+
"okf-emit-catalog.sh"
|
|
282
|
+
"okf-fix-links.sh"
|
|
205
283
|
)
|
|
206
284
|
|
|
207
285
|
# ─────────────────────────────────────────────────────────
|
|
@@ -264,12 +342,18 @@ extract_body() {
|
|
|
264
342
|
}
|
|
265
343
|
|
|
266
344
|
# Validate body format: line 1 blank, line 2 starts with #, line 3 blank.
|
|
345
|
+
# $3 (optional): pre-extracted body — skips a second extract_body parse when
|
|
346
|
+
# the caller already has it.
|
|
267
347
|
validate_skill_body_format() {
|
|
268
348
|
local skill="$1"
|
|
269
349
|
local skill_file="$2"
|
|
270
350
|
|
|
271
351
|
local body_head line1 line2 line3
|
|
272
|
-
|
|
352
|
+
if (( $# >= 3 )); then
|
|
353
|
+
body_head=$(printf '%s\n' "$3" | sed -n '1,3p')
|
|
354
|
+
else
|
|
355
|
+
body_head=$(extract_body "$skill_file" | sed -n '1,3p' || true)
|
|
356
|
+
fi
|
|
273
357
|
line1=$(echo "$body_head" | sed -n '1p')
|
|
274
358
|
line2=$(echo "$body_head" | sed -n '2p')
|
|
275
359
|
line3=$(echo "$body_head" | sed -n '3p')
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
# `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type
|
|
13
13
|
# alternation `[:A|B]`, simple `count(x)`.
|
|
14
14
|
# UNSAFE : coalesce(), `<>` / `!=` / `<=` / `>=`, `NOT EXISTS(...)`,
|
|
15
|
-
# `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern joins
|
|
15
|
+
# `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern joins,
|
|
16
|
+
# and comparing one property against another (`a.x < b.x` — the
|
|
17
|
+
# parser wants a literal on the right and fails with "expected value
|
|
18
|
+
# at pos N"). `<` against a literal is fine.
|
|
16
19
|
# Every builder below stays inside the SAFE set.
|
|
17
20
|
#
|
|
18
21
|
# Label-agnostic on name matches: code units are :Method ⪢ :Function in OO repos;
|
|
@@ -28,9 +31,12 @@ if [[ "$(type -t memory_cli 2>/dev/null)" != function ]]; then
|
|
|
28
31
|
source "$_GQ_DIR/_lib.sh"
|
|
29
32
|
fi
|
|
30
33
|
|
|
31
|
-
# Escape
|
|
34
|
+
# Escape for embedding inside a Cypher single-quoted string literal.
|
|
35
|
+
# Backslashes must be doubled BEFORE quotes are escaped, otherwise an input
|
|
36
|
+
# ending in `\` yields `\'` — an escaped quote that never closes the literal.
|
|
32
37
|
gq_escape() {
|
|
33
38
|
local s="$1"
|
|
39
|
+
s="${s//\\/\\\\}"
|
|
34
40
|
printf '%s' "${s//\'/\\\'}"
|
|
35
41
|
}
|
|
36
42
|
|
|
@@ -40,7 +46,12 @@ gq_q_exists() { printf "MATCH (f {name:'%s'}) RETURN f.name AS name L
|
|
|
40
46
|
gq_q_callers() { printf "MATCH (c)-[:CALLS]->(f {name:'%s'}) RETURN c.name AS caller, c.file_path AS file LIMIT 200" "$1"; }
|
|
41
47
|
gq_q_callers_prod() { printf "MATCH (c)-[:CALLS]->(f {name:'%s'}) WHERE c.is_test=false AND NOT c.file_path STARTS WITH 'tests/' RETURN c.name AS caller, c.file_path AS file LIMIT 200" "$1"; }
|
|
42
48
|
gq_q_callers_qualified() { printf "MATCH (c)-[:CALLS]->(f {qualified_name:'%s'}) RETURN c.name AS caller, c.file_path AS file LIMIT 200" "$1"; }
|
|
43
|
-
|
|
49
|
+
# The `WHERE a.qualified_name < b.qualified_name` this used to carry was doing
|
|
50
|
+
# two jobs — drop self-loops, and emit each mutual pair once instead of twice —
|
|
51
|
+
# but the engine cannot parse a property-to-property comparison, so the query
|
|
52
|
+
# failed on every call and cycle-detect swallowed the error. Both jobs now happen
|
|
53
|
+
# in jq downstream; the limit is doubled because the raw rows come in pairs.
|
|
54
|
+
gq_q_cycles2() { printf "MATCH (a)-[:CALLS]->(b)-[:CALLS]->(a) RETURN a.qualified_name AS a, b.qualified_name AS b LIMIT 200"; }
|
|
44
55
|
gq_q_cycles3() { printf "MATCH (a)-[:CALLS]->(b)-[:CALLS]->(c)-[:CALLS]->(a) RETURN a.qualified_name AS a, b.qualified_name AS b, c.qualified_name AS c LIMIT 100"; }
|
|
45
56
|
gq_q_tests() { printf "MATCH (t)-[:TESTS]->(f {name:'%s'}) RETURN t.qualified_name AS test, t.file_path AS file LIMIT 200" "$1"; }
|
|
46
57
|
gq_q_tested_all() { printf "MATCH (t)-[:TESTS]->(f) RETURN f.qualified_name AS symbol LIMIT 2000"; }
|
|
@@ -50,7 +61,6 @@ gq_q_co_change() { printf "MATCH (a:File)-[r:FILE_CHANGES_WITH]->(b:File
|
|
|
50
61
|
gq_q_inherits() { printf "MATCH (c)-[:INHERITS]->(p) RETURN c.qualified_name AS child, p.qualified_name AS parent LIMIT 500"; }
|
|
51
62
|
gq_q_inherits_sym() { printf "MATCH (c)-[:INHERITS]->(p) WHERE c.name='%s' RETURN c.qualified_name AS child, p.qualified_name AS parent LIMIT 200" "$1"; }
|
|
52
63
|
gq_q_derived_sym() { printf "MATCH (c)-[:INHERITS]->(p) WHERE p.name='%s' RETURN c.qualified_name AS child, p.qualified_name AS parent LIMIT 200" "$1"; }
|
|
53
|
-
gq_q_writes() { printf "MATCH (f {name:'%s'})-[:WRITES]->(v) RETURN v.name AS target, v.file_path AS file LIMIT 200" "$1"; }
|
|
54
64
|
gq_q_raises() { printf "MATCH (f {name:'%s'})-[:RAISES|THROWS]->(e) RETURN e.name AS error, e.qualified_name AS qualified LIMIT 200" "$1"; }
|
|
55
65
|
gq_q_raisers() { printf "MATCH (f)-[:RAISES|THROWS]->(e {name:'%s'}) RETURN f.qualified_name AS raiser, f.file_path AS file LIMIT 200" "$1"; }
|
|
56
66
|
gq_q_node_props() { printf "MATCH (f) RETURN f.qualified_name AS q, f.complexity AS c, f.cognitive AS cog, f.is_entry_point AS ep LIMIT 10000"; }
|
|
@@ -82,19 +92,23 @@ gq_rows_len() {
|
|
|
82
92
|
printf '%s' "$1" | jq -r '(.rows // []) | length' 2>/dev/null || printf '0'
|
|
83
93
|
}
|
|
84
94
|
|
|
85
|
-
# gq_symbol_status <project> <sym_esc> <result_json>
|
|
95
|
+
# gq_symbol_status <project> <sym_esc> <result_json>
|
|
96
|
+
# -> ok | no-edges | no-match | probe-failed
|
|
86
97
|
# Fail-loud disambiguation (Guardrail 4): an empty result is only a true negative
|
|
87
98
|
# ("no-edges") when the node actually exists; otherwise it is "no-match". An
|
|
88
99
|
# existence probe runs only when the primary result is empty (hot path stays one
|
|
89
|
-
# query).
|
|
100
|
+
# query). If the probe itself errors, that is NOT a true negative — report
|
|
101
|
+
# "probe-failed" so callers never mistake an engine failure for absence.
|
|
90
102
|
gq_symbol_status() {
|
|
91
103
|
local project="$1" sym="$2" result="$3"
|
|
92
104
|
if [[ "$(gq_rows_len "$result")" -gt 0 ]]; then
|
|
93
105
|
printf 'ok'; return
|
|
94
106
|
fi
|
|
95
107
|
local ex
|
|
96
|
-
ex="$(gq_run "$project" "$(gq_q_exists "$sym")"
|
|
97
|
-
|
|
108
|
+
if ! ex="$(gq_run "$project" "$(gq_q_exists "$sym")")"; then
|
|
109
|
+
printf 'probe-failed'; return
|
|
110
|
+
fi
|
|
111
|
+
if [[ "$(gq_rows_len "$ex")" -gt 0 ]]; then
|
|
98
112
|
printf 'no-edges'
|
|
99
113
|
else
|
|
100
114
|
printf 'no-match'
|
package/scripts/tools/_lib.sh
CHANGED
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
# shellcheck shell=bash
|
|
7
7
|
|
|
8
|
+
# Give a mktemp-created replacement file the mode its destination should carry.
|
|
9
|
+
# mktemp creates 0600 and `mv` swaps the inode, so an atomic rewrite silently
|
|
10
|
+
# strips the destination's permissions (a 0644 doc came back 0600). Call this on
|
|
11
|
+
# the temp file BEFORE the mv. Uses the destination's current mode when it
|
|
12
|
+
# exists, else the mode a plain `>` redirect would have produced under the
|
|
13
|
+
# caller's umask.
|
|
14
|
+
apply_dest_mode() {
|
|
15
|
+
local tmp="$1" dest="${2:-}" mode=""
|
|
16
|
+
if [[ -n "$dest" && -e "$dest" ]]; then
|
|
17
|
+
mode="$(stat -c '%a' "$dest" 2>/dev/null || stat -f '%Lp' "$dest" 2>/dev/null || true)"
|
|
18
|
+
fi
|
|
19
|
+
[[ -n "$mode" ]] || mode="$(printf '%o' "$(( 0666 & ~0$(umask) ))")"
|
|
20
|
+
chmod "$mode" "$tmp" 2>/dev/null || true
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
json_escape() {
|
|
9
24
|
local s="$1"
|
|
10
25
|
s="${s//\\/\\\\}"
|
|
@@ -15,7 +30,6 @@ json_escape() {
|
|
|
15
30
|
printf '%s' "$s"
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
# Extract a top-level YAML frontmatter field value from a Markdown file.
|
|
19
33
|
# Discover track directories under a repo root (default: caller's Draft repo).
|
|
20
34
|
discover_track_dirs() {
|
|
21
35
|
local repo_root="${1:-}"
|
|
@@ -90,6 +104,7 @@ skill_line_cap() {
|
|
|
90
104
|
printf '%s' "$global_cap"
|
|
91
105
|
}
|
|
92
106
|
|
|
107
|
+
# Extract a top-level YAML frontmatter field value from a Markdown file.
|
|
93
108
|
get_yaml_field() {
|
|
94
109
|
local file="$1"
|
|
95
110
|
local key="$2"
|
|
@@ -109,9 +124,53 @@ get_yaml_field() {
|
|
|
109
124
|
' "$file"
|
|
110
125
|
}
|
|
111
126
|
|
|
127
|
+
# GitHub-flavored-markdown-ish heading id (shared by TOC, anchors, link fixers).
|
|
128
|
+
# Keeps underscores; strips other punctuation; collapses whitespace to '-'.
|
|
129
|
+
gfm_slug() {
|
|
130
|
+
local s="${1:-}"
|
|
131
|
+
# shellcheck disable=SC2001
|
|
132
|
+
s="$(printf '%s' "$s" | tr '[:upper:]' '[:lower:]')"
|
|
133
|
+
s="$(printf '%s' "$s" | sed -E 's/[^[:alnum:]_[:space:]-]+//g; s/[[:space:]]+/-/g; s/-+/-/g; s/^-+//; s/-+$//')"
|
|
134
|
+
printf '%s' "$s"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Count entries in an x-grounded-paths frontmatter field. Always prints an
|
|
138
|
+
# integer — a page with no such field counts 0, not "" (callers embed the value
|
|
139
|
+
# in diagnostics, and an empty string rendered as "x-grounded-paths < 2").
|
|
140
|
+
# Supports both inline (`x-grounded-paths: ["a", "b"]`) and block lists:
|
|
141
|
+
# x-grounded-paths:
|
|
142
|
+
# - "a"
|
|
143
|
+
# - "b"
|
|
144
|
+
grounded_paths_count() {
|
|
145
|
+
local file="$1"
|
|
146
|
+
[[ -f "$file" ]] || { echo 0; return; }
|
|
147
|
+
awk '
|
|
148
|
+
NR==1 && /^---$/ { fm=1; next }
|
|
149
|
+
fm && /^---$/ { exit }
|
|
150
|
+
fm && /^x-grounded-paths:[[:space:]]*\[/ {
|
|
151
|
+
line=$0
|
|
152
|
+
sub(/^x-grounded-paths:[[:space:]]*\[/, "", line)
|
|
153
|
+
sub(/\].*$/, "", line)
|
|
154
|
+
gsub(/[[:space:]]/, "", line)
|
|
155
|
+
if (line=="") { print 0; printed=1; exit }
|
|
156
|
+
n=split(line, a, ",")
|
|
157
|
+
print n
|
|
158
|
+
printed=1
|
|
159
|
+
exit
|
|
160
|
+
}
|
|
161
|
+
fm && /^x-grounded-paths:[[:space:]]*$/ { collect=1; next }
|
|
162
|
+
fm && collect {
|
|
163
|
+
if ($0 ~ /^[[:space:]]*-[[:space:]]*/) { n++; next }
|
|
164
|
+
if ($0 ~ /^[A-Za-z0-9_-]+:/) { print n+0; printed=1; exit }
|
|
165
|
+
}
|
|
166
|
+
END { if (!printed) print n+0 }
|
|
167
|
+
' "$file"
|
|
168
|
+
}
|
|
169
|
+
|
|
112
170
|
# Locate the `codebase-memory-mcp` binary (Draft knowledge-graph engine).
|
|
113
171
|
# Sets MEMORY_BIN globally; returns 0 if found, 1 otherwise.
|
|
114
|
-
#
|
|
172
|
+
# Precedence: DRAFT_MEMORY_DISABLE (hard off) > DRAFT_MEMORY_BIN > PATH >
|
|
173
|
+
# Draft-managed install (~/.cache/draft/bin) > vendored bin/<arch> under known roots.
|
|
115
174
|
# No legacy fallbacks: the Aether `graph`/`graph-clang` binaries are retired.
|
|
116
175
|
find_memory_bin() {
|
|
117
176
|
local repo_abs="$1"
|
|
@@ -157,14 +216,6 @@ find_memory_bin() {
|
|
|
157
216
|
local roots=()
|
|
158
217
|
[[ -n "$repo_abs" && -d "$repo_abs" ]] && roots+=("$repo_abs")
|
|
159
218
|
[[ -n "$self_repo" && -d "$self_repo" ]] && roots+=("$self_repo")
|
|
160
|
-
for bc in \
|
|
161
|
-
"$HOME/.cursor/plugins/local/draft/.draft-install-path" \
|
|
162
|
-
"$HOME/.claude/plugins/draft/.draft-install-path"; do
|
|
163
|
-
if [[ -f "$bc" ]]; then
|
|
164
|
-
local pr; pr="$(cat "$bc" 2>/dev/null || true)"
|
|
165
|
-
[[ -n "$pr" && -d "$pr" ]] && roots+=("$pr")
|
|
166
|
-
fi
|
|
167
|
-
done
|
|
168
219
|
|
|
169
220
|
for pr in "${roots[@]}"; do
|
|
170
221
|
local cand="$pr/bin/$ARCH/$bin_name"
|
|
@@ -177,6 +228,21 @@ find_memory_bin() {
|
|
|
177
228
|
return 1
|
|
178
229
|
}
|
|
179
230
|
|
|
231
|
+
# graph_bootstrap <repo-dir>
|
|
232
|
+
# Shared engine bootstrap for the graph-*.sh wrappers: resolves the repo path,
|
|
233
|
+
# locates the engine binary, checks jq, and ensures the index. Sets the globals
|
|
234
|
+
# REPO_ABS and PROJECT. Returns 1 on any failure so each wrapper routes it to
|
|
235
|
+
# its own unavailable-JSON shape (the shapes differ per tool).
|
|
236
|
+
graph_bootstrap() {
|
|
237
|
+
local repo="$1" self_repo
|
|
238
|
+
REPO_ABS="$(cd "$repo" 2>/dev/null && pwd)" || return 1
|
|
239
|
+
self_repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
240
|
+
find_memory_bin "$REPO_ABS" "$self_repo" || return 1
|
|
241
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
242
|
+
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
243
|
+
[[ -n "$PROJECT" ]] || return 1
|
|
244
|
+
}
|
|
245
|
+
|
|
180
246
|
# Run a codebase-memory-mcp CLI tool. Echoes the JSON result (stdout); the engine's
|
|
181
247
|
# `level=...` log lines go to stderr and are discarded unless DRAFT_MEMORY_DEBUG is set.
|
|
182
248
|
# Usage: memory_cli <tool> [json-args]
|
|
@@ -250,7 +316,11 @@ _can_cgroup_bound() {
|
|
|
250
316
|
# Echoes the engine's JSON result on stdout (same contract as memory_cli).
|
|
251
317
|
memory_index_bounded() {
|
|
252
318
|
local repo_abs="$1"
|
|
253
|
-
|
|
319
|
+
# Payload built with jq (never string concatenation) so a repo path
|
|
320
|
+
# containing a `"` or `\` can never corrupt the JSON sent to the engine.
|
|
321
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
322
|
+
local json
|
|
323
|
+
json="$(jq -n --arg r "$repo_abs" '{repo_path:$r}')" || return 1
|
|
254
324
|
export CBM_WORKERS="${CBM_WORKERS:-4}"
|
|
255
325
|
local total pct
|
|
256
326
|
total="$(_total_ram_mb)"
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-repo-size.sh
|
|
3
|
+
#
|
|
4
|
+
# Guards the size of the tree at HEAD.
|
|
5
|
+
#
|
|
6
|
+
# `claude plugin marketplace add <owner>/<repo>` git-clones this repository, so
|
|
7
|
+
# every byte tracked at HEAD is downloaded before the plugin can be installed.
|
|
8
|
+
# In v2.8.3 ~670 MB of audiobook .m4a files in HEAD stalled that first install
|
|
9
|
+
# step with no error message — an install-breaking regression that no test
|
|
10
|
+
# caught. This is that test.
|
|
11
|
+
#
|
|
12
|
+
# The threshold is deliberately loose (default 10 MB against a ~6 MB HEAD): it
|
|
13
|
+
# exists to catch binary assets landing in HEAD, not to police prose growth.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# scripts/tools/check-repo-size.sh # default cap, human output
|
|
17
|
+
# scripts/tools/check-repo-size.sh --max-mb 5 # custom cap
|
|
18
|
+
# scripts/tools/check-repo-size.sh --json # machine-readable
|
|
19
|
+
# scripts/tools/check-repo-size.sh --rev v3.6.0 # audit a past revision
|
|
20
|
+
#
|
|
21
|
+
# Exit codes:
|
|
22
|
+
# 0 tree at HEAD is within the cap
|
|
23
|
+
# 1 cap exceeded
|
|
24
|
+
# 2 usage / runtime error
|
|
25
|
+
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
usage() {
|
|
29
|
+
cat <<'EOF'
|
|
30
|
+
check-repo-size.sh — fail if the tree at HEAD exceeds a size cap
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
--max-mb <n> Cap in megabytes (default: 10)
|
|
34
|
+
--rev <rev> Revision to measure (default: HEAD)
|
|
35
|
+
--repo <path> Repository root (default: this repo)
|
|
36
|
+
--top <n> Largest blobs to list (default: 10)
|
|
37
|
+
--json Emit JSON instead of human-readable output
|
|
38
|
+
--help, -h Show this message
|
|
39
|
+
EOF
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
|
|
43
|
+
usage
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
48
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
49
|
+
|
|
50
|
+
MAX_MB=10
|
|
51
|
+
REV="HEAD"
|
|
52
|
+
TOP=10
|
|
53
|
+
EMIT_JSON=0
|
|
54
|
+
|
|
55
|
+
while [[ $# -gt 0 ]]; do
|
|
56
|
+
case "$1" in
|
|
57
|
+
--max-mb)
|
|
58
|
+
[[ -n "${2:-}" ]] || { echo "check-repo-size: --max-mb requires a value" >&2; exit 2; }
|
|
59
|
+
MAX_MB="$2"; shift 2 ;;
|
|
60
|
+
--rev)
|
|
61
|
+
[[ -n "${2:-}" ]] || { echo "check-repo-size: --rev requires a value" >&2; exit 2; }
|
|
62
|
+
REV="$2"; shift 2 ;;
|
|
63
|
+
--repo)
|
|
64
|
+
[[ -n "${2:-}" ]] || { echo "check-repo-size: --repo requires a value" >&2; exit 2; }
|
|
65
|
+
REPO_ROOT="$2"; shift 2 ;;
|
|
66
|
+
--top)
|
|
67
|
+
[[ -n "${2:-}" ]] || { echo "check-repo-size: --top requires a value" >&2; exit 2; }
|
|
68
|
+
TOP="$2"; shift 2 ;;
|
|
69
|
+
--json) EMIT_JSON=1; shift ;;
|
|
70
|
+
--help|-h) usage; exit 0 ;;
|
|
71
|
+
*) echo "check-repo-size: unknown argument '$1'" >&2; usage >&2; exit 2 ;;
|
|
72
|
+
esac
|
|
73
|
+
done
|
|
74
|
+
|
|
75
|
+
[[ "$MAX_MB" =~ ^[0-9]+$ ]] || { echo "check-repo-size: --max-mb must be an integer, got '$MAX_MB'" >&2; exit 2; }
|
|
76
|
+
[[ "$TOP" =~ ^[0-9]+$ ]] || { echo "check-repo-size: --top must be an integer, got '$TOP'" >&2; exit 2; }
|
|
77
|
+
[[ -d "$REPO_ROOT" ]] || { echo "check-repo-size: no such directory: $REPO_ROOT" >&2; exit 2; }
|
|
78
|
+
|
|
79
|
+
cd "$REPO_ROOT"
|
|
80
|
+
git rev-parse --git-dir >/dev/null 2>&1 || { echo "check-repo-size: $REPO_ROOT is not a git repository" >&2; exit 2; }
|
|
81
|
+
git rev-parse --verify --quiet "$REV" >/dev/null || { echo "check-repo-size: unknown revision '$REV'" >&2; exit 2; }
|
|
82
|
+
|
|
83
|
+
# `ls-tree -l` gives per-blob sizes; symlinks/submodules report '-' and are skipped.
|
|
84
|
+
listing="$(git ls-tree -r -l "$REV")"
|
|
85
|
+
|
|
86
|
+
total_bytes=0
|
|
87
|
+
file_count=0
|
|
88
|
+
while read -r _mode _type _sha size _path; do
|
|
89
|
+
[[ "$size" =~ ^[0-9]+$ ]] || continue
|
|
90
|
+
total_bytes=$((total_bytes + size))
|
|
91
|
+
file_count=$((file_count + 1))
|
|
92
|
+
done <<< "$listing"
|
|
93
|
+
|
|
94
|
+
max_bytes=$((MAX_MB * 1024 * 1024))
|
|
95
|
+
status="ok"
|
|
96
|
+
[[ "$total_bytes" -gt "$max_bytes" ]] && status="over"
|
|
97
|
+
|
|
98
|
+
total_mb="$(awk -v b="$total_bytes" 'BEGIN { printf "%.2f", b / 1048576 }')"
|
|
99
|
+
|
|
100
|
+
if [[ "$EMIT_JSON" -eq 1 ]]; then
|
|
101
|
+
printf '{"rev":"%s","status":"%s","total_bytes":%d,"total_mb":%s,"max_mb":%d,"files":%d}\n' \
|
|
102
|
+
"$REV" "$status" "$total_bytes" "$total_mb" "$MAX_MB" "$file_count"
|
|
103
|
+
else
|
|
104
|
+
echo "Tree at $REV: ${total_mb} MB across $file_count files (cap: ${MAX_MB} MB)"
|
|
105
|
+
if [[ "$status" == "over" ]]; then
|
|
106
|
+
echo ""
|
|
107
|
+
echo "Largest tracked blobs:"
|
|
108
|
+
# Materialize the sorted list before slicing it. Piping straight into
|
|
109
|
+
# `head` lets head close the pipe early, which under `pipefail` surfaces
|
|
110
|
+
# sort's SIGPIPE as a hard failure — non-deterministically, depending on
|
|
111
|
+
# whether the output fit in the pipe buffer.
|
|
112
|
+
largest="$(awk '$4 ~ /^[0-9]+$/ { printf " %8.2f MB %s\n", $4 / 1048576, $5 }' <<< "$listing" | sort -rn)"
|
|
113
|
+
head -n "$TOP" <<< "$largest"
|
|
114
|
+
fi
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ "$status" == "over" ]]; then
|
|
118
|
+
echo "" >&2
|
|
119
|
+
echo "FAIL: HEAD exceeds ${MAX_MB} MB. 'plugin marketplace add' clones this tree —" >&2
|
|
120
|
+
echo " oversized blobs stall install with no error. Ship large assets as" >&2
|
|
121
|
+
echo " GitHub Release attachments instead of committing them." >&2
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
exit 0
|
|
@@ -51,8 +51,8 @@ while (($#)); do
|
|
|
51
51
|
-h|--help) USAGE_HELP_MODE=1 usage ;;
|
|
52
52
|
--enforce) ENFORCE=1; shift ;;
|
|
53
53
|
--json) EMIT_JSON=1; shift ;;
|
|
54
|
-
--caps) CAPS_CONF="$2"; shift 2 ;;
|
|
55
|
-
--skills-dir) SKILLS_DIR="$2"; shift 2 ;;
|
|
54
|
+
--caps) CAPS_CONF="${2:?--caps requires a value}"; shift 2 ;;
|
|
55
|
+
--skills-dir) SKILLS_DIR="${2:?--skills-dir requires a value}"; shift 2 ;;
|
|
56
56
|
-*) printf 'Unknown flag: %s\n' "$1" >&2; usage ;;
|
|
57
57
|
*) printf 'Unexpected arg: %s\n' "$1" >&2; usage ;;
|
|
58
58
|
esac
|
|
@@ -64,7 +64,6 @@ declare -a findings=()
|
|
|
64
64
|
record() { findings+=("$1|$2|$3"); over_count=$((over_count + 1)); }
|
|
65
65
|
|
|
66
66
|
while IFS= read -r path; do
|
|
67
|
-
rel="${path#"$REPO_ROOT/"}"
|
|
68
67
|
name="$(basename "$(dirname "$path")")"
|
|
69
68
|
lines="$(wc -l < "$path" | tr -d ' ')"
|
|
70
69
|
cap="$(skill_line_cap "$name" "$CAPS_CONF" "$GLOBAL_CAP")"
|
|
@@ -90,7 +89,7 @@ emit() {
|
|
|
90
89
|
printf 'OK: all skills within cap (default %d).\n' "$GLOBAL_CAP"
|
|
91
90
|
return
|
|
92
91
|
fi
|
|
93
|
-
local mode="warn-only"
|
|
92
|
+
local mode="warn-only"
|
|
94
93
|
((ENFORCE)) && mode="enforced"
|
|
95
94
|
if ((ENFORCE)); then
|
|
96
95
|
printf 'SKILL-CAPS: %d skill(s) over cap (%s):\n' \
|