@esoteric-logic/praxis-harness 2.11.0 → 2.12.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 +14 -1
- package/base/hooks/auto-format.sh +1 -1
- package/base/hooks/dep-audit.sh +1 -1
- package/base/hooks/file-guard.sh +3 -3
- package/base/hooks/recursion-guard.sh +7 -1
- package/base/hooks/session-data-collect.sh +1 -1
- package/base/hooks/vault-checkpoint.sh +5 -5
- package/base/rules/code-excellence.md +22 -0
- package/base/rules/coding.md +16 -0
- package/base/rules/observable-code.md +87 -0
- package/base/rules/refactor-triggers.md +59 -0
- package/base/rules/writing-quality.md +122 -0
- package/base/skills/px-complexity-audit/SKILL.md +118 -0
- package/base/skills/px-discover/SKILL.md +4 -1
- package/base/skills/px-discuss/SKILL.md +4 -1
- package/base/skills/px-doc-lint/SKILL.md +107 -0
- package/base/skills/px-prose-review/SKILL.md +96 -0
- package/base/skills/px-quality-gate/SKILL.md +182 -0
- package/base/skills/px-risk/SKILL.md +4 -1
- package/base/skills/px-scaffold-new/SKILL.md +16 -14
- package/base/skills/px-session-retro/SKILL.md +1 -1
- package/base/skills/px-spec/SKILL.md +6 -2
- package/base/skills/px-verify/SKILL.md +2 -1
- package/bin/praxis.js +27 -6
- package/kits/api/install.sh +1 -1
- package/kits/api/teardown.sh +1 -1
- package/kits/code-quality/hooks/generate-baseline.sh +1 -1
- package/kits/code-quality/hooks/post-commit.sh +3 -2
- package/kits/code-quality/hooks/pre-push.sh +15 -15
- package/kits/code-quality/install.sh +1 -1
- package/kits/code-quality/teardown.sh +3 -3
- package/kits/data/install.sh +1 -1
- package/kits/data/teardown.sh +1 -1
- package/kits/infrastructure/install.sh +1 -1
- package/kits/infrastructure/teardown.sh +1 -1
- package/kits/security/install.sh +1 -1
- package/kits/security/teardown.sh +1 -1
- package/kits/web-designer/install.sh +1 -1
- package/kits/web-designer/teardown.sh +1 -1
- package/package.json +1 -1
- package/scripts/health-check.sh +21 -15
- package/scripts/install-tools.sh +5 -5
- package/scripts/lint-harness.sh +1 -1
- package/scripts/onboard-mcp.sh +1 -1
- package/scripts/test-harness.sh +1 -1
- package/scripts/update.sh +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: px-doc-lint
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: "Fast structural markdown check. No subagent — pure pattern matching. Completes in under 5 seconds. Fires inside px-quality-gate for staged *.md files, or on-demand via /px-doc-lint."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# px-doc-lint — Fast Structural Markdown Check
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Lightweight structural validation for markdown files. No subagent.
|
|
12
|
+
Pure pattern matching. Completes in under 5 seconds.
|
|
13
|
+
Use as a quick pre-save check or let px-quality-gate invoke it automatically.
|
|
14
|
+
|
|
15
|
+
## When It Fires
|
|
16
|
+
|
|
17
|
+
1. Automatically inside px-quality-gate for every staged `*.md` file
|
|
18
|
+
2. On-demand: `/px-doc-lint {filepath}`
|
|
19
|
+
3. On-demand batch: `/px-doc-lint {directory}` (all .md files in directory)
|
|
20
|
+
|
|
21
|
+
## Checks by Document Type
|
|
22
|
+
|
|
23
|
+
### All markdown files
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Frontmatter check — if present, must be valid
|
|
27
|
+
head -1 "$f" | grep -q '^---$' && {
|
|
28
|
+
awk '/^---$/{c++} c==2{found=1; exit} END{if(!found) print "BLOCK: unclosed frontmatter"}' "$f"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# 2. No trailing whitespace
|
|
32
|
+
grep -nE ' +$' "$f" && echo "WARN: trailing whitespace"
|
|
33
|
+
|
|
34
|
+
# 3. No consecutive blank lines (more than 2)
|
|
35
|
+
awk '/^$/{blank++; if(blank>2) print "WARN: line "NR": excessive blank lines"} /^.+$/{blank=0}' "$f"
|
|
36
|
+
|
|
37
|
+
# 4. Headers must increment by one level (no h1 -> h3 skip)
|
|
38
|
+
grep -E '^#{1,6} ' "$f" | awk '{
|
|
39
|
+
level = length($0) - length(gensub(/^#+/, "", 1, $0)) - 1
|
|
40
|
+
if (prev > 0 && level > prev + 1)
|
|
41
|
+
print "WARN: header level skip at: "$0
|
|
42
|
+
prev = level
|
|
43
|
+
}'
|
|
44
|
+
|
|
45
|
+
# 5. No empty headers
|
|
46
|
+
grep -nE '^#{1,6}\s*$' "$f" && echo "BLOCK: empty header"
|
|
47
|
+
|
|
48
|
+
# 6. Fluff kill list (same as px-quality-gate prose check)
|
|
49
|
+
grep -inE "$FLUFF_PATTERN" "$f" && echo "BLOCK: fluff phrases detected"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Design docs (DESIGN-*.md, *-design.md)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
for section in "## Problem" "## Decision" "## Tradeoffs" "## Acceptance Criteria"; do
|
|
56
|
+
grep -q "$section" "$f" || echo "BLOCK: missing required section: $section"
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
tradeoff_count=$(awk '/## Tradeoffs/,/## /' "$f" | grep -c '^- ' || true)
|
|
60
|
+
if [[ "$tradeoff_count" -lt 2 ]]; then
|
|
61
|
+
echo "BLOCK: Tradeoffs section needs at least 2 items (found: $tradeoff_count)"
|
|
62
|
+
fi
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### ADRs (ADR-*.md)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
for field in "Status:" "Date:" "## Context" "## Decision" "## Consequences"; do
|
|
69
|
+
grep -q "$field" "$f" || echo "BLOCK: missing required field: $field"
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
status=$(grep -oE 'Status: .*' "$f" | head -1)
|
|
73
|
+
echo "$status" | grep -qE '(Proposed|Accepted|Deprecated|Superseded)' || \
|
|
74
|
+
echo "BLOCK: invalid ADR status. Must be: Proposed | Accepted | Deprecated | Superseded by ADR-NNN"
|
|
75
|
+
|
|
76
|
+
grep -q '### Positive' "$f" || echo "BLOCK: missing Consequences > Positive"
|
|
77
|
+
grep -q '### Negative' "$f" || echo "BLOCK: missing Consequences > Negative"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### READMEs (README.md)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
for section in "## Install\|## Setup" "## Run" "## Test"; do
|
|
84
|
+
grep -qE "$section" "$f" || echo "BLOCK: missing required section matching: $section"
|
|
85
|
+
done
|
|
86
|
+
|
|
87
|
+
awk '/^```/,/^```/' "$f" | grep -E '\{placeholder\}|\{TODO\}|\{TBD\}' && \
|
|
88
|
+
echo "BLOCK: placeholder found inside code block"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Output Format
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
px-doc-lint: {filename}
|
|
95
|
+
Type: {Design Doc | ADR | README | General}
|
|
96
|
+
[PASS] frontmatter valid
|
|
97
|
+
[PASS] no trailing whitespace
|
|
98
|
+
[BLOCK] missing required section: ## Tradeoffs
|
|
99
|
+
[WARN] header level skip at: ### Details
|
|
100
|
+
Result: BLOCK (1 error, 1 warning)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Performance Contract
|
|
104
|
+
|
|
105
|
+
- Single file: under 2 seconds
|
|
106
|
+
- Batch (10 files): under 5 seconds
|
|
107
|
+
- No network calls. No subagent. Pure grep/awk/sed.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: px-prose-review
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: "Deep document review using an isolated subagent. Checks argument quality, clarity, completeness, and adherence to writing-quality.md. Use for design docs, ADRs, READMEs, specs. More thorough than px-doc-lint."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# px-prose-review — Deep Document Review
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Performs deep quality review on prose documents using an isolated subagent.
|
|
12
|
+
More thorough than px-doc-lint — checks argument quality, clarity, completeness,
|
|
13
|
+
and adherence to writing-quality.md standards.
|
|
14
|
+
|
|
15
|
+
## When It Fires
|
|
16
|
+
|
|
17
|
+
1. On-demand: `/px-prose-review {filepath}`
|
|
18
|
+
2. Automatic: during `/px-ship` against the PR description
|
|
19
|
+
3. Recommended: before finalizing any design doc, ADR, or spec
|
|
20
|
+
|
|
21
|
+
## Subagent Configuration
|
|
22
|
+
|
|
23
|
+
The review runs in an isolated context with zero conversation history.
|
|
24
|
+
The subagent receives ONLY the document content and the review instructions.
|
|
25
|
+
|
|
26
|
+
### Subagent persona
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
You are a ruthless technical editor. Your job is to make this document
|
|
30
|
+
shorter, clearer, and more precise. You have no loyalty to the author's
|
|
31
|
+
feelings. You have total loyalty to the reader's time.
|
|
32
|
+
|
|
33
|
+
Rules:
|
|
34
|
+
- Every sentence must earn its place. If it restates something already said, cut it.
|
|
35
|
+
- Every paragraph must have exactly one idea. If it has two, split it.
|
|
36
|
+
- Every claim must be specific. "Improves performance" is not a claim.
|
|
37
|
+
"Reduces p99 latency from 200ms to 50ms" is.
|
|
38
|
+
- Active voice on all decisions and actions.
|
|
39
|
+
- No fluff words (see kill list in writing-quality.md).
|
|
40
|
+
- No hedging on decided things. "will" not "should" or "might".
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The full fluff kill list from writing-quality.md is injected into the subagent prompt.
|
|
44
|
+
|
|
45
|
+
## Document Type Detection
|
|
46
|
+
|
|
47
|
+
The subagent detects document type from filename and content:
|
|
48
|
+
|
|
49
|
+
| Pattern | Type | Structural checks applied |
|
|
50
|
+
| ------- | ---- | ------------------------- |
|
|
51
|
+
| `DESIGN-*.md`, `*-design.md` | Design Doc | Problem, Decision, Tradeoffs, Acceptance Criteria |
|
|
52
|
+
| `ADR-*.md` | ADR | Status, Date, Context, Decision, Consequences (Positive + Negative) |
|
|
53
|
+
| `README.md` | README | First paragraph, Install/Setup, Run, Test |
|
|
54
|
+
| `*.md` in PR context | PR Description | What, Why, How to verify, Breaking changes |
|
|
55
|
+
| All other `*.md` | General prose | Sentence limits, fluff, voice, hedging |
|
|
56
|
+
|
|
57
|
+
## Review Output Format
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
## px-prose-review: {filename}
|
|
61
|
+
Type detected: {Design Doc | ADR | README | PR Description | General}
|
|
62
|
+
|
|
63
|
+
### Structure
|
|
64
|
+
- [PASS|BLOCK] {section}: {detail}
|
|
65
|
+
|
|
66
|
+
### Clarity
|
|
67
|
+
- Line {N}: {issue} → {suggested fix}
|
|
68
|
+
|
|
69
|
+
### Fluff
|
|
70
|
+
- Line {N}: "{flagged phrase}" → {replacement or "delete"}
|
|
71
|
+
|
|
72
|
+
### Voice
|
|
73
|
+
- Line {N}: passive on decision → {active rewrite}
|
|
74
|
+
|
|
75
|
+
### Verdict
|
|
76
|
+
{CLEAN | {N} issues found — {M} auto-fixable}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Auto-Fix Flow
|
|
80
|
+
|
|
81
|
+
For minor issues (fluff deletion, passive-to-active rewrites on clear cases):
|
|
82
|
+
1. Subagent proposes the fix with before/after
|
|
83
|
+
2. Operator is shown the diff
|
|
84
|
+
3. Operator approves or rejects each fix individually
|
|
85
|
+
4. Approved fixes are applied in-place
|
|
86
|
+
|
|
87
|
+
For structural issues (missing sections, incomplete analysis):
|
|
88
|
+
- Subagent flags the gap and describes what is needed
|
|
89
|
+
- Operator writes the content — subagent does NOT generate missing sections
|
|
90
|
+
- Rationale: the subagent lacks project context to write accurate content
|
|
91
|
+
|
|
92
|
+
## Limitations
|
|
93
|
+
|
|
94
|
+
- Does not verify technical accuracy of claims — only prose quality
|
|
95
|
+
- Does not check code blocks inside markdown — only surrounding prose
|
|
96
|
+
- Does not run on files outside the repo (external links, references)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: px-quality-gate
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: "Code style and prose quality gate. Checks what linters cannot: naming, doc completeness, prose clarity, structural patterns. Integrated into px-verify as Step 1 item 5b. Also available standalone via /px-quality-gate."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# px-quality-gate — Style and Prose Quality Enforcement
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Runs automated style checks against changed files. Catches issues that linters and
|
|
12
|
+
test suites do not: generic variable names, missing docstrings, prose quality,
|
|
13
|
+
structural violations, and import verification.
|
|
14
|
+
|
|
15
|
+
Integrated into `/px-verify` as Step 1 item 5b (after security scan, before functional check).
|
|
16
|
+
Also available standalone via `/px-quality-gate`.
|
|
17
|
+
|
|
18
|
+
## When It Fires
|
|
19
|
+
|
|
20
|
+
1. Automatically inside `/px-verify` after security scan
|
|
21
|
+
2. As the first step of `/px-ship`
|
|
22
|
+
3. On-demand via `/px-quality-gate` for ad-hoc checks
|
|
23
|
+
|
|
24
|
+
## Scope
|
|
25
|
+
|
|
26
|
+
Checks run against staged or changed files only (not the full codebase).
|
|
27
|
+
Use `/px-complexity-audit` for full codebase scans.
|
|
28
|
+
|
|
29
|
+
## Verdicts
|
|
30
|
+
|
|
31
|
+
- **BLOCK**: violation found — must fix before proceeding. Commit blocked.
|
|
32
|
+
- **WARN**: advisory issue found — commit allowed, fix recommended.
|
|
33
|
+
- **PASS**: all checks clear.
|
|
34
|
+
|
|
35
|
+
## Check Categories
|
|
36
|
+
|
|
37
|
+
### 1. Code Structure Checks
|
|
38
|
+
|
|
39
|
+
Run against all changed code files (exclude vendor/, node_modules/, .git/).
|
|
40
|
+
|
|
41
|
+
#### File size
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(go|ts|tsx|js|jsx|py|rs|java|sh|sql)$'); do
|
|
45
|
+
lines=$(wc -l < "$f")
|
|
46
|
+
if [[ "$lines" -gt 300 ]]; then
|
|
47
|
+
echo "BLOCK: $f is $lines lines (limit: 300). Split before committing."
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### TODO/FIXME/HACK detection
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(go|ts|tsx|js|jsx|py|rs|java|sh|sql)$'); do
|
|
56
|
+
hits=$(grep -nE 'TODO|FIXME|HACK' "$f" || true)
|
|
57
|
+
if [[ -n "$hits" ]]; then
|
|
58
|
+
echo "BLOCK: $f contains banned markers. Use QUALITY: with a ticket number instead."
|
|
59
|
+
echo "$hits"
|
|
60
|
+
fi
|
|
61
|
+
done
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Nesting depth heuristic
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(go|ts|tsx|js|jsx|py|rs|java)$'); do
|
|
68
|
+
deep=$(grep -nE '^\s{16,}\S|^\t{4,}\S' "$f" || true)
|
|
69
|
+
if [[ -n "$deep" ]]; then
|
|
70
|
+
echo "WARN: $f may have deep nesting. Review these lines:"
|
|
71
|
+
echo "$deep"
|
|
72
|
+
fi
|
|
73
|
+
done
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Generic variable names
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git diff --cached -U0 | grep '^+' | grep -v '^+++' | \
|
|
80
|
+
grep -oE '\b(data|result|info|temp|tmp|obj|val|item|stuff|thing|ret|res)\b' | \
|
|
81
|
+
sort | uniq -c | sort -rn | head -10
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If any appear 3+ times: WARN with suggestion to use domain-specific names.
|
|
85
|
+
|
|
86
|
+
#### Missing docstrings on new public functions
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git diff --cached -U3 | grep -E '^\+.*(func |def |export function |export const .* = )' | \
|
|
90
|
+
while read -r line; do
|
|
91
|
+
echo "CHECK: verify doc comment exists for: $line"
|
|
92
|
+
done
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 2. Prose Checks
|
|
96
|
+
|
|
97
|
+
Run against all changed markdown files.
|
|
98
|
+
|
|
99
|
+
#### AI fluff detection
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
FLUFF_PATTERN='leverage|utilize|facilitate|moving forward|going forward|at this point in time|comprehensive solution|robust solution|seamlessly|cutting-edge|best-in-class|in order to|due to the fact that|at the end of the day|synergy|holistic|empower|streamline'
|
|
103
|
+
|
|
104
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$'); do
|
|
105
|
+
hits=$(grep -inE "$FLUFF_PATTERN" "$f" || true)
|
|
106
|
+
if [[ -n "$hits" ]]; then
|
|
107
|
+
echo "BLOCK: $f contains fluff phrases. Remove or replace per writing-quality.md."
|
|
108
|
+
echo "$hits"
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Passive voice on decisions
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$'); do
|
|
117
|
+
hits=$(grep -inE '(it was decided|was implemented|was chosen|was selected|has been determined)' "$f" || true)
|
|
118
|
+
if [[ -n "$hits" ]]; then
|
|
119
|
+
echo "WARN: $f uses passive voice on decisions. Rewrite in active voice."
|
|
120
|
+
echo "$hits"
|
|
121
|
+
fi
|
|
122
|
+
done
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Sentence length check
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$'); do
|
|
129
|
+
awk 'BEGIN{RS="[.!?]"} NF>30 {print NR": "NF" words: "$0}' "$f" | head -5
|
|
130
|
+
done
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Unreplaced placeholders
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
for f in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$'); do
|
|
137
|
+
hits=$(grep -nE '\{placeholder\}|\{TODO\}|\{TBD\}|\[INSERT\]|\[TBD\]|XXX' "$f" || true)
|
|
138
|
+
if [[ -n "$hits" ]]; then
|
|
139
|
+
echo "BLOCK: $f contains unreplaced placeholders."
|
|
140
|
+
echo "$hits"
|
|
141
|
+
fi
|
|
142
|
+
done
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3. Context7 Import Check
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
NEW_IMPORTS=$(git diff --cached -U0 | grep -E '^\+.*(import |require\(|using |use )' | grep -v '^+++' | grep -v '^//\|^#')
|
|
149
|
+
|
|
150
|
+
if [[ -n "$NEW_IMPORTS" ]]; then
|
|
151
|
+
echo "GATE: New external imports detected. Each requires a Context7 lookup:"
|
|
152
|
+
echo "$NEW_IMPORTS"
|
|
153
|
+
echo ""
|
|
154
|
+
echo "Confirm each import was verified via Context7 in this session."
|
|
155
|
+
echo "Internal packages (same repo/module) are excluded."
|
|
156
|
+
fi
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Output Format
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
━━━ QUALITY GATE ━━━━━━━━━━━━━━━━━━━
|
|
163
|
+
Code checks: PASS | WARN | BLOCK
|
|
164
|
+
Prose checks: PASS | WARN | BLOCK
|
|
165
|
+
Import check: PASS | WARN | BLOCK
|
|
166
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
167
|
+
Overall: PASS | WARN | BLOCK
|
|
168
|
+
|
|
169
|
+
Details:
|
|
170
|
+
[BLOCK] services/auth.go: 342 lines (limit: 300)
|
|
171
|
+
[BLOCK] docs/DESIGN-auth.md: contains "comprehensive solution" (fluff)
|
|
172
|
+
[WARN] handlers/login.go: possible deep nesting at line 47
|
|
173
|
+
[PASS] All other checks
|
|
174
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Rules
|
|
178
|
+
|
|
179
|
+
- Any single BLOCK = overall BLOCK. Commit prevented.
|
|
180
|
+
- WARN-only = overall WARN. Commit allowed. Fix recommended.
|
|
181
|
+
- Gate re-runs on re-stage. Fix the issue, `git add`, gate runs again.
|
|
182
|
+
- Do NOT bypass the gate. There is no `--force` flag.
|
|
@@ -9,7 +9,10 @@ You are adding a risk register entry for the current project.
|
|
|
9
9
|
**Step 1 — Detect project and existing risks**
|
|
10
10
|
- Read vault_path from `~/.claude/praxis.config.json`
|
|
11
11
|
- Detect project from CWD
|
|
12
|
-
-
|
|
12
|
+
- Search vault for existing risks using configured backend:
|
|
13
|
+
- If `obsidian`: run `obsidian search query="risk register {project-slug}" limit=3`
|
|
14
|
+
- If `ripgrep`: run `rg --files-with-matches "risk" {vault_path}/specs/`
|
|
15
|
+
- If vault search fails: proceed without blocking
|
|
13
16
|
- Check if `{vault_path}/specs/risk-register.md` exists
|
|
14
17
|
- If found: read it to determine next sequential risk ID (R-01, R-02, etc.)
|
|
15
18
|
|
|
@@ -46,10 +46,10 @@ Read identity table from `~/.claude/rules/git-workflow.md`.
|
|
|
46
46
|
|
|
47
47
|
## Phase 1 — Gather Info
|
|
48
48
|
|
|
49
|
-
Before asking, run vault duplicate check:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
Before asking, run vault duplicate check using configured backend:
|
|
50
|
+
- If `obsidian`: `obsidian search query="{slug}" path="01_Projects" limit=3`
|
|
51
|
+
- If `ripgrep`: `rg --files-with-matches "{slug}" {vault_path}/`
|
|
52
|
+
- If vault search fails: warn and proceed without blocking.
|
|
53
53
|
|
|
54
54
|
Ask in one message:
|
|
55
55
|
- **Project name** — display name
|
|
@@ -71,7 +71,7 @@ today_date = current date YYYY-MM-DD
|
|
|
71
71
|
|
|
72
72
|
## Phase 2 — Scaffold repo CLAUDE.md
|
|
73
73
|
|
|
74
|
-
1. Read `
|
|
74
|
+
1. Read `templates/project-index.md` (the repo CLAUDE.md template)
|
|
75
75
|
2. Apply full substitution map. All placeholders must resolve.
|
|
76
76
|
3. Scan output for remaining `{placeholder}` patterns. Resolve before writing.
|
|
77
77
|
4. Write to `{repo_root}/CLAUDE.md`
|
|
@@ -85,19 +85,19 @@ Create directories:
|
|
|
85
85
|
mkdir -p {vault_path}/plans {vault_path}/notes {vault_path}/specs {vault_path}/research
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Create files from templates in `
|
|
89
|
-
- `_index.md` from `
|
|
90
|
-
- `status.md` from `
|
|
91
|
-
- `tasks.md` from `
|
|
92
|
-
- `notes/learnings.md`
|
|
93
|
-
- `notes/decision-log.md` from `decision-log`
|
|
94
|
-
- `.gitignore`
|
|
88
|
+
Create files from templates in `templates/`:
|
|
89
|
+
- `_index.md` from `templates/_index.md`
|
|
90
|
+
- `status.md` from `templates/status.md` (`current_plan:` empty)
|
|
91
|
+
- `tasks.md` from `templates/tasks.md`
|
|
92
|
+
- `notes/learnings.md` — scaffold minimal learnings file (no template exists; create with YAML frontmatter + empty `## Learnings` section)
|
|
93
|
+
- `notes/decision-log.md` from `templates/decision-log.md` (append-only decision log)
|
|
94
|
+
- `.gitignore` — scaffold standard gitignore for detected stack (new repos only)
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
98
98
|
## Phase 3.5 — Scaffold claude-progress.json
|
|
99
99
|
|
|
100
|
-
From `
|
|
100
|
+
From `templates/claude-progress.json`. Apply substitution map.
|
|
101
101
|
Write to `{vault_path}/claude-progress.json`.
|
|
102
102
|
|
|
103
103
|
---
|
|
@@ -105,7 +105,9 @@ Write to `{vault_path}/claude-progress.json`.
|
|
|
105
105
|
## Phase 4 — Vault Search Check
|
|
106
106
|
|
|
107
107
|
Vault indexing is automatic for `obsidian` backend. No manual re-index needed.
|
|
108
|
-
Verify the new project is searchable
|
|
108
|
+
Verify the new project is searchable using configured backend:
|
|
109
|
+
- If `obsidian`: `obsidian search query="{slug}" limit=1`
|
|
110
|
+
- If `ripgrep`: `rg --files-with-matches "{slug}" {vault_path}/`
|
|
109
111
|
On failure: warn, do not block.
|
|
110
112
|
|
|
111
113
|
---
|
|
@@ -48,7 +48,7 @@ Classify each: type, tag (`bugfix|convention|perf|security|tooling|arch|process`
|
|
|
48
48
|
For each finding with clear root cause:
|
|
49
49
|
- Project-specific → `{vault_path}/notes/learnings.md`
|
|
50
50
|
- Global/harness pattern → harness project learnings.md
|
|
51
|
-
- Check for duplicates via
|
|
51
|
+
- Check for duplicates via vault search (using configured backend) before writing. If vault search unavailable: skip duplicate check and proceed.
|
|
52
52
|
|
|
53
53
|
Format:
|
|
54
54
|
```markdown
|
|
@@ -21,11 +21,15 @@ You are creating a spec for the current project.
|
|
|
21
21
|
Ask the following in a single message:
|
|
22
22
|
- What is this spec for? (one sentence)
|
|
23
23
|
- Type: ADR (architecture decision) / RISK (risk register entry) / SPEC (technical spec)
|
|
24
|
-
- Is there an existing related spec in `specs/`?
|
|
24
|
+
- Is there an existing related spec in `specs/`?
|
|
25
|
+
- Read `vault_backend` from `~/.claude/praxis.config.json`
|
|
26
|
+
- If `obsidian`: run `obsidian search query="{topic}" limit=3`
|
|
27
|
+
- If `ripgrep`: run `rg --files-with-matches "{topic}" {vault_path}/specs/`
|
|
28
|
+
- If vault search fails (e.g., Obsidian not running): warn "Vault search unavailable — skipping conflict check" and proceed without blocking
|
|
25
29
|
|
|
26
30
|
**Step 2b — Cross-spec conflict check**
|
|
27
31
|
After the vault search from Step 2, check for conflicts with accepted ADRs:
|
|
28
|
-
- Run
|
|
32
|
+
- Run vault search for topic (using backend detected above) with limit=10
|
|
29
33
|
- For each result with `status: accepted` or `status: proposed`:
|
|
30
34
|
- Compare the decision direction. Does the new spec contradict an accepted decision?
|
|
31
35
|
- If conflict detected, present:
|
|
@@ -17,7 +17,8 @@ Execute in order, showing actual output (never assertions):
|
|
|
17
17
|
- **DeepSource**: `deepsource issues list <file>` for files in diff (if deepsource CLI available)
|
|
18
18
|
- If either tool finds HIGH/CRITICAL: treat as blocking — must fix before proceeding.
|
|
19
19
|
- If tools not installed: skip with advisory note, do not block. DeepSource cloud validates on push.
|
|
20
|
-
6. **
|
|
20
|
+
6. **Quality gate** → run `/px-quality-gate` on changed files → BLOCK on naming/prose/structure violations. See `px-quality-gate` skill for full check list.
|
|
21
|
+
7. **Functional check** — ask: "Is there a smoke test, `terraform plan` output, or browser check needed for this milestone?" If yes: block until user confirms it passed. If no: proceed.
|
|
21
22
|
|
|
22
23
|
Read test/lint/build commands from the project CLAUDE.md `## Commands` section.
|
|
23
24
|
If no commands are defined: warn and ask user for the correct commands.
|
package/bin/praxis.js
CHANGED
|
@@ -19,7 +19,7 @@ function fail(msg) { console.error(' \x1b[31m\u2717\x1b[0m ' + msg); }
|
|
|
19
19
|
function dim(msg) { console.log(' \x1b[2m' + msg + '\x1b[0m'); }
|
|
20
20
|
|
|
21
21
|
function toolExists(name) {
|
|
22
|
-
const r = spawnSync('
|
|
22
|
+
const r = spawnSync('command', ['-v', name], { stdio: 'pipe', shell: true });
|
|
23
23
|
return r.status === 0;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -94,12 +94,33 @@ async function install() {
|
|
|
94
94
|
const hooksConfig = path.join(hooksDir, 'settings-hooks.json');
|
|
95
95
|
const settingsFile = path.join(CLAUDE_DIR, 'settings.json');
|
|
96
96
|
if (fs.existsSync(hooksConfig)) {
|
|
97
|
-
|
|
97
|
+
let hooksCfg;
|
|
98
|
+
try {
|
|
99
|
+
hooksCfg = JSON.parse(fs.readFileSync(hooksConfig, 'utf8'));
|
|
100
|
+
} catch (hookParseErr) {
|
|
101
|
+
fail('settings-hooks.json has invalid JSON: ' + hookParseErr.message);
|
|
102
|
+
hooksCfg = {};
|
|
103
|
+
}
|
|
98
104
|
let settings = {};
|
|
99
105
|
if (fs.existsSync(settingsFile)) {
|
|
100
|
-
|
|
106
|
+
const raw = fs.readFileSync(settingsFile, 'utf8');
|
|
107
|
+
try {
|
|
108
|
+
settings = JSON.parse(raw);
|
|
109
|
+
} catch (parseErr) {
|
|
110
|
+
// Preserve corrupt file as backup before overwriting
|
|
111
|
+
const backupPath = settingsFile + '.backup';
|
|
112
|
+
fs.writeFileSync(backupPath, raw);
|
|
113
|
+
fail('settings.json has invalid JSON — backed up to ' + backupPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Deep merge: preserve existing keys, overlay hooks
|
|
117
|
+
for (const [key, value] of Object.entries(hooksCfg)) {
|
|
118
|
+
if (typeof value === 'object' && !Array.isArray(value) && settings[key] && typeof settings[key] === 'object') {
|
|
119
|
+
settings[key] = { ...settings[key], ...value };
|
|
120
|
+
} else {
|
|
121
|
+
settings[key] = value;
|
|
122
|
+
}
|
|
101
123
|
}
|
|
102
|
-
Object.assign(settings, hooksCfg);
|
|
103
124
|
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
|
104
125
|
ok('hooks configuration merged into settings.json');
|
|
105
126
|
}
|
|
@@ -261,7 +282,7 @@ function health() {
|
|
|
261
282
|
} else {
|
|
262
283
|
total++; fail('vault_path not set in config');
|
|
263
284
|
}
|
|
264
|
-
} catch { total++; fail('praxis.config.json is invalid JSON'); }
|
|
285
|
+
} catch (configErr) { total++; fail('praxis.config.json is invalid JSON: ' + configErr.message); }
|
|
265
286
|
}
|
|
266
287
|
|
|
267
288
|
// Tools
|
|
@@ -386,7 +407,7 @@ else if (arg === '--version' || arg === '-v') { console.log(VERSION); }
|
|
|
386
407
|
else if (commands[arg]) {
|
|
387
408
|
const result = commands[arg]();
|
|
388
409
|
if (result && typeof result.catch === 'function') {
|
|
389
|
-
result.catch(err => { fail(err
|
|
410
|
+
result.catch(err => { fail(err?.message || String(err)); process.exit(1); });
|
|
390
411
|
}
|
|
391
412
|
}
|
|
392
413
|
else { fail('Unknown command: ' + arg); printHelp(); process.exit(1); }
|
package/kits/api/install.sh
CHANGED
package/kits/api/teardown.sh
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#!/bin/bash
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
2
3
|
# Triggers Claude AI self-review after commit
|
|
3
4
|
# This is advisory — does not block, surfaces findings for next commit
|
|
4
5
|
|
|
@@ -11,7 +12,7 @@ if ! command -v claude &>/dev/null; then
|
|
|
11
12
|
fi
|
|
12
13
|
|
|
13
14
|
CHANGED=$(git diff --name-only HEAD~1...HEAD 2>/dev/null | head -20)
|
|
14
|
-
if [ -z "$CHANGED" ]; then
|
|
15
|
+
if [[ -z "$CHANGED" ]]; then
|
|
15
16
|
exit 0
|
|
16
17
|
fi
|
|
17
18
|
|