@esoteric-logic/praxis-harness 2.5.1 → 2.6.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/base/CLAUDE.md +17 -1
- package/base/configs/mcp-servers.json +26 -0
- package/base/configs/registry.json +113 -0
- package/base/hooks/credential-guard.sh +98 -0
- package/base/hooks/dep-audit.sh +105 -0
- package/base/hooks/quality-check.sh +4 -3
- package/base/hooks/recursion-guard.sh +76 -0
- package/base/hooks/session-data-collect.sh +3 -0
- package/base/hooks/settings-hooks.json +18 -0
- package/base/rules/coding.md +2 -2
- package/base/rules/dependency-freshness.md +50 -0
- package/base/rules/live-docs-required.md +81 -0
- package/base/rules/security-posture.md +62 -0
- package/base/skills/blind-judge/SKILL.md +88 -0
- package/base/skills/context7-lookup/SKILL.md +17 -6
- package/base/skills/deliberate/SKILL.md +103 -0
- package/base/skills/dep-hygiene/SKILL.md +93 -0
- package/base/skills/pre-commit-lint/SKILL.md +2 -3
- package/base/skills/research/SKILL.md +106 -0
- package/base/skills/scaffold-new/SKILL.md +1 -4
- package/base/skills/scaffold-new/references/repo-CLAUDE-md-template.md +1 -1
- package/bin/praxis-preflight.sh +317 -0
- package/package.json +1 -1
- package/scripts/install-tools.sh +3 -23
- package/scripts/lint-harness.sh +1 -1
- package/scripts/test-harness.sh +1 -26
- package/base/configs/vale/.vale.ini +0 -17
- package/base/configs/vale/Praxis/AISlop.yml +0 -90
- package/base/configs/vale/Praxis/CopulaAvoidance.yml +0 -22
- package/base/configs/vale/Praxis/ElegantVariation.yml +0 -14
- package/base/configs/vale/Praxis/HeadingCase.yml +0 -26
- package/base/configs/vale/Praxis/Hedging.yml +0 -22
- package/base/configs/vale/Praxis/NaturalVoice.yml +0 -85
- package/base/configs/vale/Praxis/Precision.yml +0 -60
- package/base/configs/vale/Praxis/SentenceLength.yml +0 -6
- package/base/configs/vale/Praxis/Terminology.yml +0 -25
- package/base/configs/vale/Praxis/vocabularies/accept.txt +0 -32
- package/base/configs/vale/Praxis/vocabularies/reject.txt +0 -3
- package/base/skills/prose-review/SKILL.md +0 -165
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# praxis-preflight.sh — "Ready to build" gate
|
|
3
|
+
# Phase 1 is hard-blocking (exit 1). Phases 2-5 produce warnings only.
|
|
4
|
+
# Also implements: set-key subcommand for secret management.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# ─── Colors ───
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
GREEN='\033[0;32m'
|
|
10
|
+
YELLOW='\033[0;33m'
|
|
11
|
+
CYAN='\033[0;36m'
|
|
12
|
+
BOLD='\033[1m'
|
|
13
|
+
NC='\033[0m'
|
|
14
|
+
|
|
15
|
+
PASS=0
|
|
16
|
+
WARN=0
|
|
17
|
+
BLOCK=0
|
|
18
|
+
PRAXIS_DIR="$HOME/.praxis"
|
|
19
|
+
SECRETS_FILE="$PRAXIS_DIR/secrets"
|
|
20
|
+
REPORT_FILE="$PRAXIS_DIR/preflight-report.json"
|
|
21
|
+
|
|
22
|
+
ok() { echo -e " ${GREEN}✓${NC} $1"; PASS=$((PASS + 1)); }
|
|
23
|
+
warn() { echo -e " ${YELLOW}⚠${NC} $1"; WARN=$((WARN + 1)); }
|
|
24
|
+
fail() { echo -e " ${RED}✗${NC} $1"; BLOCK=$((BLOCK + 1)); }
|
|
25
|
+
step() { echo -e "\n${CYAN}${BOLD}$1${NC}"; }
|
|
26
|
+
|
|
27
|
+
# ═══════════════════════════════════════════
|
|
28
|
+
# Subcommand: set-key
|
|
29
|
+
# ═══════════════════════════════════════════
|
|
30
|
+
if [[ "${1:-}" == "set-key" ]]; then
|
|
31
|
+
KEY="${2:-}"
|
|
32
|
+
VALUE="${3:-}"
|
|
33
|
+
if [[ -z "$KEY" || -z "$VALUE" ]]; then
|
|
34
|
+
echo "Usage: praxis-preflight.sh set-key KEY VALUE"
|
|
35
|
+
echo "Example: praxis-preflight.sh set-key PERPLEXITY_API_KEY pplx-..."
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Create secure directory
|
|
40
|
+
mkdir -p "$PRAXIS_DIR"
|
|
41
|
+
chmod 700 "$PRAXIS_DIR"
|
|
42
|
+
|
|
43
|
+
# Create or update secrets file
|
|
44
|
+
if [[ -f "$SECRETS_FILE" ]]; then
|
|
45
|
+
# Remove existing key line
|
|
46
|
+
grep -v "^${KEY}=" "$SECRETS_FILE" > "${SECRETS_FILE}.tmp" 2>/dev/null || true
|
|
47
|
+
mv "${SECRETS_FILE}.tmp" "$SECRETS_FILE"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "${KEY}=${VALUE}" >> "$SECRETS_FILE"
|
|
51
|
+
chmod 600 "$SECRETS_FILE"
|
|
52
|
+
|
|
53
|
+
MASKED="${VALUE:0:8}********"
|
|
54
|
+
echo -e "${GREEN}✓${NC} Stored $KEY (${MASKED})"
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# ═══════════════════════════════════════════
|
|
59
|
+
# Load secrets from file if it exists
|
|
60
|
+
# ═══════════════════════════════════════════
|
|
61
|
+
load_secrets() {
|
|
62
|
+
if [[ -f "$SECRETS_FILE" ]]; then
|
|
63
|
+
while IFS='=' read -r key value; do
|
|
64
|
+
[[ -z "$key" || "$key" == \#* ]] && continue
|
|
65
|
+
export "$key=$value" 2>/dev/null || true
|
|
66
|
+
done < "$SECRETS_FILE"
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# ═══════════════════════════════════════════
|
|
71
|
+
# Version comparison helper
|
|
72
|
+
# ═══════════════════════════════════════════
|
|
73
|
+
version_gte() {
|
|
74
|
+
# Returns 0 if $1 >= $2
|
|
75
|
+
printf '%s\n%s' "$2" "$1" | sort -V -C 2>/dev/null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
echo ""
|
|
79
|
+
echo -e "${BOLD}╔══════════════════════════════════════════╗${NC}"
|
|
80
|
+
echo -e "${BOLD}║ Praxis Preflight Check ║${NC}"
|
|
81
|
+
echo -e "${BOLD}╚══════════════════════════════════════════╝${NC}"
|
|
82
|
+
|
|
83
|
+
load_secrets
|
|
84
|
+
|
|
85
|
+
# ═══════════════════════════════════════════
|
|
86
|
+
# Phase 1: BLOCKING — hard exit if any fail
|
|
87
|
+
# ═══════════════════════════════════════════
|
|
88
|
+
step "Phase 1: Required (blocking)"
|
|
89
|
+
|
|
90
|
+
# Claude auth
|
|
91
|
+
if claude auth status &>/dev/null 2>&1; then
|
|
92
|
+
ok "Claude Code authenticated"
|
|
93
|
+
else
|
|
94
|
+
fail "Claude Code not authenticated"
|
|
95
|
+
echo -e " Fix: ${CYAN}claude auth login${NC}"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Node.js >= 18
|
|
99
|
+
if command -v node &>/dev/null; then
|
|
100
|
+
NODE_VER=$(node --version 2>/dev/null | sed 's/^v//')
|
|
101
|
+
if version_gte "$NODE_VER" "18.0.0"; then
|
|
102
|
+
ok "Node.js $NODE_VER (>= 18 required)"
|
|
103
|
+
else
|
|
104
|
+
fail "Node.js $NODE_VER is below minimum 18.0.0"
|
|
105
|
+
echo -e " Fix: ${CYAN}brew install node${NC}"
|
|
106
|
+
fi
|
|
107
|
+
else
|
|
108
|
+
fail "Node.js not found"
|
|
109
|
+
echo -e " Fix: ${CYAN}brew install node${NC}"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# jq
|
|
113
|
+
if command -v jq &>/dev/null; then
|
|
114
|
+
ok "jq $(jq --version 2>/dev/null | sed 's/^jq-//' || echo 'installed')"
|
|
115
|
+
else
|
|
116
|
+
fail "jq not found"
|
|
117
|
+
echo -e " Fix: ${CYAN}brew install jq${NC}"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# git config
|
|
121
|
+
GIT_NAME=$(git config --global user.name 2>/dev/null || true)
|
|
122
|
+
GIT_EMAIL=$(git config --global user.email 2>/dev/null || true)
|
|
123
|
+
if [[ -n "$GIT_NAME" && -n "$GIT_EMAIL" ]]; then
|
|
124
|
+
ok "Git identity: $GIT_NAME <$GIT_EMAIL>"
|
|
125
|
+
else
|
|
126
|
+
if [[ -z "$GIT_NAME" ]]; then
|
|
127
|
+
fail "git config user.name not set"
|
|
128
|
+
echo -e " Fix: ${CYAN}git config --global user.name \"Your Name\"${NC}"
|
|
129
|
+
fi
|
|
130
|
+
if [[ -z "$GIT_EMAIL" ]]; then
|
|
131
|
+
fail "git config user.email not set"
|
|
132
|
+
echo -e " Fix: ${CYAN}git config --global user.email \"you@example.com\"${NC}"
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Check if Phase 1 failed
|
|
137
|
+
if [[ $BLOCK -gt 0 ]]; then
|
|
138
|
+
echo ""
|
|
139
|
+
echo -e " ${RED}${BOLD}PREFLIGHT FAILED${NC} — fix $BLOCK blocking issue(s) above"
|
|
140
|
+
# Write report even on failure
|
|
141
|
+
mkdir -p "$PRAXIS_DIR" 2>/dev/null || true
|
|
142
|
+
jq -nc \
|
|
143
|
+
--argjson pass "$PASS" \
|
|
144
|
+
--argjson warn "$WARN" \
|
|
145
|
+
--argjson block "$BLOCK" \
|
|
146
|
+
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
147
|
+
--arg status "failed" \
|
|
148
|
+
'{timestamp: $ts, status: $status, pass: $pass, warn: $warn, block: $block}' \
|
|
149
|
+
> "$REPORT_FILE" 2>/dev/null || true
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# ═══════════════════════════════════════════
|
|
154
|
+
# Phase 2: GitHub Auth
|
|
155
|
+
# ═══════════════════════════════════════════
|
|
156
|
+
step "Phase 2: GitHub authentication"
|
|
157
|
+
|
|
158
|
+
if gh auth status &>/dev/null 2>&1; then
|
|
159
|
+
ok "GitHub CLI authenticated"
|
|
160
|
+
# Auto-populate GITHUB_TOKEN
|
|
161
|
+
GH_TOKEN=$(gh auth token 2>/dev/null || true)
|
|
162
|
+
if [[ -n "$GH_TOKEN" ]]; then
|
|
163
|
+
mkdir -p "$PRAXIS_DIR"
|
|
164
|
+
chmod 700 "$PRAXIS_DIR"
|
|
165
|
+
if [[ -f "$SECRETS_FILE" ]]; then
|
|
166
|
+
grep -v "^GITHUB_TOKEN=" "$SECRETS_FILE" > "${SECRETS_FILE}.tmp" 2>/dev/null || true
|
|
167
|
+
mv "${SECRETS_FILE}.tmp" "$SECRETS_FILE"
|
|
168
|
+
fi
|
|
169
|
+
echo "GITHUB_TOKEN=${GH_TOKEN}" >> "$SECRETS_FILE"
|
|
170
|
+
chmod 600 "$SECRETS_FILE"
|
|
171
|
+
ok "GITHUB_TOKEN auto-populated from gh auth"
|
|
172
|
+
export GITHUB_TOKEN="$GH_TOKEN"
|
|
173
|
+
fi
|
|
174
|
+
elif [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
|
175
|
+
ok "GITHUB_TOKEN found in environment"
|
|
176
|
+
else
|
|
177
|
+
warn "GitHub not authenticated — github-mcp feature disabled"
|
|
178
|
+
echo -e " Fix: ${CYAN}gh auth login${NC}"
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# ═══════════════════════════════════════════
|
|
182
|
+
# Phase 3: Optional keys
|
|
183
|
+
# ═══════════════════════════════════════════
|
|
184
|
+
step "Phase 3: Optional API keys"
|
|
185
|
+
|
|
186
|
+
# PERPLEXITY_API_KEY
|
|
187
|
+
if [[ -n "${PERPLEXITY_API_KEY:-}" ]]; then
|
|
188
|
+
if [[ "$PERPLEXITY_API_KEY" == pplx-* ]]; then
|
|
189
|
+
MASKED="${PERPLEXITY_API_KEY:0:8}********"
|
|
190
|
+
ok "PERPLEXITY_API_KEY ($MASKED) — live-research enabled"
|
|
191
|
+
else
|
|
192
|
+
warn "PERPLEXITY_API_KEY set but doesn't start with pplx-"
|
|
193
|
+
fi
|
|
194
|
+
else
|
|
195
|
+
warn "PERPLEXITY_API_KEY not set — live research via Sonar disabled"
|
|
196
|
+
echo -e " Get key: ${CYAN}https://www.perplexity.ai/settings/api${NC}"
|
|
197
|
+
echo -e " Set: ${CYAN}praxis-preflight.sh set-key PERPLEXITY_API_KEY pplx-...${NC}"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# OBSIDIAN_VAULT
|
|
201
|
+
VAULT_PATH="${OBSIDIAN_VAULT:-}"
|
|
202
|
+
if [[ -z "$VAULT_PATH" ]]; then
|
|
203
|
+
# Try to read from praxis.config.json
|
|
204
|
+
CONFIG_FILE="$HOME/.claude/praxis.config.json"
|
|
205
|
+
if [[ -f "$CONFIG_FILE" ]]; then
|
|
206
|
+
VAULT_PATH=$(jq -r '.vault_path // empty' "$CONFIG_FILE" 2>/dev/null || true)
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
if [[ -n "$VAULT_PATH" && -d "$VAULT_PATH" ]]; then
|
|
211
|
+
ok "Vault: $VAULT_PATH"
|
|
212
|
+
else
|
|
213
|
+
warn "No Obsidian vault configured — long-term memory disabled"
|
|
214
|
+
echo -e " Set: ${CYAN}praxis-preflight.sh set-key OBSIDIAN_VAULT /path/to/vault${NC}"
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# ═══════════════════════════════════════════
|
|
218
|
+
# Phase 4: MCP servers
|
|
219
|
+
# ═══════════════════════════════════════════
|
|
220
|
+
step "Phase 4: MCP servers"
|
|
221
|
+
|
|
222
|
+
if command -v claude &>/dev/null; then
|
|
223
|
+
MCP_LIST=$(claude mcp list 2>/dev/null || true)
|
|
224
|
+
|
|
225
|
+
# Context7 (always expected)
|
|
226
|
+
if echo "$MCP_LIST" | grep -q "context7"; then
|
|
227
|
+
ok "context7 MCP registered (live docs)"
|
|
228
|
+
else
|
|
229
|
+
warn "context7 MCP not registered"
|
|
230
|
+
echo -e " Fix: ${CYAN}bash scripts/onboard-mcp.sh context7${NC}"
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# GitHub MCP (if token available)
|
|
234
|
+
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
|
235
|
+
if echo "$MCP_LIST" | grep -q "github"; then
|
|
236
|
+
ok "github MCP registered"
|
|
237
|
+
else
|
|
238
|
+
warn "github MCP not registered (token available)"
|
|
239
|
+
echo -e " Fix: ${CYAN}bash scripts/onboard-mcp.sh github${NC}"
|
|
240
|
+
fi
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
# Perplexity MCP (if key available)
|
|
244
|
+
if [[ -n "${PERPLEXITY_API_KEY:-}" ]]; then
|
|
245
|
+
if echo "$MCP_LIST" | grep -qi "perplexity"; then
|
|
246
|
+
ok "perplexity MCP registered (live research)"
|
|
247
|
+
else
|
|
248
|
+
warn "perplexity MCP not registered (key available)"
|
|
249
|
+
echo -e " Fix: ${CYAN}bash scripts/onboard-mcp.sh perplexity${NC}"
|
|
250
|
+
fi
|
|
251
|
+
fi
|
|
252
|
+
else
|
|
253
|
+
warn "claude CLI not available — cannot check MCP servers"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# ═══════════════════════════════════════════
|
|
257
|
+
# Phase 5: Optional tools + hooks
|
|
258
|
+
# ═══════════════════════════════════════════
|
|
259
|
+
step "Phase 5: Optional tools & hooks"
|
|
260
|
+
|
|
261
|
+
# Optional security tools
|
|
262
|
+
for tool in trufflehog gitleaks osv-scanner; do
|
|
263
|
+
if command -v "$tool" &>/dev/null; then
|
|
264
|
+
ok "$tool available"
|
|
265
|
+
else
|
|
266
|
+
warn "$tool not installed — enhanced scanning unavailable"
|
|
267
|
+
fi
|
|
268
|
+
done
|
|
269
|
+
|
|
270
|
+
# Check hook scripts exist and are executable
|
|
271
|
+
HOOKS_DIR="$HOME/.claude/hooks"
|
|
272
|
+
REQUIRED_HOOKS=(secret-scan.sh file-guard.sh identity-check.sh credential-guard.sh quality-check.sh session-data-collect.sh)
|
|
273
|
+
for hook in "${REQUIRED_HOOKS[@]}"; do
|
|
274
|
+
if [[ -x "$HOOKS_DIR/$hook" ]] || [[ -L "$HOOKS_DIR/$hook" ]]; then
|
|
275
|
+
ok "Hook: $hook"
|
|
276
|
+
else
|
|
277
|
+
warn "Hook missing or not executable: $hook"
|
|
278
|
+
fi
|
|
279
|
+
done
|
|
280
|
+
|
|
281
|
+
# ═══════════════════════════════════════════
|
|
282
|
+
# Summary
|
|
283
|
+
# ═══════════════════════════════════════════
|
|
284
|
+
echo ""
|
|
285
|
+
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
286
|
+
echo -e " Pass: ${GREEN}${BOLD}$PASS${NC}"
|
|
287
|
+
echo -e " Warnings: ${YELLOW}${BOLD}$WARN${NC}"
|
|
288
|
+
echo -e " Blocking: ${RED}${BOLD}$BLOCK${NC}"
|
|
289
|
+
|
|
290
|
+
if [[ $BLOCK -eq 0 ]]; then
|
|
291
|
+
echo -e " ${GREEN}${BOLD}✓ Praxis ready to build${NC}"
|
|
292
|
+
else
|
|
293
|
+
echo -e " ${RED}${BOLD}PREFLIGHT FAILED — fix blocking issues above${NC}"
|
|
294
|
+
fi
|
|
295
|
+
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
296
|
+
|
|
297
|
+
# Write JSON report
|
|
298
|
+
mkdir -p "$PRAXIS_DIR" 2>/dev/null || true
|
|
299
|
+
chmod 700 "$PRAXIS_DIR" 2>/dev/null || true
|
|
300
|
+
|
|
301
|
+
STATUS="passed"
|
|
302
|
+
[[ $BLOCK -gt 0 ]] && STATUS="failed"
|
|
303
|
+
[[ $WARN -gt 0 && $BLOCK -eq 0 ]] && STATUS="passed_with_warnings"
|
|
304
|
+
|
|
305
|
+
if command -v jq &>/dev/null; then
|
|
306
|
+
jq -nc \
|
|
307
|
+
--argjson pass "$PASS" \
|
|
308
|
+
--argjson warn "$WARN" \
|
|
309
|
+
--argjson block "$BLOCK" \
|
|
310
|
+
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
311
|
+
--arg status "$STATUS" \
|
|
312
|
+
'{timestamp: $ts, status: $status, pass: $pass, warn: $warn, block: $block}' \
|
|
313
|
+
> "$REPORT_FILE" 2>/dev/null || true
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
[[ $BLOCK -gt 0 ]] && exit 1
|
|
317
|
+
exit 0
|
package/package.json
CHANGED
package/scripts/install-tools.sh
CHANGED
|
@@ -44,12 +44,12 @@ install_go() { go install "$@" 2>/dev/null || true; }
|
|
|
44
44
|
# ── Core (always) ──
|
|
45
45
|
echo "── Installing core tools ──"
|
|
46
46
|
if [ "$PKG" = "brew" ]; then
|
|
47
|
-
install_brew shellcheck shfmt jq
|
|
47
|
+
install_brew shellcheck shfmt jq gitleaks
|
|
48
48
|
else
|
|
49
49
|
sudo apt-get update -qq
|
|
50
50
|
sudo apt-get install -y -qq shellcheck jq
|
|
51
51
|
install_go mvdan.cc/sh/v3/cmd/shfmt@latest
|
|
52
|
-
echo "NOTE: Install
|
|
52
|
+
echo "NOTE: Install gitleaks manually (see https://github.com/gitleaks/gitleaks)"
|
|
53
53
|
fi
|
|
54
54
|
install_npm markdownlint-cli @commitlint/cli @commitlint/config-conventional
|
|
55
55
|
install_pip semgrep yamllint
|
|
@@ -87,31 +87,11 @@ if command -v docker &>/dev/null; then
|
|
|
87
87
|
fi
|
|
88
88
|
fi
|
|
89
89
|
|
|
90
|
-
# ── Vale setup (sync packages + copy Praxis rules) ──
|
|
91
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
92
|
-
VALE_CONFIG_DIR="$SCRIPT_DIR/../base/configs/vale"
|
|
93
|
-
if command -v vale &>/dev/null && [ -f "$VALE_CONFIG_DIR/.vale.ini" ]; then
|
|
94
|
-
echo ""
|
|
95
|
-
echo "── Setting up Vale prose linter ──"
|
|
96
|
-
(cd "$VALE_CONFIG_DIR" && vale sync 2>/dev/null || true)
|
|
97
|
-
if [ -d "$VALE_CONFIG_DIR/Praxis" ] && [ -d "$VALE_CONFIG_DIR/.vale-styles" ]; then
|
|
98
|
-
cp -R "$VALE_CONFIG_DIR/Praxis" "$VALE_CONFIG_DIR/.vale-styles/Praxis"
|
|
99
|
-
echo " Praxis rules copied to .vale-styles/"
|
|
100
|
-
# Copy vocabulary files to Vale's expected location
|
|
101
|
-
if [ -d "$VALE_CONFIG_DIR/Praxis/vocabularies" ]; then
|
|
102
|
-
mkdir -p "$VALE_CONFIG_DIR/.vale-styles/config/vocabularies/Praxis"
|
|
103
|
-
cp "$VALE_CONFIG_DIR/Praxis/vocabularies/"*.txt "$VALE_CONFIG_DIR/.vale-styles/config/vocabularies/Praxis/"
|
|
104
|
-
echo " Praxis vocabulary copied to .vale-styles/config/vocabularies/"
|
|
105
|
-
fi
|
|
106
|
-
fi
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
90
|
# ── VS Code extensions ──
|
|
110
91
|
if command -v code &>/dev/null; then
|
|
111
92
|
echo ""
|
|
112
93
|
echo "── Installing VS Code extensions ──"
|
|
113
94
|
CORE_EXTENSIONS=(
|
|
114
|
-
chrischinchilla.vale-vscode
|
|
115
95
|
timonwong.shellcheck
|
|
116
96
|
editorconfig.editorconfig
|
|
117
97
|
davidanson.vscode-markdownlint
|
|
@@ -134,7 +114,7 @@ echo ""
|
|
|
134
114
|
echo "=== Done. Run 'bash scripts/install-tools.sh --all' to install all stacks. ==="
|
|
135
115
|
echo ""
|
|
136
116
|
echo "Installed tools:"
|
|
137
|
-
for tool in shellcheck shfmt jq
|
|
117
|
+
for tool in shellcheck shfmt jq gitleaks goimports golangci-lint govulncheck tflint trivy infracost hadolint semgrep yamllint markdownlint commitlint; do
|
|
138
118
|
if command -v "$tool" &>/dev/null; then
|
|
139
119
|
printf " ✓ %s\n" "$tool"
|
|
140
120
|
else
|
package/scripts/lint-harness.sh
CHANGED
|
@@ -124,7 +124,7 @@ while IFS= read -r file; do
|
|
|
124
124
|
| grep -vE '`[^`]*\{[^}]+\}[^`]*`' \
|
|
125
125
|
| grep -vE '^[0-9]+:\s*#' \
|
|
126
126
|
| grep -vE '^[0-9]+:\s*echo ' \
|
|
127
|
-
| grep -vE '\{vault_path\}|\{today_date\}|\{project-slug\}|\{YYYY-MM-DD\}|\{kebab-title\}|\{date\}|\{[nN]\}|\{1-line\}|\{ISO timestamp\}|\{repo_root\}|\{identity_email\}|\{stack\}|\{placeholder\}|\{placeholders\}|\{http_code\}' \
|
|
127
|
+
| grep -vE '\{vault_path\}|\{today_date\}|\{project-slug\}|\{YYYY-MM-DD\}|\{kebab-title\}|\{date\}|\{[nN]\}|\{1-line\}|\{ISO timestamp\}|\{repo_root\}|\{identity_email\}|\{stack\}|\{placeholder\}|\{placeholders\}|\{http_code\}|\$\{[A-Z_]+\}|\$[a-z_]+' \
|
|
128
128
|
|| true)
|
|
129
129
|
if [[ -n "$matches" ]]; then
|
|
130
130
|
rel_path="${file#"$REPO_PATH"/}"
|
package/scripts/test-harness.sh
CHANGED
|
@@ -204,38 +204,13 @@ echo "Config directory:"
|
|
|
204
204
|
|
|
205
205
|
if [[ -d "$REPO_PATH/base/configs" ]]; then
|
|
206
206
|
ok "base/configs/ exists"
|
|
207
|
-
for config_dir in
|
|
207
|
+
for config_dir in linters; do
|
|
208
208
|
if [[ -d "$REPO_PATH/base/configs/$config_dir" ]]; then
|
|
209
209
|
ok "base/configs/$config_dir/ exists"
|
|
210
210
|
else
|
|
211
211
|
error "base/configs/$config_dir/ missing"
|
|
212
212
|
fi
|
|
213
213
|
done
|
|
214
|
-
|
|
215
|
-
# Vale config check
|
|
216
|
-
if [[ -f "$REPO_PATH/base/configs/vale/.vale.ini" ]]; then
|
|
217
|
-
ok "vale config exists"
|
|
218
|
-
else
|
|
219
|
-
error "vale config missing"
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
# Vale rule files
|
|
223
|
-
VALE_COUNT=$(find "$REPO_PATH/base/configs/vale/Praxis" -name "*.yml" 2>/dev/null | wc -l | tr -d ' ')
|
|
224
|
-
if [[ "$VALE_COUNT" -ge 6 ]]; then
|
|
225
|
-
ok "vale Praxis rules: $VALE_COUNT files"
|
|
226
|
-
else
|
|
227
|
-
error "vale Praxis rules: expected >=6, found $VALE_COUNT"
|
|
228
|
-
fi
|
|
229
|
-
|
|
230
|
-
# YAML validation on vale rules
|
|
231
|
-
for f in "$REPO_PATH"/base/configs/vale/Praxis/*.yml; do
|
|
232
|
-
name=$(basename "$f")
|
|
233
|
-
if python3 -c "import yaml; yaml.safe_load(open('$f'))" 2>/dev/null; then
|
|
234
|
-
ok "vale/$name valid YAML"
|
|
235
|
-
else
|
|
236
|
-
error "vale/$name invalid YAML"
|
|
237
|
-
fi
|
|
238
|
-
done
|
|
239
214
|
else
|
|
240
215
|
error "base/configs/ directory missing"
|
|
241
216
|
fi
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
StylesPath = .vale-styles
|
|
2
|
-
MinAlertLevel = suggestion
|
|
3
|
-
Vocab = Praxis
|
|
4
|
-
|
|
5
|
-
Packages = Google, proselint, alex, write-good, Readability
|
|
6
|
-
|
|
7
|
-
# Full suite on Markdown
|
|
8
|
-
[*.md]
|
|
9
|
-
BasedOnStyles = Praxis, Google, proselint, alex, write-good, Readability
|
|
10
|
-
|
|
11
|
-
# Comment-only scanning on code files
|
|
12
|
-
[*.{go,tf,sh,ps1}]
|
|
13
|
-
BasedOnStyles = Praxis
|
|
14
|
-
|
|
15
|
-
# Praxis rules only on YAML
|
|
16
|
-
[*.{yml,yaml}]
|
|
17
|
-
BasedOnStyles = Praxis
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
extends: substitution
|
|
2
|
-
message: "AI vocabulary detected: '%s' — rephrase in plain language."
|
|
3
|
-
level: warning
|
|
4
|
-
ignorecase: true
|
|
5
|
-
swap:
|
|
6
|
-
delve: "examine, explore, investigate"
|
|
7
|
-
tapestry: "combination, mix"
|
|
8
|
-
multifaceted: "complex, varied"
|
|
9
|
-
leverage: "use"
|
|
10
|
-
paradigm: "model, approach"
|
|
11
|
-
synergy: "collaboration, combination"
|
|
12
|
-
holistic: "complete, whole"
|
|
13
|
-
robust: "strong, reliable"
|
|
14
|
-
seamless: "smooth"
|
|
15
|
-
cutting-edge: "modern, latest"
|
|
16
|
-
game-changer: "improvement, breakthrough"
|
|
17
|
-
deep dive: "detailed look, analysis"
|
|
18
|
-
unpack: "explain, examine"
|
|
19
|
-
landscape: "field, area, environment"
|
|
20
|
-
ecosystem: "system, environment"
|
|
21
|
-
streamline: "simplify"
|
|
22
|
-
spearhead: "lead"
|
|
23
|
-
groundbreaking: "new, innovative"
|
|
24
|
-
transformative: "significant"
|
|
25
|
-
empower: "enable, help"
|
|
26
|
-
reimagine: "rethink, redesign"
|
|
27
|
-
cornerstone: "foundation, basis"
|
|
28
|
-
pivotal: "important, key"
|
|
29
|
-
catalyst: "trigger, cause"
|
|
30
|
-
navigate: "manage, handle"
|
|
31
|
-
harness: "use"
|
|
32
|
-
unlock: "enable, reveal"
|
|
33
|
-
bolster: "strengthen, support"
|
|
34
|
-
underscore: "highlight, emphasize"
|
|
35
|
-
illuminate: "explain, clarify"
|
|
36
|
-
forge: "build, create"
|
|
37
|
-
foster: "encourage, develop"
|
|
38
|
-
cultivate: "develop, build"
|
|
39
|
-
paramount: "important, critical"
|
|
40
|
-
meticulous: "careful, thorough"
|
|
41
|
-
intricate: "complex, detailed"
|
|
42
|
-
nuanced: "subtle"
|
|
43
|
-
comprehensive: "complete, thorough"
|
|
44
|
-
invaluable: "very useful, essential"
|
|
45
|
-
instrumental: "important, helpful"
|
|
46
|
-
embark: "start, begin"
|
|
47
|
-
endeavor: "effort, attempt"
|
|
48
|
-
realm: "area, field"
|
|
49
|
-
facet: "aspect, part"
|
|
50
|
-
Moreover: "Also, Additionally"
|
|
51
|
-
Furthermore: "Also"
|
|
52
|
-
Notably: "Note that"
|
|
53
|
-
Importantly: "Note"
|
|
54
|
-
Essentially: "(omit or rewrite)"
|
|
55
|
-
Fundamentally: "(omit or rewrite)"
|
|
56
|
-
Undoubtedly: "(omit or rewrite)"
|
|
57
|
-
Interestingly: "(omit or rewrite)"
|
|
58
|
-
testament: "proof, evidence"
|
|
59
|
-
beacon: "example, guide"
|
|
60
|
-
resonate: "connect, appeal"
|
|
61
|
-
bespoke: "custom"
|
|
62
|
-
elevate: "improve, raise"
|
|
63
|
-
amplify: "increase, strengthen"
|
|
64
|
-
augment: "add to, supplement"
|
|
65
|
-
myriad: "many"
|
|
66
|
-
plethora: "many, excess"
|
|
67
|
-
burgeoning: "growing"
|
|
68
|
-
nascent: "new, early"
|
|
69
|
-
advent: "arrival, start"
|
|
70
|
-
proliferation: "spread, growth"
|
|
71
|
-
propel: "drive, push"
|
|
72
|
-
overarching: "main, overall"
|
|
73
|
-
dovetail: "fit, align"
|
|
74
|
-
tailored: "custom, specific"
|
|
75
|
-
juxtapose: "compare, contrast"
|
|
76
|
-
dichotomy: "contrast, split"
|
|
77
|
-
eponymous: "named after"
|
|
78
|
-
erstwhile: "former"
|
|
79
|
-
zeitgeist: "spirit of the time"
|
|
80
|
-
ethos: "values, character"
|
|
81
|
-
nexus: "connection, link"
|
|
82
|
-
crucible: "test, trial"
|
|
83
|
-
vanguard: "forefront, leading edge"
|
|
84
|
-
linchpin: "key element"
|
|
85
|
-
bedrock: "foundation"
|
|
86
|
-
scaffolding: "framework, structure"
|
|
87
|
-
underpinning: "basis, foundation"
|
|
88
|
-
bulwark: "defense, safeguard"
|
|
89
|
-
bastion: "stronghold"
|
|
90
|
-
harbinger: "sign, indicator"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
extends: existence
|
|
2
|
-
message: "Inflated copula: '%s' — simplify."
|
|
3
|
-
level: suggestion
|
|
4
|
-
ignorecase: true
|
|
5
|
-
tokens:
|
|
6
|
-
- "serves as a"
|
|
7
|
-
- "acts as a"
|
|
8
|
-
- "functions as a"
|
|
9
|
-
- "plays a pivotal role"
|
|
10
|
-
- "plays a crucial role"
|
|
11
|
-
- "plays a vital role"
|
|
12
|
-
- "plays a key role"
|
|
13
|
-
- "plays an important role"
|
|
14
|
-
- "boasts a"
|
|
15
|
-
- "represents a"
|
|
16
|
-
- "constitutes a"
|
|
17
|
-
- "comprises a"
|
|
18
|
-
- "stands as a"
|
|
19
|
-
- "remains a"
|
|
20
|
-
- "proves to be"
|
|
21
|
-
- "turns out to be"
|
|
22
|
-
- "happens to be"
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
extends: existence
|
|
2
|
-
message: "Elegant variation: '%s' — use the plain term."
|
|
3
|
-
level: suggestion
|
|
4
|
-
ignorecase: true
|
|
5
|
-
tokens:
|
|
6
|
-
- "the aforementioned"
|
|
7
|
-
- "the above-mentioned"
|
|
8
|
-
- "the previously mentioned"
|
|
9
|
-
- "this individual"
|
|
10
|
-
- "said individual"
|
|
11
|
-
- "the eponymous"
|
|
12
|
-
- "this entity"
|
|
13
|
-
- "the latter"
|
|
14
|
-
- "the former"
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
extends: capitalization
|
|
2
|
-
message: "'%s' should use sentence case."
|
|
3
|
-
level: warning
|
|
4
|
-
scope: heading
|
|
5
|
-
match: $sentence
|
|
6
|
-
indicators:
|
|
7
|
-
- ":"
|
|
8
|
-
exceptions:
|
|
9
|
-
- Praxis
|
|
10
|
-
- CLAUDE
|
|
11
|
-
- MCP
|
|
12
|
-
- SPEC
|
|
13
|
-
- ADR
|
|
14
|
-
- API
|
|
15
|
-
- CLI
|
|
16
|
-
- VS Code
|
|
17
|
-
- GitHub
|
|
18
|
-
- Azure
|
|
19
|
-
- Terraform
|
|
20
|
-
- Obsidian
|
|
21
|
-
- Node.js
|
|
22
|
-
- TypeScript
|
|
23
|
-
- JavaScript
|
|
24
|
-
- PowerShell
|
|
25
|
-
- YAML
|
|
26
|
-
- JSON
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
extends: existence
|
|
2
|
-
message: "Hedging detected: '%s' — commit to a position or remove."
|
|
3
|
-
level: suggestion
|
|
4
|
-
ignorecase: true
|
|
5
|
-
tokens:
|
|
6
|
-
- "it could potentially"
|
|
7
|
-
- "it might potentially"
|
|
8
|
-
- "it may potentially"
|
|
9
|
-
- "arguably"
|
|
10
|
-
- "it remains to be seen"
|
|
11
|
-
- "only time will tell"
|
|
12
|
-
- "it is not unreasonable to"
|
|
13
|
-
- "one could argue"
|
|
14
|
-
- "some might say"
|
|
15
|
-
- "it would not be wrong to"
|
|
16
|
-
- "to some extent"
|
|
17
|
-
- "to a certain degree"
|
|
18
|
-
- "in some ways"
|
|
19
|
-
- "more or less"
|
|
20
|
-
- "for the most part"
|
|
21
|
-
- "by and large"
|
|
22
|
-
- "generally speaking"
|