@esoteric-logic/praxis-harness 2.9.0 → 2.9.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.
@@ -2,6 +2,7 @@
2
2
  # PostToolUse hook — auto-formats files after edit.
3
3
  # Always exits 0 (advisory, never blocks).
4
4
  set -uo pipefail
5
+ trap 'exit 0' ERR
5
6
 
6
7
  INPUT=$(cat)
7
8
  FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
@@ -2,7 +2,8 @@
2
2
  # dep-audit.sh — PostToolUse:Write|Edit|MultiEdit hook
3
3
  # Runs dependency vulnerability checks when manifest files are modified.
4
4
  # Always exits 0 (advisory only — PostToolUse cannot hard-block).
5
- set -euo pipefail
5
+ set -uo pipefail
6
+ trap 'exit 0' ERR
6
7
 
7
8
  INPUT=$(cat)
8
9
  FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
@@ -20,28 +21,40 @@ case "$BASENAME" in
20
21
  package.json)
21
22
  ECOSYSTEM="npm"
22
23
  if command -v npm &>/dev/null && [[ -f "$DIR/package-lock.json" || -f "$DIR/node_modules/.package-lock.json" ]]; then
23
- AUDIT_RESULT=$(cd "$DIR" && npm audit --audit-level=high --json 2>/dev/null || true)
24
+ # npm audit exits non-zero when vulnerabilities exist — capture output regardless
25
+ AUDIT_RESULT=$(cd "$DIR" && npm audit --audit-level=high --json 2>/dev/null) || {
26
+ if [[ -n "$AUDIT_RESULT" ]]; then
27
+ : # non-zero with output = vulnerabilities found (expected)
28
+ else
29
+ echo "DEP-AUDIT (npm): audit command failed" >&2
30
+ fi
31
+ }
24
32
  fi
25
33
  ;;
26
34
  go.mod)
27
35
  ECOSYSTEM="go"
28
36
  if command -v govulncheck &>/dev/null; then
29
- AUDIT_RESULT=$(cd "$DIR" && govulncheck -json ./... 2>/dev/null || true)
37
+ AUDIT_RESULT=$(cd "$DIR" && govulncheck -json ./... 2>/dev/null) || {
38
+ [[ -z "$AUDIT_RESULT" ]] && echo "DEP-AUDIT (go): govulncheck failed" >&2
39
+ }
30
40
  elif command -v go &>/dev/null; then
31
- # Fallback: at least check for known issues via go list
32
- AUDIT_RESULT=$(cd "$DIR" && go list -m -json all 2>/dev/null | head -c 5000 || true)
41
+ AUDIT_RESULT=$(cd "$DIR" && go list -m -json all 2>/dev/null | head -c 5000) || AUDIT_RESULT=""
33
42
  fi
34
43
  ;;
35
44
  requirements.txt|pyproject.toml)
36
45
  ECOSYSTEM="python"
37
46
  if command -v pip-audit &>/dev/null; then
38
- AUDIT_RESULT=$(cd "$DIR" && pip-audit --format=json 2>/dev/null || true)
47
+ AUDIT_RESULT=$(cd "$DIR" && pip-audit --format=json 2>/dev/null) || {
48
+ [[ -z "$AUDIT_RESULT" ]] && echo "DEP-AUDIT (python): pip-audit failed" >&2
49
+ }
39
50
  fi
40
51
  ;;
41
52
  Cargo.toml)
42
53
  ECOSYSTEM="rust"
43
54
  if command -v cargo-audit &>/dev/null; then
44
- AUDIT_RESULT=$(cd "$DIR" && cargo audit --json 2>/dev/null || true)
55
+ AUDIT_RESULT=$(cd "$DIR" && cargo audit --json 2>/dev/null) || {
56
+ [[ -z "$AUDIT_RESULT" ]] && echo "DEP-AUDIT (rust): cargo-audit failed" >&2
57
+ }
45
58
  fi
46
59
  ;;
47
60
  *)
@@ -2,6 +2,7 @@
2
2
  # Stop hook — collects structured session data and stages it for the Stop prompt.
