@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.
- package/base/CLAUDE.md +17 -1
- package/base/configs/ci/lint-stack.yml +126 -0
- package/base/configs/linters/.markdownlint.json +7 -0
- package/base/configs/linters/.pre-commit-config.yaml +63 -0
- package/base/configs/linters/biome.json +41 -0
- package/base/configs/linters/clippy.toml +4 -0
- package/base/configs/linters/ruff.toml +26 -0
- package/base/configs/linters/rustfmt.toml +7 -0
- package/base/configs/linters/semgrep.yml +65 -0
- package/base/configs/mcp-servers.json +26 -0
- package/base/configs/registry.json +121 -0
- package/base/hooks/credential-guard.sh +98 -0
- package/base/hooks/dep-audit.sh +105 -0
- package/base/hooks/quality-check.sh +40 -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
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 (
|
|
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,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,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,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
|