@esoteric-logic/praxis-harness 2.5.1 → 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.
Files changed (47) hide show
  1. package/base/CLAUDE.md +17 -1
  2. package/base/configs/ci/lint-stack.yml +126 -0
  3. package/base/configs/linters/.markdownlint.json +7 -0
  4. package/base/configs/linters/.pre-commit-config.yaml +63 -0
  5. package/base/configs/linters/biome.json +41 -0
  6. package/base/configs/linters/clippy.toml +4 -0
  7. package/base/configs/linters/ruff.toml +26 -0
  8. package/base/configs/linters/rustfmt.toml +7 -0
  9. package/base/configs/linters/semgrep.yml +65 -0
  10. package/base/configs/mcp-servers.json +26 -0
  11. package/base/configs/registry.json +121 -0
  12. package/base/hooks/credential-guard.sh +98 -0
  13. package/base/hooks/dep-audit.sh +105 -0
  14. package/base/hooks/quality-check.sh +40 -3
  15. package/base/hooks/recursion-guard.sh +76 -0
  16. package/base/hooks/session-data-collect.sh +3 -0
  17. package/base/hooks/settings-hooks.json +18 -0
  18. package/base/rules/coding.md +2 -2
  19. package/base/rules/dependency-freshness.md +50 -0
  20. package/base/rules/live-docs-required.md +81 -0
  21. package/base/rules/security-posture.md +62 -0
  22. package/base/skills/blind-judge/SKILL.md +88 -0
  23. package/base/skills/context7-lookup/SKILL.md +17 -6
  24. package/base/skills/deliberate/SKILL.md +103 -0
  25. package/base/skills/dep-hygiene/SKILL.md +93 -0
  26. package/base/skills/pre-commit-lint/SKILL.md +2 -3
  27. package/base/skills/research/SKILL.md +106 -0
  28. package/base/skills/scaffold-new/SKILL.md +1 -4
  29. package/base/skills/scaffold-new/references/repo-CLAUDE-md-template.md +1 -1
  30. package/bin/praxis-preflight.sh +317 -0
  31. package/package.json +1 -1
  32. package/scripts/install-tools.sh +3 -23
  33. package/scripts/lint-harness.sh +1 -1
  34. package/scripts/test-harness.sh +1 -26
  35. package/base/configs/vale/.vale.ini +0 -17
  36. package/base/configs/vale/Praxis/AISlop.yml +0 -90
  37. package/base/configs/vale/Praxis/CopulaAvoidance.yml +0 -22
  38. package/base/configs/vale/Praxis/ElegantVariation.yml +0 -14
  39. package/base/configs/vale/Praxis/HeadingCase.yml +0 -26
  40. package/base/configs/vale/Praxis/Hedging.yml +0 -22
  41. package/base/configs/vale/Praxis/NaturalVoice.yml +0 -85
  42. package/base/configs/vale/Praxis/Precision.yml +0 -60
  43. package/base/configs/vale/Praxis/SentenceLength.yml +0 -6
  44. package/base/configs/vale/Praxis/Terminology.yml +0 -25
  45. package/base/configs/vale/Praxis/vocabularies/accept.txt +0 -32
  46. package/base/configs/vale/Praxis/vocabularies/reject.txt +0 -3
  47. package/base/skills/prose-review/SKILL.md +0 -165
package/base/CLAUDE.md CHANGED
@@ -134,7 +134,7 @@ Kit manifests live in `~/.claude/kits/<name>/KIT.md`.
134
134
 
135
135
  ## Rules Registry — Load on Demand Only
136
136
 
137
- ### Universal — always active (7 rules)
137
+ ### Universal — always active (8 rules)
138
138
  | File | Purpose |
139
139
  |------|---------|
140
140
  | `~/.claude/rules/profile.md` | Who the user is, identities, working style |
@@ -144,6 +144,7 @@ Kit manifests live in `~/.claude/kits/<name>/KIT.md`.
144
144
  | `~/.claude/rules/vault.md` | Second brain integration — vault backend, file purposes |
145
145
  | `~/.claude/rules/context-management.md` | Context anti-rot, phase scoping, context reset protocol |
146
146
  | `~/.claude/rules/memory-boundary.md` | Auto-memory boundary, MEMORY.md cap, dream integration |