3
3
  # Always exits 0 (advisory, never blocks session end).
4
4
  set -uo pipefail
5
+ trap 'exit 0' ERR
5
6
 
6
7
  CONFIG_FILE="$HOME/.claude/praxis.config.json"
7
8
 
@@ -2,6 +2,7 @@
2
2
  # PreCompact hook — writes minimal checkpoint to vault before context compaction.
3
3
  # Always exits 0 (advisory, never blocks compaction).
4
4
  set -uo pipefail
5
+ trap 'exit 0' ERR
5
6
 
6
7
  CONFIG_FILE="$HOME/.claude/praxis.config.json"
7
8
 
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # ════════════════════════════════════════════════════════════════
3
+ # Praxis — Shared Kit Install Check
4
+ # Source this file in kit install.sh scripts for consistent
5
+ # tool-checking output and counters.
6
+ #
7
+ # Usage:
8
+ # source "$(dirname "$0")/../../base/lib/kit-check.sh"
9
+ # check "jq" "brew install jq"
10
+ # check "curl" "pre-installed on macOS/Linux"
11
+ # kit_check_summary
12
+ # ════════════════════════════════════════════════════════════════
13
+
14
+ KIT_CHECK_PASS=0
15
+ KIT_CHECK_TOTAL=0
16
+
17
+ check() {
18
+ local cmd="$1"
19
+ local install_hint="$2"
20
+ local optional="${3:-}"
21
+
22
+ KIT_CHECK_TOTAL=$((KIT_CHECK_TOTAL + 1))
23
+ if command -v "$cmd" &>/dev/null; then
24
+ echo " ✓ $cmd found ($(command -v "$cmd"))"
25
+ KIT_CHECK_PASS=$((KIT_CHECK_PASS + 1))
26
+ else
27
+ if [[ "$optional" == "optional" ]]; then
28
+ echo " ⚠ $cmd not found (optional)"
29
+ else
30
+ echo " ✗ $cmd not found"
31
+ fi
32
+ echo " Install: $install_hint"
33
+ fi
34
+ }
35
+
36
+ kit_check_summary() {
37
+ echo ""
38
+ echo " $KIT_CHECK_PASS/$KIT_CHECK_TOTAL tools found"
39
+ if [[ $KIT_CHECK_PASS -lt $KIT_CHECK_TOTAL ]]; then
40
+ echo " ⚠ Some tools missing. Install them before using kit commands."
41
+ fi
42
+ }
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════��════════════════════════════════════════════════════════
3
+ # Praxis — Shared Output Helpers
4
+ # Source this file instead of defining colors/helpers inline.
5
+ #
6
+ # Usage:
7
+ # source "$(dirname "$0")/../base/lib/output.sh"
8
+ # # or with absolute path:
9
+ # source "$HOME/.claude/lib/output.sh"
10
+ #
11
+ # Safe to source multiple times — guards against redefinition.
12
+ # ════��═══════════════════════════════════════════════════════════
13
+
14
+ # ─── Colors (skip if already set) ───
15
+ RED="${RED:-\033[0;31m}"
16
+ GREEN="${GREEN:-\033[0;32m}"
17
+ YELLOW="${YELLOW:-\033[0;33m}"
18
+ CYAN="${CYAN:-\033[0;36m}"
19
+ BOLD="${BOLD:-\033[1m}"
20
+ DIM="${DIM:-\033[2m}"
21
+ NC="${NC:-\033[0m}"
22
+
23
+ # ─── Output helpers (skip if already defined) ───
24
+ if ! declare -f ok &>/dev/null; then
25
+ ok() { echo -e " ${GREEN}✓${NC} $1"; }
26
+ fi
27
+ if ! declare -f warn &>/dev/null; then
28
+ warn() { echo -e " ${YELLOW}⚠${NC} $1"; }
29
+ fi
30
+ if ! declare -f fail &>/dev/null; then
31
+ fail() { echo -e " ${RED}✗${NC} $1"; }
32
+ fi
33
+ if ! declare -f step &>/dev/null; then
34
+ step() { echo -e "\n${CYAN}${BOLD}$1${NC}"; }
35
+ fi
36
+ if ! declare -f dim &>/dev/null; then
37
+ dim() { echo -e " ${DIM}$1${NC}"; }
38
+ fi
@@ -4,13 +4,10 @@
4
4
  # Also implements: set-key subcommand for secret management.
