@esoteric-logic/praxis-harness 2.3.0 → 2.4.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/README.md +13 -1
- package/base/CLAUDE.md +4 -0
- package/base/configs/linters/.editorconfig +29 -0
- package/base/configs/linters/.golangci.yml +63 -0
- package/base/configs/linters/.hadolint.yaml +7 -0
- package/base/configs/linters/.shellcheckrc +4 -0
- package/base/configs/linters/.tflint.hcl +30 -0
- package/base/configs/linters/commitlint.config.js +10 -0
- package/base/configs/vale/.vale.ini +16 -0
- package/base/configs/vale/Praxis/AISlop.yml +90 -0
- package/base/configs/vale/Praxis/CopulaAvoidance.yml +22 -0
- package/base/configs/vale/Praxis/ElegantVariation.yml +14 -0
- package/base/configs/vale/Praxis/Hedging.yml +22 -0
- package/base/configs/vale/Praxis/NaturalVoice.yml +85 -0
- package/base/configs/vale/Praxis/Precision.yml +60 -0
- package/base/hooks/file-guard.sh +53 -0
- package/base/hooks/post-session-lint.sh +5 -0
- package/base/hooks/quality-check.sh +165 -0
- package/base/hooks/settings-hooks.json +14 -1
- package/base/hooks/vault-checkpoint.sh +25 -0
- package/base/rules/coding.md +23 -0
- package/base/rules/context-management.md +2 -0
- package/base/rules/terraform.md +10 -0
- package/base/skills/execute/SKILL.md +15 -0
- package/base/skills/plan/SKILL.md +16 -0
- package/base/skills/pre-commit-lint/SKILL.md +21 -1
- package/base/skills/repair/SKILL.md +7 -0
- package/base/skills/scaffold-new/SKILL.md +40 -0
- package/base/skills/scaffold-new/references/repo-CLAUDE-md-template.md +35 -0
- package/base/skills/ship/SKILL.md +6 -0
- package/base/skills/verify/SKILL.md +16 -5
- package/bin/praxis.js +19 -0
- package/package.json +1 -1
- package/scripts/install-tools.sh +137 -0
- package/scripts/lint-harness.sh +2 -1
- package/scripts/test-harness.sh +87 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# quality-check.sh — PostToolUse hook (merged format + lint + typecheck)
|
|
3
|
+
# Detects stack from file extension. Formats, lints, type-checks.
|
|
4
|
+
# Always exits 0 (PostToolUse cannot hard-block).
|
|
5
|
+
# Emits JSON feedback for Claude self-correction.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
# Guard: skip if stop hook is active (prevent infinite loop)
|
|
11
|
+
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active // false')" = "true" ]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
|
|
16
|
+
|
|
17
|
+
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
EXT="${FILE_PATH##*.}"
|
|
22
|
+
BASENAME=$(basename "$FILE_PATH")
|
|
23
|
+
ISSUES=()
|
|
24
|
+
|
|
25
|
+
# ── FORMAT ──────────────────────────────────────────────
|
|
26
|
+
case "$EXT" in
|
|
27
|
+
go)
|
|
28
|
+
if command -v goimports &>/dev/null; then
|
|
29
|
+
goimports -w "$FILE_PATH" 2>/dev/null
|
|
30
|
+
elif command -v gofmt &>/dev/null; then
|
|
31
|
+
gofmt -w "$FILE_PATH" 2>/dev/null
|
|
32
|
+
fi
|
|
33
|
+
;;
|
|
34
|
+
tf|tfvars)
|
|
35
|
+
if command -v terraform &>/dev/null; then
|
|
36
|
+
terraform fmt "$FILE_PATH" 2>/dev/null
|
|
37
|
+
fi
|
|
38
|
+
;;
|
|
39
|
+
sh|bash)
|
|
40
|
+
if command -v shfmt &>/dev/null; then
|
|
41
|
+
shfmt -i 2 -ci -bn -w "$FILE_PATH" 2>/dev/null
|
|
42
|
+
fi
|
|
43
|
+
;;
|
|
44
|
+
py)
|
|
45
|
+
if command -v ruff &>/dev/null; then
|
|
46
|
+
ruff format "$FILE_PATH" 2>/dev/null
|
|
47
|
+
fi
|
|
48
|
+
;;
|
|
49
|
+
json)
|
|
50
|
+
if command -v jq &>/dev/null; then
|
|
51
|
+
TMP=$(mktemp)
|
|
52
|
+
if jq . "$FILE_PATH" > "$TMP" 2>/dev/null; then
|
|
53
|
+
mv "$TMP" "$FILE_PATH"
|
|
54
|
+
else
|
|
55
|
+
rm -f "$TMP"
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
;;
|
|
59
|
+
toml)
|
|
60
|
+
if command -v taplo &>/dev/null; then
|
|
61
|
+
taplo format "$FILE_PATH" 2>/dev/null
|
|
62
|
+
fi
|
|
63
|
+
;;
|
|
64
|
+
md)
|
|
65
|
+
if command -v prettier &>/dev/null; then
|
|
66
|
+
prettier --write --prose-wrap always "$FILE_PATH" 2>/dev/null
|
|
67
|
+
fi
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
|
|
71
|
+
# ── LINT ────────────────────────────────────────────────
|
|
72
|
+
case "$EXT" in
|
|
73
|
+
go)
|
|
74
|
+
if command -v go &>/dev/null; then
|
|
75
|
+
LINT_OUT=$(go vet "$FILE_PATH" 2>&1) || true
|
|
76
|
+
if [ -n "$LINT_OUT" ]; then
|
|
77
|
+
ISSUES+=("go vet: $LINT_OUT")
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
;;
|
|
81
|
+
tf|tfvars)
|
|
82
|
+
if command -v tflint &>/dev/null; then
|
|
83
|
+
LINT_OUT=$(tflint --filter="$FILE_PATH" 2>&1) || true
|
|
84
|
+
if [ -n "$LINT_OUT" ]; then
|
|
85
|
+
ISSUES+=("tflint: $LINT_OUT")
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
;;
|
|
89
|
+
sh|bash)
|
|
90
|
+
if command -v shellcheck &>/dev/null; then
|
|
91
|
+
LINT_OUT=$(shellcheck -f gcc "$FILE_PATH" 2>&1) || true
|
|
92
|
+
if [ -n "$LINT_OUT" ]; then
|
|
93
|
+
ISSUES+=("shellcheck: $LINT_OUT")
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
;;
|
|
97
|
+
py)
|
|
98
|
+
if command -v ruff &>/dev/null; then
|
|
99
|
+
LINT_OUT=$(ruff check --fix "$FILE_PATH" 2>&1) || true
|
|
100
|
+
if [ -n "$LINT_OUT" ] && ! echo "$LINT_OUT" | grep -q "All checks passed"; then
|
|
101
|
+
ISSUES+=("ruff: $LINT_OUT")
|
|
102
|
+
fi
|
|
103
|
+
fi
|
|
104
|
+
;;
|
|
105
|
+
md)
|
|
106
|
+
if command -v vale &>/dev/null; then
|
|
107
|
+
LINT_OUT=$(vale --output=line "$FILE_PATH" 2>&1) || true
|
|
108
|
+
if [ -n "$LINT_OUT" ]; then
|
|
109
|
+
ISSUES+=("vale: $LINT_OUT")
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
;;
|
|
113
|
+
yml|yaml)
|
|
114
|
+
if command -v yamllint &>/dev/null; then
|
|
115
|
+
LINT_OUT=$(yamllint -f parsable "$FILE_PATH" 2>&1) || true
|
|
116
|
+
if [ -n "$LINT_OUT" ]; then
|
|
117
|
+
ISSUES+=("yamllint: $LINT_OUT")
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
;;
|
|
121
|
+
esac
|
|
122
|
+
|
|
123
|
+
# ── LINT (Dockerfile special case) ─────────────────────
|
|
124
|
+
if [ "$BASENAME" = "Dockerfile" ] || echo "$BASENAME" | grep -qE "^Dockerfile\."; then
|
|
125
|
+
if command -v hadolint &>/dev/null; then
|
|
126
|
+
LINT_OUT=$(hadolint "$FILE_PATH" 2>&1) || true
|
|
127
|
+
if [ -n "$LINT_OUT" ]; then
|
|
128
|
+
ISSUES+=("hadolint: $LINT_OUT")
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# ── TYPECHECK ───────────────────────────────────────────
|
|
134
|
+
case "$EXT" in
|
|
135
|
+
go)
|
|
136
|
+
if command -v go &>/dev/null; then
|
|
137
|
+
DIR=$(dirname "$FILE_PATH")
|
|
138
|
+
TC_OUT=$(cd "$DIR" && go build ./... 2>&1) || true
|
|
139
|
+
if [ -n "$TC_OUT" ]; then
|
|
140
|
+
ISSUES+=("go build: $TC_OUT")
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
;;
|
|
144
|
+
bicep)
|
|
145
|
+
if command -v az &>/dev/null; then
|
|
146
|
+
TC_OUT=$(az bicep build --file "$FILE_PATH" 2>&1) || true
|
|
147
|
+
if echo "$TC_OUT" | grep -qi "error"; then
|
|
148
|
+
ISSUES+=("bicep build: $TC_OUT")
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
;;
|
|
152
|
+
esac
|
|
153
|
+
|
|
154
|
+
# ── EMIT RESULT ─────────────────────────────────────────
|
|
155
|
+
if [ ${#ISSUES[@]} -eq 0 ]; then
|
|
156
|
+
echo '{"decision":"pass","reason":""}'
|
|
157
|
+
else
|
|
158
|
+
# Truncate to avoid flooding context window
|
|
159
|
+
COMBINED=$(printf '%s\n' "${ISSUES[@]}" | head -30)
|
|
160
|
+
# Escape for JSON
|
|
161
|
+
ESCAPED=$(echo "$COMBINED" | jq -Rs .)
|
|
162
|
+
echo "{\"decision\":\"block\",\"reason\":$ESCAPED}"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
exit 0
|
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "bash ~/.claude/hooks/file-guard.sh"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
13
22
|
{
|
|
14
23
|
"matcher": "Bash",
|
|
15
24
|
"hooks": [
|
|
@@ -26,7 +35,7 @@
|
|
|
26
35
|
"hooks": [
|
|
27
36
|
{
|
|
28
37
|
"type": "command",
|
|
29
|
-
"command": "bash ~/.claude/hooks/
|
|
38
|
+
"command": "bash ~/.claude/hooks/quality-check.sh"
|
|
30
39
|
}
|
|
31
40
|
]
|
|
32
41
|
}
|
|
@@ -39,6 +48,10 @@
|
|
|
39
48
|
"type": "command",
|
|
40
49
|
"command": "bash ~/.claude/hooks/post-session-lint.sh"
|
|
41
50
|
},
|
|
51
|
+
{
|
|
52
|
+
"type": "prompt",
|
|
53
|
+
"prompt": "Run the project test suite. Read CLAUDE.md ## Commands for the test command. If no test command defined, respond {ok:true}. Run tests. If all pass: respond {ok:true}. If tests fail: respond {ok:false, reason:'Tests failing: <summary>'}. Do not fix — only report."
|
|
54
|
+
},
|
|
42
55
|
{
|
|
43
56
|
"type": "command",
|
|
44
57
|
"command": "bash ~/.claude/hooks/session-data-collect.sh"
|
|
@@ -37,6 +37,27 @@ if [[ -f "$STATUS_FILE" ]]; then
|
|
|
37
37
|
[[ -z "$LOOP_POSITION" ]] && LOOP_POSITION="unknown"
|
|
38
38
|
fi
|
|
39
39
|
|
|
40
|
+
# ── Quality state snapshot (survives compaction) ──
|
|
41
|
+
LINT_STATE="unknown"
|
|
42
|
+
TEST_STATE="unknown"
|
|
43
|
+
|
|
44
|
+
if [ -f "go.mod" ] && command -v golangci-lint &>/dev/null; then
|
|
45
|
+
LINT_COUNT=$(golangci-lint run ./... 2>&1 | grep -c "^" || true)
|
|
46
|
+
if [ "$LINT_COUNT" -eq 0 ]; then
|
|
47
|
+
LINT_STATE="clean"
|
|
48
|
+
else
|
|
49
|
+
LINT_STATE="$LINT_COUNT findings"
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [ -f "go.mod" ] && command -v go &>/dev/null; then
|
|
54
|
+
if go test ./... -short 2>&1 | grep -q "^ok"; then
|
|
55
|
+
TEST_STATE="passing"
|
|
56
|
+
else
|
|
57
|
+
TEST_STATE="failing"
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
40
61
|
cat > "$CHECKPOINT_FILE" <<EOF
|
|
41
62
|
---
|
|
42
63
|
tags: [checkpoint, compact]
|
|
@@ -58,6 +79,10 @@ $CURRENT_PLAN
|
|
|
58
79
|
## Loop Position
|
|
59
80
|
$LOOP_POSITION
|
|
60
81
|
|
|
82
|
+
## Quality State
|
|
83
|
+
- Lint: $LINT_STATE
|
|
84
|
+
- Tests: $TEST_STATE
|
|
85
|
+
|
|
61
86
|
## Note
|
|
62
87
|
This checkpoint was auto-written by the PreCompact hook.
|
|
63
88
|
Read this file after compaction to restore context.
|
package/base/rules/coding.md
CHANGED
|
@@ -113,8 +113,31 @@ Before adding any new package:
|
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
116
|
+
## Linter Delegation
|
|
117
|
+
|
|
118
|
+
Style, formatting, import ordering, complexity thresholds, and security patterns
|
|
119
|
+
are enforced by automated tooling (golangci-lint, shellcheck, hadolint, tflint,
|
|
120
|
+
semgrep, vale). Rules files cover ONLY:
|
|
121
|
+
- Architecture decisions tooling cannot express
|
|
122
|
+
- Permission boundaries and file-group scoping
|
|
123
|
+
- Error learning from real session failures
|
|
124
|
+
- Verification command references
|
|
125
|
+
|
|
126
|
+
Do NOT duplicate in CLAUDE.md what hooks already enforce.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
116
130
|
## Verification Commands
|
|
117
131
|
|
|
132
|
+
Single-file (matches PostToolUse hooks):
|
|
133
|
+
- `go vet <file>` / `shellcheck <file>` / `hadolint Dockerfile` / `vale <file>.md`
|
|
134
|
+
|
|
135
|
+
Project-level (matches pre-commit + /verify):
|
|
136
|
+
- `golangci-lint run`
|
|
137
|
+
- `semgrep --config=auto --error .`
|
|
138
|
+
- `govulncheck ./...`
|
|
139
|
+
- `trivy config .`
|
|
140
|
+
|
|
118
141
|
```bash
|
|
119
142
|
# Verify Context7 MCP is active
|
|
120
143
|
claude mcp list | grep context7
|
|
@@ -57,6 +57,8 @@ conversation length heuristic (not token count — we cannot read session JSONL)
|
|
|
57
57
|
- Re-read key requirements before implementation decisions
|
|
58
58
|
- Reinforce constraint awareness — re-state SPEC if drifting
|
|
59
59
|
- Prefer concise output — save context for implementation
|
|
60
|
+
- Run lint on all files modified this session to catch drift-induced errors:
|
|
61
|
+
`golangci-lint run $(git diff --name-only HEAD) 2>/dev/null || true`
|
|
60
62
|
|
|
61
63
|
### DEPLETED (late session, >60%)
|
|
62
64
|
- Checkpoint progress to vault files before continuing
|
package/base/rules/terraform.md
CHANGED
|
@@ -39,10 +39,20 @@ paths:
|
|
|
39
39
|
- Managed identity preferred over service principal where possible
|
|
40
40
|
- Tag all resources: `project`, `environment`, `owner`, `created_by = "terraform"`
|
|
41
41
|
|
|
42
|
+
### Security scanning
|
|
43
|
+
- Run `trivy config .` before committing infrastructure changes.
|
|
44
|
+
- If trivy is not installed: warn, do not block. (tfsec is deprecated — use trivy.)
|
|
45
|
+
|
|
46
|
+
### Cost Awareness
|
|
47
|
+
Before proposing any Azure resource, state the estimated SKU and monthly cost.
|
|
48
|
+
Prefer B-series (burstable) over D/E-series unless workload justifies it.
|
|
49
|
+
Infracost runs on every commit — cost surprises are treated as bugs.
|
|
50
|
+
|
|
42
51
|
## Verification Commands
|
|
43
52
|
```bash
|
|
44
53
|
terraform fmt -recursive -check
|
|
45
54
|
terraform validate
|
|
55
|
+
trivy config --severity HIGH,CRITICAL .
|
|
46
56
|
rg 'source\s*=\s*"\.\.\/environments' modules/
|
|
47
57
|
rg '^resource "' environments/
|
|
48
58
|
```
|
|
@@ -41,6 +41,21 @@ Before implementing the current milestone, declare the file group:
|
|
|
41
41
|
- **Rationale**: {why}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
**Step 2c — Execution mode selection**
|
|
45
|
+
Read `execution_type` from the active plan frontmatter.
|
|
46
|
+
|
|
47
|
+
**If execution_type: tdd**:
|
|
48
|
+
1. Write test file first (`*_test.go`, `*.test.ts`, etc.)
|
|
49
|
+
2. Run test command → confirm RED (tests fail as expected)
|
|
50
|
+
3. If tests pass before implementation → tests are wrong, rewrite them
|
|
51
|
+
4. Implement minimum code to make tests GREEN
|
|
52
|
+
5. Run lint (project lint command from CLAUDE.md ## Commands)
|
|
53
|
+
6. Refactor if needed — tests must stay green
|
|
54
|
+
7. Commit test + implementation together
|
|
55
|
+
|
|
56
|
+
**If execution_type: execute** (default):
|
|
57
|
+
Proceed with existing implementation flow unchanged.
|
|
58
|
+
|
|
44
59
|
**Step 3 — Implement current milestone**
|
|
45
60
|
- Update `{vault_path}/status.md`: set `loop_position: EXECUTE`.
|
|
46
61
|
- One milestone at a time. Keep diffs scoped.
|
|
@@ -106,6 +106,22 @@ After building the milestone list:
|
|
|
106
106
|
- Validate checkpoints: milestones with architectural decisions or user-facing output
|
|
107
107
|
should be annotated as `checkpoint: decision` or `checkpoint: human-verify`.
|
|
108
108
|
|
|
109
|
+
**Step 2c — Execution type recommendation**
|
|
110
|
+
After building milestones, recommend `execution_type` in plan frontmatter:
|
|
111
|
+
|
|
112
|
+
Recommend **tdd** when:
|
|
113
|
+
- Plan touches existing code that already has tests
|
|
114
|
+
- Plan involves API contracts, interfaces, or data transformations
|
|
115
|
+
- Plan is a bug fix (regression test first)
|
|
116
|
+
|
|
117
|
+
Recommend **execute** when:
|
|
118
|
+
- Pure IaC / infrastructure work
|
|
119
|
+
- Greenfield scaffolding with no existing test patterns
|
|
120
|
+
- Documentation-only changes
|
|
121
|
+
|
|
122
|
+
Write the recommendation to plan frontmatter. Explain the reasoning in one line.
|
|
123
|
+
The operator can override during plan review.
|
|
124
|
+
|
|
109
125
|
**Step 3 — Write and wire**
|
|
110
126
|
- Write to: `{vault_path}/plans/{YYYY-MM-DD}_{kebab-title}.md`
|
|
111
127
|
- Update `status.md`: set `current_plan:`, update `last_updated`, set `loop_position: PLAN`, update `## Now What`
|
|
@@ -36,7 +36,7 @@ Out of scope:
|
|
|
36
36
|
|
|
37
37
|
1. Read `{repo_root}/CLAUDE.md` → extract `{identity_email}` and `{stack}`
|
|
38
38
|
2. Detect stack from repo: `git ls-files | grep -E "\.(tf|ps1|yml|py|ts|sh|go)$" | sed 's/.*\.//' | sort -u`
|
|
39
|
-
3. Check tool availability: tflint,
|
|
39
|
+
3. Check tool availability: tflint, trivy, actionlint, ruff, mypy, terraform, shellcheck, golangci-lint, semgrep, hadolint, govulncheck, infracost, vale, markdownlint, gitleaks, commitlint
|
|
40
40
|
|
|
41
41
|
## Phase 1 — Generate Hook
|
|
42
42
|
|
|
@@ -47,6 +47,26 @@ Append stack sections based on detected flags:
|
|
|
47
47
|
- **Shell**: bash -n syntax, set -euo check, shellcheck
|
|
48
48
|
- **GitHub Actions**: actionlint, unpinned action check
|
|
49
49
|
- **Python**: ruff, mypy
|
|
50
|
+
- **Go** (if golangci-lint available): `golangci-lint run` on staged .go files
|
|
51
|
+
- **Security** (if tools available):
|
|
52
|
+
- `gitleaks protect --staged` (replaces regex-based secret scan if gitleaks available)
|
|
53
|
+
- `semgrep --config=auto --error` on staged code files (if semgrep available)
|
|
54
|
+
- **Terraform security** (if tools available):
|
|
55
|
+
- `trivy config --severity HIGH,CRITICAL --exit-code 1` on staged .tf files
|
|
56
|
+
- `infracost breakdown --path=.` (advisory only, exit 0)
|
|
57
|
+
- **Docker** (if hadolint available):
|
|
58
|
+
- `hadolint` on staged Dockerfiles
|
|
59
|
+
- `trivy config` on staged Dockerfiles (if trivy available)
|
|
60
|
+
- **Dependencies** (if applicable):
|
|
61
|
+
- `govulncheck ./...` (if go.mod exists and govulncheck available)
|
|
62
|
+
- **Markdown** (if tools available):
|
|
63
|
+
- `vale` on staged .md files
|
|
64
|
+
- `markdownlint` on staged .md files
|
|
65
|
+
|
|
66
|
+
### Commit Message Hook
|
|
67
|
+
Generate a SEPARATE `.git/hooks/commit-msg` file that runs:
|
|
68
|
+
- `commitlint --edit $1` (if commitlint available)
|
|
69
|
+
|
|
50
70
|
Append summary footer.
|
|
51
71
|
|
|
52
72
|
## Phase 2 — Write and Verify
|
|
@@ -52,6 +52,13 @@ Run the full validation sequence from `/verify` Step 1:
|
|
|
52
52
|
3. Typecheck (if applicable)
|
|
53
53
|
4. Build (if applicable)
|
|
54
54
|
|
|
55
|
+
**Step 4b — Post-fix security re-scan**
|
|
56
|
+
After applying the fix, run the full validation sequence from /verify Step 1 (including security scan).
|
|
57
|
+
If the original failure was security-related:
|
|
58
|
+
1. Re-run the exact scanner that found the original issue
|
|
59
|
+
2. Confirm the specific finding is resolved
|
|
60
|
+
3. Verify the fix didn't introduce NEW findings in the same file
|
|
61
|
+
|
|
55
62
|
**Step 5 — Evaluate result**
|
|
56
63
|
- **PASS** → Milestone repaired. Commit the fix. Return to workflow.
|
|
57
64
|
Output: `Repaired: {what was fixed} in {file}. Next: continue with /execute or /verify.`
|
|
@@ -129,6 +129,46 @@ If no: skip silently.
|
|
|
129
129
|
|
|
130
130
|
---
|
|
131
131
|
|
|
132
|
+
## Phase 5.5b — Linter Config Generation
|
|
133
|
+
|
|
134
|
+
Based on the detected tech stack from Phase 1, copy relevant configs from `base/configs/`:
|
|
135
|
+
|
|
136
|
+
| Stack Detected | Configs to Copy |
|
|
137
|
+
|---------------|-----------------|
|
|
138
|
+
| Go | `.golangci.yml` |
|
|
139
|
+
| Shell scripts | `.shellcheckrc` |
|
|
140
|
+
| Dockerfile | `.hadolint.yaml` |
|
|
141
|
+
| Terraform | `.tflint.hcl` |
|
|
142
|
+
| Any | `.vale.ini`, `.vale-styles/Praxis/*` (all rule files), `.editorconfig`, `commitlint.config.js` |
|
|
143
|
+
|
|
144
|
+
After copying Vale configs: run `vale sync` to download community packages (Microsoft, write-good, Readability).
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Phase 5.5c — VS Code Workspace (optional)
|
|
149
|
+
|
|
150
|
+
Ask: "Generate VS Code workspace settings?"
|
|
151
|
+
If yes:
|
|
152
|
+
- Generate `.vscode/settings.json` with stack-detected configuration (formatter, linter paths)
|
|
153
|
+
- Generate `.vscode/extensions.json` with recommended extensions from this mapping:
|
|
154
|
+
|
|
155
|
+
| Always | Extension ID |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| Vale | `chrischinchilla.vale-vscode` |
|
|
158
|
+
| ShellCheck | `timonwong.shellcheck` |
|
|
159
|
+
| EditorConfig | `editorconfig.editorconfig` |
|
|
160
|
+
| markdownlint | `davidanson.vscode-markdownlint` |
|
|
161
|
+
|
|
162
|
+
| Stack Detected | Extension ID |
|
|
163
|
+
|---------------|-------------|
|
|
164
|
+
| Go | `golang.go` |
|
|
165
|
+
| Terraform | `hashicorp.terraform` |
|
|
166
|
+
| Docker | `exiasr.hadolint` |
|
|
167
|
+
|
|
168
|
+
If no: skip silently.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
132
172
|
## Phase 6 — Update Project Registry
|
|
133
173
|
|
|
134
174
|
Append to vault CLAUDE.md Project Registry table and agenda.md.
|
|
@@ -39,14 +39,40 @@ Code lives here. Knowledge, decisions, and plans live in the vault.
|
|
|
39
39
|
- {stack_item_3}
|
|
40
40
|
|
|
41
41
|
## Commands
|
|
42
|
+
<!-- Populated by scaffold-new based on detected stack -->
|
|
43
|
+
|
|
44
|
+
### Go
|
|
45
|
+
```bash
|
|
46
|
+
test: go test ./...
|
|
47
|
+
lint: golangci-lint run
|
|
48
|
+
format: goimports -w . && shfmt -w .
|
|
49
|
+
typecheck: go build ./...
|
|
50
|
+
security: govulncheck ./... && semgrep --config=auto --error .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Terraform
|
|
54
|
+
```bash
|
|
55
|
+
lint: tflint --recursive && trivy config .
|
|
56
|
+
format: terraform fmt -recursive
|
|
57
|
+
security: trivy config --severity HIGH,CRITICAL .
|
|
58
|
+
cost: infracost breakdown --path=.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### General
|
|
42
62
|
```bash
|
|
43
63
|
dev: # fill in as project develops
|
|
44
64
|
test: # fill in as project develops
|
|
45
65
|
lint: # fill in as project develops
|
|
46
66
|
build: # fill in as project develops
|
|
47
67
|
format: # fill in as project develops
|
|
68
|
+
prose: vale .
|
|
48
69
|
```
|
|
49
70
|
|
|
71
|
+
## Thresholds
|
|
72
|
+
- coverage_minimum: 80
|
|
73
|
+
- max_function_lines: 60
|
|
74
|
+
- max_cognitive_complexity: 20
|
|
75
|
+
|
|
50
76
|
## Code Style
|
|
51
77
|
- Prefer simple, readable code over clever abstractions
|
|
52
78
|
- After finishing implementation, run `/simplify` to clean up
|
|
@@ -65,6 +91,12 @@ format: # fill in as project develops
|
|
|
65
91
|
- **Plans**: `{vault_path}/plans/YYYY-MM-DD_[task-slug].md`
|
|
66
92
|
- **Learnings**: `{vault_path}/notes/learnings.md` using [LEARN:tag] schema
|
|
67
93
|
|
|
94
|
+
## Protected Files
|
|
95
|
+
<!-- Files that file-guard.sh blocks from modification -->
|
|
96
|
+
- go.sum
|
|
97
|
+
- go.mod
|
|
98
|
+
- .github/workflows/
|
|
99
|
+
|
|
68
100
|
## Error Learning
|
|
69
101
|
<!-- When a mistake is corrected, write a new rule here to prevent recurrence -->
|
|
70
102
|
<!-- Each rule should be specific and actionable -->
|
|
@@ -77,6 +109,9 @@ format: # fill in as project develops
|
|
|
77
109
|
**Step 1** — Read this file top to bottom first.
|
|
78
110
|
**Step 2** — Active task? → read active plan. No task? → read status.md.
|
|
79
111
|
**Step 3** — Load stack rules only if the current task touches them.
|
|
112
|
+
**Step 4** — Quality re-anchor: read most recent `compact-checkpoint.md` → check Quality State section.
|
|
113
|
+
If lint findings existed before compaction: re-run lint, confirm status.
|
|
114
|
+
If tests were failing before compaction: re-run tests, confirm status.
|
|
80
115
|
|
|
81
116
|
## Compact Preservation
|
|
82
117
|
When compacting, always preserve:
|
|
@@ -14,6 +14,12 @@ You are running the ship workflow — commit, push, and PR in one command.
|
|
|
14
14
|
3. Typecheck (if applicable)
|
|
15
15
|
4. Test suite (from CLAUDE.md `## Commands`)
|
|
16
16
|
5. Identity check: `git --no-pager config user.email` must match expected
|
|
17
|
+
6. Secret deep scan: `gitleaks protect --staged` (if gitleaks available)
|
|
18
|
+
7. Security scan: `semgrep --config=auto --error .` (if semgrep available, on staged files)
|
|
19
|
+
8. Dependency check: `govulncheck ./...` (if Go project with go.mod)
|
|
20
|
+
9. Cost check: `infracost breakdown --path=.` (if Terraform project — advisory only, do not block)
|
|
21
|
+
- Any HIGH/CRITICAL finding in steps 6-8 blocks the ship. Cost check is informational.
|
|
22
|
+
- If tools not installed: skip with note, do not block.
|
|
17
23
|
- If ANY check fails: STOP. Fix first, then re-run `/ship`.
|
|
18
24
|
|
|
19
25
|
**Step 2 — Stage and review changes**
|
|
@@ -12,7 +12,12 @@ Execute in order, showing actual output (never assertions):
|
|
|
12
12
|
2. **Linter** → run the project lint command → show output, fix ALL warnings
|
|
13
13
|
3. **Typecheck** (if applicable) → show output
|
|
14
14
|
4. **Build** (if applicable) → show output
|
|
15
|
-
5. **
|
|
15
|
+
5. **Security scan** (if tools available):
|
|
16
|
+
- **Go**: `gosec ./...` OR `golangci-lint run --enable=gosec` (if golangci-lint available)
|
|
17
|
+
- **All staged code**: `semgrep --config=auto --error .` (if semgrep available)
|
|
18
|
+
- If either tool finds HIGH/CRITICAL: treat as blocking — must fix before proceeding.
|
|
19
|
+
- If tools not installed: skip with advisory note, do not block.
|
|
20
|
+
6. **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.
|
|
16
21
|
|
|
17
22
|
Read test/lint/build commands from the project CLAUDE.md `## Commands` section.
|
|
18
23
|
If no commands are defined: warn and ask user for the correct commands.
|
|
@@ -29,10 +34,16 @@ If no commands are defined: warn and ask user for the correct commands.
|
|
|
29
34
|
4. Check if more milestones remain:
|
|
30
35
|
- Yes → "Milestone committed. Run `/execute` for the next milestone."
|
|
31
36
|
- No → "All milestones committed. Running self-review."
|
|
32
|
-
5. After ALL milestones: trigger Self-Review Protocol
|
|
33
|
-
- Launch a subagent
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
5. After ALL milestones: trigger Self-Review Protocol (Subagent Isolation)
|
|
38
|
+
- Launch a subagent for code review. The subagent receives ONLY:
|
|
39
|
+
1. The diff: `git diff main...HEAD` (or `git diff HEAD~N` for N commits)
|
|
40
|
+
2. The SPEC/ACCEPTANCE section from the active plan
|
|
41
|
+
3. Relevant rules files (scoped to file types in the diff)
|
|
42
|
+
- The subagent must NOT receive session conversation history or implementation notes.
|
|
43
|
+
- Two-pass review:
|
|
44
|
+
- **Pass 1 — Spec compliance**: Does the diff deliver what ACCEPTANCE requires? Flag gaps.
|
|
45
|
+
- **Pass 2 — Code quality**: Bugs, security issues, dead code, missing error handling, maintainability.
|
|
46
|
+
- If either pass finds issues: list them. Do not auto-fix — report back to the operator.
|
|
36
47
|
|
|
37
48
|
**Step 3b — UNIFY (mandatory after all milestones verified)**
|
|
38
49
|
After self-review passes, write phase summary:
|
package/bin/praxis.js
CHANGED
|
@@ -105,6 +105,14 @@ async function install() {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// Copy base/configs/ → ~/.claude/configs/
|
|
109
|
+
const configsDir = path.join(PKG_DIR, 'base', 'configs');
|
|
110
|
+
if (fs.existsSync(configsDir)) {
|
|
111
|
+
fs.mkdirSync(path.join(CLAUDE_DIR, 'configs'), { recursive: true });
|
|
112
|
+
copyDir(configsDir, path.join(CLAUDE_DIR, 'configs'));
|
|
113
|
+
ok('linter configs installed');
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
// Copy kits/ → ~/.claude/kits/
|
|
109
117
|
const kitsDir = path.join(PKG_DIR, 'kits');
|
|
110
118
|
if (fs.existsSync(kitsDir)) {
|
|
@@ -234,6 +242,10 @@ function health() {
|
|
|
234
242
|
}
|
|
235
243
|
check(fs.existsSync(path.join(CLAUDE_DIR, 'settings.json')), 'settings.json with hooks configured');
|
|
236
244
|
|
|
245
|
+
// Configs
|
|
246
|
+
console.log('\nConfigs:');
|
|
247
|
+
check(fs.existsSync(path.join(CLAUDE_DIR, 'configs')), 'configs directory installed');
|
|
248
|
+
|
|
237
249
|
// Kits
|
|
238
250
|
console.log('\nKits:');
|
|
239
251
|
check(fs.existsSync(path.join(CLAUDE_DIR, 'kits')), 'kits directory installed');
|
|
@@ -317,6 +329,13 @@ function uninstall() {
|
|
|
317
329
|
ok('hooks removed');
|
|
318
330
|
}
|
|
319
331
|
|
|
332
|
+
// Remove configs
|
|
333
|
+
const configsTarget = path.join(CLAUDE_DIR, 'configs');
|
|
334
|
+
if (fs.existsSync(configsTarget)) {
|
|
335
|
+
fs.rmSync(configsTarget, { recursive: true, force: true });
|
|
336
|
+
ok('linter configs removed');
|
|
337
|
+
}
|
|
338
|
+
|
|
320
339
|
// Remove kits
|
|
321
340
|
const kitsTarget = path.join(CLAUDE_DIR, 'kits');
|
|
322
341
|
if (fs.existsSync(kitsTarget)) {
|
package/package.json
CHANGED