147
+ | `~/.claude/rules/security-posture.md` | Sandbox model, credential protection, protected paths |
147
148
 
148
149
  ### Scoped — load only when paths match
149
150
  | File | Loads when |
@@ -152,9 +153,24 @@ Kit manifests live in `~/.claude/kits/<name>/KIT.md`.
152
153
  | `~/.claude/rules/terraform.md` | `**/*.tf`, `**/*.tfvars` |
153
154
  | `~/.claude/rules/github-actions.md` | `.github/workflows/**` |
154
155
  | `~/.claude/rules/powershell.md` | `**/*.ps1`, `**/*.psm1` |
156
+ | `~/.claude/rules/dependency-freshness.md` | `package.json`, `go.mod`, `requirements.txt`, `Cargo.toml`, `pyproject.toml` |
157
+ | `~/.claude/rules/live-docs-required.md` | Dependency manifests, files importing external packages |
155
158
 
156
159
  ### Auto-invocable skills (replace former universal rules)
157
160
  | Skill | Triggers when |
158
161
  |-------|--------------|
159
162
  | `communication-standards` | Writing client-facing docs, proposals, status reports, commits, PRs |
160
163
  | `architecture-patterns` | Writing ADRs, specs, system design, risk docs, blocker reports |
164
+
165
+ ## Judgment & Research Commands
166
+
167
+ | Command | Purpose |
168
+ |---------|---------|
169
+ | `/duel` | Parallel Alpha/Beta implementation → blind scoring → synthesis |
170
+ | `/deliberate` | Multi-perspective decision analysis with scored option matrix |
171
+ | `/freshness` | Full dependency audit — CVEs, outdated packages, maintenance status |
172
+ | `/research <pkg>` | Live docs (Context7) + CVE/version/maintenance check (Perplexity Sonar) |
173
+
174
+ MCP server templates: `base/configs/mcp-servers.json` — declarative config for context7, github, perplexity-sonar.
175
+ Dependency registry: `base/configs/registry.json` — single source of truth for all tools, auth, hooks.
176
+ Preflight gate: `bin/praxis-preflight.sh` (alias: `praxis doctor`) — verifies auth, tools, MCP, keys.
@@ -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.*"
@@ -0,0 +1,26 @@
1
+ {
2
+ "mcpServers": {
3
+ "context7": {
4
+ "command": "npx",
5
+ "args": ["-y", "@upstash/context7-mcp@latest"],
6
+ "description": "Real-time library documentation — always use before generating code that touches any npm/PyPI/crate dependency"
7
+ },
8
+ "github": {
9
+ "command": "npx",
10
+ "args": ["-y", "@modelcontextprotocol/server-github"],
11
+ "env": {
12
+ "GITHUB_TOKEN": "${GITHUB_TOKEN}"
13
+ },
14
+ "description": "GitHub MCP — issues, PRs, repos. Token auto-set from gh auth."
15
+ },
16
+ "perplexity-sonar": {
17
+ "command": "npx",
18
+ "args": ["-y", "mcp-perplexity-server"],
19
+ "env": {
20
+ "PERPLEXITY_API_KEY": "${PERPLEXITY_API_KEY}",
21
+ "PERPLEXITY_MODEL": "sonar-pro"
22
+ },
23
+ "description": "Web search for CVEs, changelogs, security advisories. Default sonar-pro. Override per-call: sonar (fast), sonar-reasoning (/deliberate only)."
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,121 @@
1
+ {
2
+ "version": "2.0",
3
+ "auth": {
4
+ "claude": {
5
+ "check": "claude auth status",
6
+ "fix": "claude auth login",
7
+ "blocking": true,
8
+ "description": "Claude Code OAuth — browser login, no API key needed"
9
+ },
10
+ "github": {
11
+ "check": "gh auth status",
12
+ "fix": "gh auth login",
13
+ "fallback_env": "GITHUB_TOKEN",
14
+ "auto_populate": "gh auth token",
15
+ "blocking": false,
16
+ "feature": "github-mcp"
17
+ }
18
+ },
19
+ "binaries": {
20
+ "required": [
21
+ {"name": "claude", "minVersion": "1.0.0", "install": "npm install -g @anthropic-ai/claude-code"},
22
+ {"name": "node", "minVersion": "18.0.0", "install": "brew install node"},
23
+ {"name": "npm", "minVersion": "9.0.0", "install": "npm install -g npm@latest"},
24
+ {"name": "git", "minVersion": "2.30.0", "install": "brew install git"},
25
+ {"name": "jq", "minVersion": "1.6", "install": "brew install jq"},
26
+ {"name": "gh", "minVersion": "2.0.0", "install": "brew install gh"}
27
+ ],
28
+ "optional": [
29
+ {"name": "trufflehog", "feature": "secret-scanning", "install": "brew install trufflehog"},
30
+ {"name": "gitleaks", "feature": "secret-scanning", "install": "brew install gitleaks"},
31
+ {"name": "osv-scanner", "feature": "dep-audit", "install": "go install github.com/google/osv-scanner/cmd/osv-scanner@latest"},
32
+ {"name": "pip-audit", "feature": "dep-audit-python", "install": "pip install pip-audit"},
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"}
42
+ ]
43
+ },
44
+ "env_vars": {
45
+ "optional": [
46
+ {
47
+ "name": "PERPLEXITY_API_KEY",
48
+ "description": "Perplexity Sonar — live CVE lookups, dep research",
49
+ "validation": "starts_with:pplx-",
50
+ "secure_store": true,
51
+ "masked_preview": 8,
52
+ "feature": "live-research",
53
+ "get_key_url": "https://www.perplexity.ai/settings/api"
54
+ },
55
+ {
56
+ "name": "GITHUB_TOKEN",
57
+ "description": "Auto-populated from gh auth token — for GitHub MCP",
58
+ "validation": "starts_with:gh",
59
+ "secure_store": true,
60
+ "masked_preview": 8,
61
+ "feature": "github-mcp",
62
+ "auto_populate": "gh auth token"
63
+ },
64
+ {
65
+ "name": "OBSIDIAN_VAULT",
66
+ "description": "Absolute path to Obsidian vault for long-term memory",
67
+ "validation": "is_directory",
68
+ "secure_store": false,
69
+ "feature": "obsidian-memory"
70
+ }
71
+ ]
72
+ },
73
+ "git_config": {
74
+ "required": [
75
+ {"key": "user.name", "description": "git commit author name"},
76
+ {"key": "user.email", "description": "git commit author email"}
77
+ ]
78
+ },
79
+ "mcp_servers": [
80
+ {
81
+ "name": "context7",
82
+ "package": "@upstash/context7-mcp@latest",
83
+ "requires_env": [],
84
+ "zero_config": true,
85
+ "feature": "live-docs"
86
+ },
87
+ {
88
+ "name": "github",
89
+ "package": "@modelcontextprotocol/server-github",
90
+ "requires_env": ["GITHUB_TOKEN"],
91
+ "feature": "github-mcp"
92
+ },
93
+ {
94
+ "name": "perplexity-sonar",
95
+ "package": "mcp-perplexity-server",
96
+ "requires_env": ["PERPLEXITY_API_KEY"],
97
+ "env_config": {"PERPLEXITY_MODEL": "sonar-pro"},
98
+ "feature": "live-research"
99
+ }
100
+ ],
101
+ "hooks": {
102
+ "required": [
103
+ {"path": "base/hooks/secret-scan.sh", "event": "PreToolUse", "matcher": "Write|Edit|MultiEdit"},
104
+ {"path": "base/hooks/file-guard.sh", "event": "PreToolUse", "matcher": "Write|Edit|MultiEdit"},
105
+ {"path": "base/hooks/identity-check.sh", "event": "PreToolUse", "matcher": "Bash"},
106
+ {"path": "base/hooks/credential-guard.sh", "event": "PreToolUse", "matcher": "Bash"},
107
+ {"path": "base/hooks/quality-check.sh", "event": "PostToolUse", "matcher": "Write|Edit|MultiEdit"},
108
+ {"path": "base/hooks/session-data-collect.sh", "event": "Stop", "matcher": ""}
109
+ ],
110
+ "optional": [
111
+ {"path": "base/hooks/recursion-guard.sh", "event": "PreToolUse", "matcher": "", "feature": "recursion-detection"},
112
+ {"path": "base/hooks/dep-audit.sh", "event": "PostToolUse", "matcher": "Write|Edit|MultiEdit", "feature": "dep-audit"}
113
+ ]
114
+ },
115
+ "claude_settings": {
116
+ "required": [
117
+ {"path": "sandbox.failIfUnavailable", "expected": true},
118
+ {"path": "sandbox.allowUnsandboxedCommands", "expected": false}
119
+ ]
120
+ }
121
+ }
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env bash
2
+ # credential-guard.sh — PreToolUse:Bash hook
3
+ # Blocks Bash commands that access credential files or sensitive directories.
4
+ # Exit 0 = allow, Exit 2 = block with message.
5
+ set -euo pipefail
6
+
7
+ INPUT=$(cat)
8
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
9
+
10
+ if [[ -z "$COMMAND" ]]; then
11
+ exit 0
12
+ fi
13
+
14
+ # ── Allowlist: verification commands that reference credential paths safely ──
15
+ SAFE_PATTERNS=(
16
+ "ssh-keygen -l"
17
+ "ssh-keygen -lf"
18
+ "gh auth status"
19
+ "gh auth token"
20
+ "gh auth switch"
21
+ "aws sts get-caller-identity"
22
+ "az account show"
23
+ "gcloud auth list"
24
+ "kubectl config current-context"
25
+ "docker login --help"
26
+ "gpg --list-keys"
27
+ "security find-identity"
28
+ )
29
+
30
+ for safe in "${SAFE_PATTERNS[@]}"; do
31
+ if echo "$COMMAND" | grep -qF "$safe"; then
32
+ exit 0
33
+ fi
34
+ done
35
+
36
+ # ── Blocked paths: credential stores and private key locations ──
37
+ BLOCKED_PATHS=(
38
+ "$HOME/.ssh"
39
+ "$HOME/.aws"
40
+ "$HOME/.azure"
41
+ "$HOME/.kube"
42
+ "$HOME/.gnupg"
43
+ "$HOME/.docker/config.json"
44
+ "$HOME/Library/Keychains"
45
+ "$HOME/.config/gcloud"
46
+ "$HOME/.praxis/secrets"
47
+ )
48
+
49
+ # Expand ~ in commands for matching
50
+ EXPANDED_CMD=$(echo "$COMMAND" | sed "s|~|$HOME|g")
51
+
52
+ for blocked in "${BLOCKED_PATHS[@]}"; do
53
+ if echo "$EXPANDED_CMD" | grep -qF "$blocked"; then
54
+ echo "BLOCKED: Command references protected credential path: $blocked" >&2
55
+ echo "Praxis credential-guard prevents access to sensitive directories." >&2
56
+ echo "If this is a legitimate operation, use the allowlisted verification commands." >&2
57
+
58
+ # Log to audit trail
59
+ AUDIT_FILE="$HOME/.claude/praxis-audit.jsonl"
60
+ if command -v jq &>/dev/null; then
61
+ jq -nc \
62
+ --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
63
+ --arg tool "Bash" \
64
+ --arg event "credential-guard-block" \
65
+ --arg path "$blocked" \
66
+ --arg cmd "${COMMAND:0:200}" \
67
+ --arg session "$PPID" \
68
+ '{ts: $ts, tool: $tool, event: $event, blocked_path: $path, command_prefix: $cmd, session_id: $session}' \
69
+ >> "$AUDIT_FILE" 2>/dev/null || true
70
+ fi
71
+
72
+ exit 2
73
+ fi
74
+ done
75
+
76
+ # ── Blocked file patterns: private keys and env files ──
77
+ BLOCKED_FILE_PATTERNS=(
78
+ "_rsa[[:space:]]"
79
+ "_rsa$"
80
+ "_ed25519[[:space:]]"
81
+ "_ed25519$"
82
+ "\.pem[[:space:]]"
83
+ "\.pem$"
84
+ "\.p12[[:space:]]"
85
+ "\.p12$"
86
+ "\.pfx[[:space:]]"
87
+ "\.pfx$"
88
+ )
89
+
90
+ for pattern in "${BLOCKED_FILE_PATTERNS[@]}"; do
91
+ if echo "$EXPANDED_CMD" | grep -qE "$pattern"; then
92
+ echo "BLOCKED: Command references private key or certificate file pattern." >&2
93
+ echo "Praxis credential-guard prevents access to private key files." >&2
94
+ exit 2
95
+ fi
96
+ done
97
+
98
+ exit 0