5
5
  set -euo pipefail
6
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'
7
+ # ─── Colors (shared) ───
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ REPO_DIR="$(dirname "$SCRIPT_DIR")"
10
+ source "$REPO_DIR/base/lib/output.sh"
14
11
 
15
12
  PASS=0
16
13
  WARN=0
@@ -19,10 +16,10 @@ PRAXIS_DIR="$HOME/.praxis"
19
16
  SECRETS_FILE="$PRAXIS_DIR/secrets"
20
17
  REPORT_FILE="$PRAXIS_DIR/preflight-report.json"
21
18
 
19
+ # Override shared helpers with counter-incrementing versions
22
20
  ok() { echo -e " ${GREEN}✓${NC} $1"; PASS=$((PASS + 1)); }
23
21
  warn() { echo -e " ${YELLOW}⚠${NC} $1"; WARN=$((WARN + 1)); }
24
22
  fail() { echo -e " ${RED}✗${NC} $1"; BLOCK=$((BLOCK + 1)); }
25
- step() { echo -e "\n${CYAN}${BOLD}$1${NC}"; }
26
23
 
27
24
  # ═══════════════════════════════════════════
28
25
  # Subcommand: set-key
@@ -4,19 +4,7 @@ set -euo pipefail
4
4
  echo "=== Praxis: Installing api kit ==="
5
5
  echo ""
6
6
 
7
- PASS=0
8
- TOTAL=0
9
-
10
- check() {
11
- TOTAL=$((TOTAL + 1))
12
- if command -v "$1" &>/dev/null; then
13
- echo " ✓ $1 found ($(command -v "$1"))"
14
- PASS=$((PASS + 1))
15
- else
16
- echo " ✗ $1 not found"
17
- echo " Install: $2"
18
- fi
19
- }
7
+ source "$(dirname "$0")/../../base/lib/kit-check.sh"
20
8
 
21
9
  echo "Checking optional CLI tools..."
22
10
  echo ""
@@ -24,10 +12,9 @@ echo ""
24
12
  check "jq" "brew install jq OR apt-get install jq"
25
13
  check "curl" "pre-installed on macOS/Linux"
26
14
 
27
- echo ""
28
- echo " $PASS/$TOTAL tools found"
29
- echo ""
15
+ kit_check_summary
30
16
 
17
+ echo ""
31
18
  echo "Note: This kit uses Claude's built-in analysis capabilities."
32
19
  echo "No external API linting tools required."
33
20
  echo ""
@@ -8,6 +8,25 @@ BASELINE="$REPO_ROOT/.quality-baseline.json"
8
8
  TMP="/tmp/praxis-quality-$$"
9
9
  mkdir -p "$TMP"
10
10
 
11
+ # Safe jq wrapper: validates JSON before querying; returns fallback on parse failure
12
+ # and logs a warning so gate operators know a scan produced bad output.
13
+ safe_jq() {
14
+ local query="$1"
15
+ local file="$2"
16
+ local fallback="${3:-0}"
17
+ if [[ ! -s "$file" ]]; then
18
+ echo "$fallback"
19
+ return
20
+ fi
21
+ if ! jq empty "$file" 2>/dev/null; then
22
+ echo " ⚠ WARNING: $file is not valid JSON — treating as scan failure" >&2
23
+ GATE_WARNINGS+=("PARSE: $(basename "$file") produced invalid output")
24
+ echo "$fallback"
25
+ return
26
+ fi
27
+ jq -r "$query" "$file" 2>/dev/null || echo "$fallback"
28
+ }
29
+
11
30
  echo ""
12
31
  echo "Praxis Code Quality Gate"
13
32
  echo "------------------------"
