@esoteric-logic/praxis-harness 2.6.0 → 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.
@@ -0,0 +1,126 @@
1
+ # Polyglot Lint Stack — 3-Layer CI Template
2
+ # Copy to: .github/workflows/lint-stack.yml
3
+ # Conditional: each tool runs only if its language files exist in the repo.
4
+ # Pin actions to SHA — update hashes when upgrading versions.
5
+
6
+ name: Lint Stack
7
+ on:
8
+ pull_request:
9
+ branches: [main, develop]
10
+
11
+ permissions:
12
+ contents: read
13
+ pull-requests: write
14
+
15
+ jobs:
16
+ # ─────────────────────────────────────────────
17
+ # LAYER 1: Fast Feedback (~10s per tool)
18
+ # ─────────────────────────────────────────────
19
+ lint:
20
+ name: L1 — Lint + Format
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
24
+
25
+ # Python — Ruff
26
+ - name: Ruff check
27
+ if: hashFiles('**/*.py') != ''
28
+ uses: astral-sh/ruff-action@9700c1666704e06e2cf8f9046755e3dfc6297e40 # v3.2.1
29
+ with:
30
+ args: "check"
31
+ - name: Ruff format check
32
+ if: hashFiles('**/*.py') != ''
33
+ uses: astral-sh/ruff-action@9700c1666704e06e2cf8f9046755e3dfc6297e40 # v3.2.1
34
+ with:
35
+ args: "format --check"
36
+
37
+ # JS/TS — Biome
38
+ - name: Setup Biome
39
+ if: hashFiles('**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx') != ''
40
+ uses: biomejs/setup-biome@1cbe33ead22c7a2fded3b52fa2893611c815c3d2 # v2.5.0
41
+ - name: Biome CI
42
+ if: hashFiles('**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx') != ''
43
+ run: biome ci .
44
+
45
+ # Go — golangci-lint
46
+ - name: golangci-lint
47
+ if: hashFiles('go.mod') != ''
48
+ uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
49
+ with:
50
+ version: v2.1
51
+
52
+ # Rust — Clippy
53
+ - name: Setup Rust toolchain
54
+ if: hashFiles('Cargo.toml') != ''
55
+ uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # stable
56
+ with:
57
+ toolchain: stable
58
+ components: clippy, rustfmt
59
+ - name: Clippy
60
+ if: hashFiles('Cargo.toml') != ''
61
+ run: cargo clippy --all-targets --all-features -- -D warnings
62
+ - name: rustfmt check
63
+ if: hashFiles('Cargo.toml') != ''
64
+ run: cargo fmt --all -- --check
65
+
66
+ # Shell — ShellCheck
67
+ - name: ShellCheck
68
+ if: hashFiles('**/*.sh') != ''
69
+ run: |
70
+ sudo apt-get install -y shellcheck
71
+ find . -name '*.sh' -not -path './node_modules/*' -not -path './vendor/*' | xargs shellcheck -s bash
72
+
73
+ # ─────────────────────────────────────────────
74
+ # LAYER 2: Quality Gates (~2min)
75
+ # ─────────────────────────────────────────────
76
+ security-scan:
77
+ name: L2 — Semgrep
78
+ runs-on: ubuntu-latest
79
+ needs: lint
80
+ steps:
81
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
82
+ - uses: semgrep/semgrep-action@713efdd345ae26c72e79b5b32927be8e0e6bab83 # v1
83
+ with:
84
+ config: >-
85
+ p/default
86
+ p/secrets
87
+ p/owasp-top-ten
88
+ env:
89
+ SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
90
+
91
+ # Uncomment to add SonarQube quality gate (requires self-hosted server)
92
+ # sonarqube:
93
+ # name: L2 — SonarQube
94
+ # runs-on: ubuntu-latest
95
+ # needs: lint
96
+ # steps:
97
+ # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
98
+ # with:
99
+ # fetch-depth: 0
100
+ # - uses: SonarSource/sonarqube-scan-action@0e1a25e90571a34e2ec5c72ee40ba45cc73a1e6e # v4
101
+ # env:
102
+ # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
103
+ # SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
104
+ # - uses: SonarSource/sonarqube-quality-gate-action@dc2f7b0dd95544cd550de3066f25f47e3fc20894 # v1.1.0
105
+ # timeout-minutes: 5
106
+ # env:
107
+ # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
108
+
109
+ # ─────────────────────────────────────────────
110
+ # LAYER 3: AI Review (~2min, optional)
111
+ # ─────────────────────────────────────────────
112
+ # Uncomment to enable AI-powered PR review.
113
+ # Requires OPENAI_API_KEY secret or self-hosted Ollama.
114
+ #
115
+ # ai-review:
116
+ # name: L3 — AI Review
117
+ # runs-on: ubuntu-latest
118
+ # needs: [security-scan]
119
+ # steps:
120
+ # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
121
+ # - uses: Codium-ai/pr-agent@main
122
+ # env:
123
+ # OPENAI_KEY: ${{ secrets.OPENAI_API_KEY }}
124
+ # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
125
+ # with:
126
+ # command: review
@@ -0,0 +1,7 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD024": { "siblings_only": true },
5
+ "MD033": false,
6
+ "MD041": true
7
+ }
@@ -0,0 +1,63 @@
1
+ repos:
2
+ # General file hygiene
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v5.0.0
5
+ hooks:
6
+ - id: trailing-whitespace
7
+ - id: end-of-file-fixer
8
+ - id: check-yaml
9
+ - id: check-json
10
+ - id: check-added-large-files
11
+ args: ['--maxkb=500']
12
+ - id: detect-private-key
13
+ - id: check-merge-conflict
14
+
15
+ # Layer 1: Python — Ruff
16
+ - repo: https://github.com/astral-sh/ruff-pre-commit
17
+ rev: v0.11.6
18
+ hooks:
19
+ - id: ruff
20
+ args: [--fix]
21
+ - id: ruff-format
22
+
23
+ # Layer 1: JS/TS — Biome
24
+ - repo: https://github.com/biomejs/pre-commit
25
+ rev: v2.0.0
26
+ hooks:
27
+ - id: biome-check
28
+ additional_dependencies: ["@biomejs/biome@2.0.0"]
29
+
30
+ # Layer 1: Shell — ShellCheck
31
+ - repo: https://github.com/shellcheck-py/shellcheck-py
32
+ rev: v0.10.0.1
33
+ hooks:
34
+ - id: shellcheck
35
+ args: [-s, bash]
36
+
37
+ # Layer 1: Go — golangci-lint
38
+ - repo: https://github.com/golangci/golangci-lint
39
+ rev: v2.1.0
40
+ hooks:
41
+ - id: golangci-lint
42
+
43
+ # Layer 1: Rust — rustfmt + clippy
44
+ - repo: local
45
+ hooks:
46
+ - id: rustfmt
47
+ name: rustfmt
48
+ entry: rustfmt
49
+ language: system
50
+ types: [rust]
51
+ - id: clippy
52
+ name: clippy
53
+ entry: cargo clippy -- -D warnings
54
+ language: system
55
+ types: [rust]
56
+ pass_filenames: false
57
+
58
+ # Layer 2: Semgrep security scan
59
+ - repo: https://github.com/semgrep/semgrep
60
+ rev: v1.120.0
61
+ hooks:
62
+ - id: semgrep
63
+ args: ['--config', 'auto', '--error']
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
3
+ "organizeImports": { "enabled": true },
4
+ "linter": {
5
+ "enabled": true,
6
+ "rules": {
7
+ "recommended": true,
8
+ "complexity": {
9
+ "noExcessiveCognitiveComplexity": {
10
+ "level": "warn",
11
+ "options": { "maxAllowedComplexity": 15 }
12
+ }
13
+ },
14
+ "suspicious": {
15
+ "noExplicitAny": "warn",
16
+ "noConsoleLog": "warn"
17
+ },
18
+ "correctness": {
19
+ "noUnusedVariables": "error",
20
+ "noUnusedImports": "error"
21
+ }
22
+ }
23
+ },
24
+ "formatter": {
25
+ "enabled": true,
26
+ "indentStyle": "space",
27
+ "indentWidth": 2,
28
+ "lineWidth": 100
29
+ },
30
+ "javascript": {
31
+ "formatter": {
32
+ "quoteStyle": "single",
33
+ "semicolons": "always"
34
+ }
35
+ },
36
+ "json": {
37
+ "formatter": {
38
+ "indentWidth": 2
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,4 @@
1
+ cognitive-complexity-threshold = 20
2
+ too-many-arguments-threshold = 7
3
+ type-complexity-threshold = 250
4
+ single-char-binding-names-threshold = 4
@@ -0,0 +1,26 @@
1
+ target-version = "py312"
2
+ line-length = 100
3
+ fix = true
4
+
5
+ [lint]
6
+ select = [
7
+ "E", "W", # pycodestyle
8
+ "F", # pyflakes
9
+ "I", # isort
10
+ "N", # pep8-naming
11
+ "UP", # pyupgrade
12
+ "S", # bandit (security)
13
+ "B", # flake8-bugbear
14
+ "A", # flake8-builtins
15
+ "C4", # flake8-comprehensions
16
+ "SIM", # flake8-simplify
17
+ "RUF", # ruff-specific
18
+ ]
19
+ ignore = ["E501"] # line length handled by formatter
20
+
21
+ [lint.per-file-ignores]
22
+ "tests/**" = ["S101"] # allow assert in tests
23
+
24
+ [format]
25
+ quote-style = "double"
26
+ indent-style = "space"
@@ -0,0 +1,7 @@
1
+ edition = "2024"
2
+ max_width = 100
3
+ tab_spaces = 4
4
+ use_field_init_shorthand = true
5
+ use_try_shorthand = true
6
+ imports_granularity = "Crate"
7
+ group_imports = "StdExternalCrate"
@@ -0,0 +1,65 @@
1
+ rules:
2
+ # Usage:
3
+ # semgrep --config auto (recommended community rules)
4
+ # semgrep --config p/owasp-top-ten (OWASP Top 10)
5
+ # semgrep --config p/secrets (secret detection)
6
+ # semgrep --config .semgrep.yml (this file — custom rules)
7
+
8
+ # Custom: ban f-string SQL queries
9
+ - id: no-fstring-sql-execute
10
+ patterns:
11
+ - pattern: cursor.execute($QUERY, ...)
12
+ - pattern-not: cursor.execute("...", ...)
13
+ - metavariable-pattern:
14
+ metavariable: $QUERY
15
+ pattern: |
16
+ f"..."
17
+ message: "Use parameterized queries instead of f-strings in SQL"
18
+ severity: ERROR
19
+ languages: [python]
20
+
21
+ # Custom: no hardcoded secrets in source
22
+ - id: hardcoded-secret-assignment
23
+ pattern-either:
24
+ - pattern: $KEY = "..."
25
+ metavariable-regex:
26
+ metavariable: $KEY
27
+ regex: ".*(secret|password|token|api_key|apikey|auth_token).*"
28
+ message: "Possible hardcoded secret — use environment variables"
29
+ severity: WARNING
30
+ languages: [python, javascript, typescript, java, go, rust]
31
+
32
+ # Custom: no unsafe unwrap in Rust production code
33
+ - id: rust-no-unwrap
34
+ pattern: $X.unwrap()
35
+ message: "Avoid .unwrap() — use .expect() with context or propagate with ?"
36
+ severity: WARNING
37
+ languages: [rust]
38
+ paths:
39
+ exclude:
40
+ - "*_test.rs"
41
+ - "tests/**"
42
+ - "benches/**"
43
+
44
+ # Custom: no fmt.Println in Go production code
45
+ - id: go-no-fmt-println
46
+ pattern: fmt.Println(...)
47
+ message: "Use structured logging instead of fmt.Println"
48
+ severity: WARNING
49
+ languages: [go]
50
+ paths:
51
+ exclude:
52
+ - "*_test.go"
53
+ - "cmd/**"
54
+
55
+ # Paths to exclude from scanning
56
+ paths:
57
+ exclude:
58
+ - node_modules
59
+ - vendor
60
+ - .terraform
61
+ - target
62
+ - dist
63
+ - build
64
+ - "*.min.js"
65
+ - "*.generated.*"
@@ -30,7 +30,15 @@
30
30
  {"name": "gitleaks", "feature": "secret-scanning", "install": "brew install gitleaks"},
31
31
  {"name": "osv-scanner", "feature": "dep-audit", "install": "go install github.com/google/osv-scanner/cmd/osv-scanner@latest"},
32
32
  {"name": "pip-audit", "feature": "dep-audit-python", "install": "pip install pip-audit"},
33
- {"name": "docker", "feature": "docker-sandbox", "install": "brew install --cask docker"}
33
+ {"name": "docker", "feature": "docker-sandbox", "install": "brew install --cask docker"},
34
+ {"name": "biome", "feature": "lint-js", "install": "npm install -g @biomejs/biome"},
35
+ {"name": "ruff", "feature": "lint-python", "install": "pip install ruff"},
36
+ {"name": "semgrep", "feature": "security-scan", "install": "pip install semgrep"},
37
+ {"name": "markdownlint", "feature": "lint-markdown", "install": "npm install -g markdownlint-cli"},
38
+ {"name": "pre-commit", "feature": "pre-commit-hooks", "install": "pip install pre-commit"},
39
+ {"name": "golangci-lint", "feature": "lint-go", "install": "brew install golangci-lint"},
40
+ {"name": "rustfmt", "feature": "format-rust", "install": "rustup component add rustfmt"},
41
+ {"name": "clippy", "feature": "lint-rust", "install": "rustup component add clippy"}
34
42
  ]
35
43
  },