@@ -32,8 +51,8 @@ if [ -s "$TMP/code-files.txt" ]; then
32
51
  FILES=$(cat "$TMP/code-files.txt" | tr '\n' ' ')
33
52
  opengrep scan --config auto --json $FILES > "$TMP/sast.json" 2>/dev/null || true
34
53
 
35
- CRITICAL=$(jq '[.results[] | select(.extra.severity == "ERROR")] | length' "$TMP/sast.json" 2>/dev/null || echo 0)
36
- HIGH=$(jq '[.results[] | select(.extra.severity == "WARNING")] | length' "$TMP/sast.json" 2>/dev/null || echo 0)
54
+ CRITICAL=$(safe_jq '[.results[] | select(.extra.severity == "ERROR")] | length' "$TMP/sast.json")
55
+ HIGH=$(safe_jq '[.results[] | select(.extra.severity == "WARNING")] | length' "$TMP/sast.json")
37
56
 
38
57
  [ "$CRITICAL" -gt 0 ] && GATE_FAILURES+=("SAST: $CRITICAL critical findings")
39
58
  [ "$HIGH" -gt 0 ] && GATE_WARNINGS+=("SAST: $HIGH high findings")
@@ -44,7 +63,11 @@ SAST_PID=$!
44
63
  # -- SECRETS (TruffleHog) --
45
64
  echo " Secrets scan (TruffleHog)..."
46
65
  trufflehog git file://. --since-commit HEAD~1 --only-verified --json > "$TMP/secrets.json" 2>/dev/null || true
47
- SECRETS=$(jq -s 'length' "$TMP/secrets.json" 2>/dev/null || echo 0)
66
+ if [[ -s "$TMP/secrets.json" ]]; then
67
+ SECRETS=$(jq -s 'length' "$TMP/secrets.json" 2>/dev/null || echo 0)
68
+ else
69
+ SECRETS=0
70
+ fi
48
71
  [ "$SECRETS" -gt 0 ] && GATE_FAILURES+=("SECRETS: $SECRETS verified secrets found")
49
72
  echo " Verified secrets: $SECRETS" &
50
73
  SECRETS_PID=$!
@@ -52,8 +75,8 @@ SECRETS_PID=$!
52
75
  # -- SCA (OSV-Scanner) --
53
76
  echo " Dependency scan (OSV-Scanner)..."
54
77
  osv-scanner scan --format json "$REPO_ROOT" > "$TMP/sca.json" 2>/dev/null || true
55
- SCA_CRITICAL=$(jq '[.vulns[]? | select(.database_specific.severity? == "CRITICAL")] | length' "$TMP/sca.json" 2>/dev/null || echo 0)
56
- SCA_HIGH=$(jq '[.vulns[]? | select(.database_specific.severity? == "HIGH")] | length' "$TMP/sca.json" 2>/dev/null || echo 0)
78
+ SCA_CRITICAL=$(safe_jq '[.vulns[]? | select(.database_specific.severity? == "CRITICAL")] | length' "$TMP/sca.json")
79
+ SCA_HIGH=$(safe_jq '[.vulns[]? | select(.database_specific.severity? == "HIGH")] | length' "$TMP/sca.json")
57
80
  [ "$SCA_CRITICAL" -gt 0 ] && GATE_FAILURES+=("SCA: $SCA_CRITICAL critical CVEs in dependencies")
58
81
  [ "$SCA_HIGH" -gt 0 ] && GATE_WARNINGS+=("SCA: $SCA_HIGH high CVEs in dependencies")
59
82
  echo " Critical CVEs: $SCA_CRITICAL High CVEs: $SCA_HIGH" &
@@ -63,7 +86,7 @@ SCA_PID=$!
63
86
  if [ -s "$TMP/iac-files.txt" ]; then
64
87
  echo " IaC scan (Checkov)..."
65
88
  checkov -d "$REPO_ROOT" --output json --quiet --compact 2>/dev/null > "$TMP/iac.json" || true
66
- IaC_FAIL=$(jq '.results.failed_checks | length' "$TMP/iac.json" 2>/dev/null || echo 0)
89
+ IaC_FAIL=$(safe_jq '.results.failed_checks | length' "$TMP/iac.json")
67
90
  [ "$IaC_FAIL" -gt 0 ] && GATE_WARNINGS+=("IaC: $IaC_FAIL policy violations")
68
91
  echo " Policy violations: $IaC_FAIL"
69
92
  fi &
@@ -73,9 +96,9 @@ IaC_PID=$!
73
96
  wait $SAST_PID $SECRETS_PID $SCA_PID $IaC_PID 2>/dev/null || true
74
97
 
75
98
  # -- COVERAGE --
76
- COVERAGE_THRESHOLD=$(jq -r '.coverage.line_min' "$CONFIG" 2>/dev/null || echo 80)
99
+ COVERAGE_THRESHOLD=$(safe_jq '.coverage.line_min' "$CONFIG" 80)
77
100
  if [ -f "$REPO_ROOT/coverage/coverage-summary.json" ]; then
78
- COVERAGE=$(jq '.total.lines.pct' "$REPO_ROOT/coverage/coverage-summary.json" 2>/dev/null || echo 100)
101
+ COVERAGE=$(safe_jq '.total.lines.pct' "$REPO_ROOT/coverage/coverage-summary.json" 100)
79
102
  BELOW=$(echo "$COVERAGE < $COVERAGE_THRESHOLD" | bc -l 2>/dev/null || echo 0)
80
103
  [ "$BELOW" = "1" ] && GATE_WARNINGS+=("COVERAGE: ${COVERAGE}% below threshold ${COVERAGE_THRESHOLD}%")
81
104
  echo " Coverage: ${COVERAGE}% (threshold: ${COVERAGE_THRESHOLD}%)"
@@ -4,32 +4,19 @@ set -euo pipefail
4
4
  echo "=== Praxis: Installing data kit ==="
5
5
  echo ""
6
6
 
7
- PASS=0
8
- TOTAL=0
9
-
10
- check() {
11
- TOTAL=$((TOTAL + 1))
12
- if command -v "$1" &>/dev/null; then
13
- echo " ✓ $1 found ($(command -v "$1"))"
14
- PASS=$((PASS + 1))
15
- else
16
- echo " ✗ $1 not found (optional)"
17
- echo " Install: $2"
18
- fi
19
- }
7
+ source "$(dirname "$0")/../../base/lib/kit-check.sh"
20
8
 
21
9
  echo "Checking optional CLI tools..."
22
10
  echo ""
23
11
 
24
- check "psql" "brew install postgresql OR apt-get install postgresql-client"
25
- check "mysql" "brew install mysql-client OR apt-get install mysql-client"
26
- check "mongosh" "brew install mongosh OR https://www.mongodb.com/try/download/shell"
12
+ check "psql" "brew install postgresql OR apt-get install postgresql-client" "optional"
13
+ check "mysql" "brew install mysql-client OR apt-get install mysql-client" "optional"
14
+ check "mongosh" "brew install mongosh OR https://www.mongodb.com/try/download/shell" "optional"
27
15
  check "jq" "brew install jq OR apt-get install jq"
28
16
 
29
- echo ""
30
- echo " $PASS/$TOTAL tools found"
31
- echo ""
17
+ kit_check_summary
32
18
 
19
+ echo ""
33
20
  echo "Note: This kit uses Claude's built-in analysis for schema and query review."
34
21
  echo "Database CLI tools are needed only for live query testing."
35
22
  echo ""
@@ -4,19 +4,7 @@ set -euo pipefail
4
4
  echo "=== Praxis: Installing infrastructure kit ==="
5
5
  echo ""
6
6
 
7
- PASS=0
8
- TOTAL=0
9
-
10
- check() {
11
- TOTAL=$((TOTAL + 1))
12
- if command -v "$1" &>/dev/null; then
13
- echo " ✓ $1 found ($(command -v "$1"))"
14
- PASS=$((PASS + 1))
15
- else
16
- echo " ✗ $1 not found"
17
- echo " Install: $2"
18
- fi
19
- }
7
+ source "$(dirname "$0")/../../base/lib/kit-check.sh"
20
8
 