36
44
  "env_vars": {
@@ -61,6 +61,18 @@ case "$EXT" in
61
61
  taplo format "$FILE_PATH" 2>/dev/null
62
62
  fi
63
63
  ;;
64
+ js|jsx|ts|tsx)
65
+ if command -v biome &>/dev/null; then
66
+ biome format --write "$FILE_PATH" 2>/dev/null
67
+ elif command -v prettier &>/dev/null; then
68
+ prettier --write "$FILE_PATH" 2>/dev/null
69
+ fi
70
+ ;;
71
+ rs)
72
+ if command -v rustfmt &>/dev/null; then
73
+ rustfmt "$FILE_PATH" 2>/dev/null
74
+ fi
75
+ ;;
64
76
  md)
65
77
  if command -v prettier &>/dev/null; then
66
78
  prettier --write --prose-wrap always "$FILE_PATH" 2>/dev/null
@@ -111,6 +123,30 @@ case "$EXT" in
111
123
  fi
112
124
  fi
113
125
  ;;
126
+ js|jsx|ts|tsx)
127
+ if command -v biome &>/dev/null; then
128
+ LINT_OUT=$(biome lint "$FILE_PATH" 2>&1) || true
129
+ if [ -n "$LINT_OUT" ] && ! echo "$LINT_OUT" | grep -q "No diagnostics"; then
130
+ ISSUES+=("biome: $LINT_OUT")
131
+ fi
132
+ fi
133
+ ;;
134
+ rs)
135
+ if command -v cargo &>/dev/null; then
136
+ DIR=$(dirname "$FILE_PATH")
137
+ # Walk up to find Cargo.toml for crate context
138
+ CRATE_DIR="$DIR"
139
+ while [ "$CRATE_DIR" != "/" ] && [ ! -f "$CRATE_DIR/Cargo.toml" ]; do
140
+ CRATE_DIR=$(dirname "$CRATE_DIR")
141
+ done
142
+ if [ -f "$CRATE_DIR/Cargo.toml" ]; then
143
+ LINT_OUT=$(cd "$CRATE_DIR" && cargo clippy --quiet -- -D warnings 2>&1) || true
144
+ if [ -n "$LINT_OUT" ]; then
145
+ ISSUES+=("clippy: $LINT_OUT")
146
+ fi
147
+ fi
148
+ fi
149
+ ;;
114
150
  yml|yaml)
115
151
  if command -v yamllint &>/dev/null; then
116
152
  LINT_OUT=$(yamllint -f parsable "$FILE_PATH" 2>&1) || true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esoteric-logic/praxis-harness",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "Layered Claude Code harness — workflow discipline, AI-Kits, persistent vault integration",
5
5
  "bin": {
6
6
  "praxis-harness": "./bin/praxis.js"