21
9
  echo "Checking required CLI tools..."
22
10
  echo ""
@@ -26,13 +14,7 @@ check "terraform" "https://developer.hashicorp.com/terraform/install"
26
14
  check "tflint" "brew install tflint OR https://github.com/terraform-linters/tflint"
27
15
  check "jq" "brew install jq OR apt-get install jq"
28
16
 
29
- echo ""
30
- echo " $PASS/$TOTAL tools found"
31
- echo ""
32
-
33
- if [[ $PASS -lt $TOTAL ]]; then
34
- echo " ⚠ Some tools missing. Install them before using infrastructure commands."
35
- fi
17
+ kit_check_summary
36
18
 
37
19
  echo ""
38
20
  echo "Note: Skills chain phases are status: planned."
@@ -4,31 +4,18 @@ set -euo pipefail
4
4
  echo "=== Praxis: Installing security kit ==="
5
5
  echo ""
6
6
 
7
- PASS=0
8
- TOTAL=0
9
-
10
- check() {
11
- TOTAL=$((TOTAL + 1))
12
- if command -v "$1" &>/dev/null; then
13
- echo " ✓ $1 found ($(command -v "$1"))"
14
- PASS=$((PASS + 1))
15
- else
16
- echo " ✗ $1 not found (optional)"
17
- echo " Install: $2"
18
- fi
19
- }
7
+ source "$(dirname "$0")/../../base/lib/kit-check.sh"
20
8
 
21
9
  echo "Checking optional CLI tools..."
22
10
  echo ""
23
11
 
24
- check "trivy" "brew install trivy OR https://aquasecurity.github.io/trivy"
25
- check "deepsource" "curl -fsSL https://cli.deepsource.com/install | sh"
12
+ check "trivy" "brew install trivy OR https://aquasecurity.github.io/trivy" "optional"
13
+ check "deepsource" "curl -fsSL https://cli.deepsource.com/install | sh" "optional"
26
14
  check "rg" "brew install ripgrep OR apt-get install ripgrep"
27
15
 
28
- echo ""
29
- echo " $PASS/$TOTAL tools found"
30
- echo ""
16
+ kit_check_summary
31
17
 
18
+ echo ""
32
19
  echo "Note: This kit uses Claude's built-in analysis for most checks."
33
20
  echo "External tools enhance scanning but are not required."
34
21
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esoteric-logic/praxis-harness",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "Layered Claude Code harness — workflow discipline, AI-Kits, persistent vault integration",
5
5
  "bin": {
6
6
  "praxis-harness": "./bin/praxis.js"
@@ -20,24 +20,9 @@ set -euo pipefail
20
20
  CLAUDE_DIR="${CLAUDE_DIR:-$HOME/.claude}"
21
21
  CONFIG_FILE="${CONFIG_FILE:-$CLAUDE_DIR/praxis.config.json}"
22
22
 
23
- # ─── Colors (safe defaults if not already set) ───
24
- RED="${RED:-\033[0;31m}"
25
- GREEN="${GREEN:-\033[0;32m}"
26
- YELLOW="${YELLOW:-\033[0;33m}"
27
- CYAN="${CYAN:-\033[0;36m}"
28
- BOLD="${BOLD:-\033[1m}"
29
- NC="${NC:-\033[0m}"
30
-
31
- # ─── Output helpers (no-op if already defined by install.sh) ───
32
- if ! declare -f ok &>/dev/null; then
33
- ok() { echo -e " $GREEN✓$NC $1"; }
34
- fi
35
- if ! declare -f warn &>/dev/null; then
36
- warn() { echo -e " $YELLOW⚠$NC $1"; }
37
- fi
38
- if ! declare -f fail &>/dev/null; then
39
- fail() { echo -e " $RED✗$NC $1"; }
40
- fi
23
+ # ─── Colors & output helpers (safe to source multiple times) ───
24
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
25
+ source "$SCRIPT_DIR/../base/lib/output.sh"
41
26
 
42
27
  # ═══════════════════════════════════════════
43
28
  # Utilities