@esoteric-logic/praxis-harness 2.3.1 → 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/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
package/base/CLAUDE.md
CHANGED
|
@@ -103,6 +103,10 @@ Missing servers are non-blocking — features degrade gracefully.
|
|
|
103
103
|
- Git operation → `~/.claude/rules/git-workflow.md`
|
|
104
104
|
- Client-facing writing → auto-loaded by `communication-standards` skill
|
|
105
105
|
- Architecture/specs → auto-loaded by `architecture-patterns` skill
|
|
106
|
+
5. Quality re-anchor: read most recent `compact-checkpoint.md` → check the Quality State section.
|
|
107
|
+
- If lint findings existed before compaction: re-run `golangci-lint run`, confirm status.
|
|
108
|
+
- If tests were failing before compaction: re-run test command, confirm status.
|
|
109
|
+
- Do NOT assume pre-compaction state is current. Always re-run fresh.
|
|
106
110
|
|
|
107
111
|
## Core Anti-Patterns (NEVER)
|
|
108
112
|
- Silently swallow errors or use empty catch blocks
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
|
|
9
|
+
[*.{yml,yaml,json,tf,tfvars,md,toml,bicep}]
|
|
10
|
+
indent_style = space
|
|
11
|
+
indent_size = 2
|
|
12
|
+
|
|
13
|
+
[*.py]
|
|
14
|
+
indent_style = space
|
|
15
|
+
indent_size = 4
|
|
16
|
+
|
|
17
|
+
[*.go]
|
|
18
|
+
indent_style = tab
|
|
19
|
+
|
|
20
|
+
[Makefile]
|
|
21
|
+
indent_style = tab
|
|
22
|
+
|
|
23
|
+
[*.{sh,bash}]
|
|
24
|
+
indent_style = space
|
|
25
|
+
indent_size = 2
|
|
26
|
+
|
|
27
|
+
[*.ps1]
|
|
28
|
+
indent_style = space
|
|
29
|
+
indent_size = 4
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
run:
|
|
2
|
+
timeout: 3m
|
|
3
|
+
modules-download-mode: readonly
|
|
4
|
+
|
|
5
|
+
linters:
|
|
6
|
+
enable:
|
|
7
|
+
# Error handling
|
|
8
|
+
- errcheck
|
|
9
|
+
- errchkjson
|
|
10
|
+
- nilerr
|
|
11
|
+
- wrapcheck
|
|
12
|
+
- errorlint
|
|
13
|
+
# Logic & correctness
|
|
14
|
+
- govet
|
|
15
|
+
- staticcheck
|
|
16
|
+
- exhaustive
|
|
17
|
+
- nilnil
|
|
18
|
+
- copyloopvar
|
|
19
|
+
- bodyclose
|
|
20
|
+
- sqlclosecheck
|
|
21
|
+
# Security
|
|
22
|
+
- gosec
|
|
23
|
+
# Maintainability
|
|
24
|
+
- dupl
|
|
25
|
+
- goconst
|
|
26
|
+
- funlen
|
|
27
|
+
- gocognit
|
|
28
|
+
- unused
|
|
29
|
+
- ineffassign
|
|
30
|
+
- unconvert
|
|
31
|
+
- misspell
|
|
32
|
+
- goimports
|
|
33
|
+
- gosimple
|
|
34
|
+
- revive
|
|
35
|
+
disable:
|
|
36
|
+
- depguard
|
|
37
|
+
|
|
38
|
+
linters-settings:
|
|
39
|
+
funlen:
|
|
40
|
+
lines: 60
|
|
41
|
+
statements: 40
|
|
42
|
+
gocognit:
|
|
43
|
+
min-complexity: 20
|
|
44
|
+
dupl:
|
|
45
|
+
threshold: 100
|
|
46
|
+
goconst:
|
|
47
|
+
min-len: 3
|
|
48
|
+
min-occurrences: 3
|
|
49
|
+
errcheck:
|
|
50
|
+
check-type-assertions: true
|
|
51
|
+
revive:
|
|
52
|
+
rules:
|
|
53
|
+
- name: exported
|
|
54
|
+
disabled: false
|
|
55
|
+
- name: unexported-return
|
|
56
|
+
disabled: false
|
|
57
|
+
- name: blank-imports
|
|
58
|
+
disabled: false
|
|
59
|
+
|
|
60
|
+
issues:
|
|
61
|
+
max-issues-per-linter: 50
|
|
62
|
+
max-same-issues: 5
|
|
63
|
+
exclude-use-default: false
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
plugin "azurerm" {
|
|
2
|
+
enabled = true
|
|
3
|
+
version = "0.27.0"
|
|
4
|
+
source = "github.com/terraform-linters/tflint-ruleset-azurerm"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
rule "terraform_naming_convention" {
|
|
8
|
+
enabled = true
|
|
9
|
+
format = "snake_case"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
rule "terraform_documented_variables" {
|
|
13
|
+
enabled = true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
rule "terraform_documented_outputs" {
|
|
17
|
+
enabled = true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
rule "terraform_unused_declarations" {
|
|
21
|
+
enabled = true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rule "terraform_typed_variables" {
|
|
25
|
+
enabled = true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
rule "terraform_standard_module_structure" {
|
|
29
|
+
enabled = true
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
extends: ['@commitlint/config-conventional'],
|
|
3
|
+
rules: {
|
|
4
|
+
'type-enum': [2, 'always', ['feat', 'fix', 'refactor', 'test', 'docs', 'chore', 'ci', 'perf', 'style', 'build', 'revert']],
|
|
5
|
+
'scope-empty': [1, 'never'],
|
|
6
|
+
'subject-max-length': [2, 'always', 72],
|
|
7
|
+
'body-max-line-length': [2, 'always', 100],
|
|
8
|
+
'subject-case': [2, 'always', 'lower-case'],
|
|
9
|
+
}
|
|
10
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
StylesPath = .vale-styles
|
|
2
|
+
MinAlertLevel = suggestion
|
|
3
|
+
|
|
4
|
+
Packages = Microsoft, write-good, Readability
|
|
5
|
+
|
|
6
|
+
# Full suite on Markdown
|
|
7
|
+
[*.md]
|
|
8
|
+
BasedOnStyles = Praxis, Microsoft, write-good, Readability
|
|
9
|
+
|
|
10
|
+
# Comment-only scanning on code files
|
|
11
|
+
[*.{go,tf,sh,ps1}]
|
|
12
|
+
BasedOnStyles = Praxis
|
|
13
|
+
|
|
14
|
+
# Praxis rules only on YAML
|
|
15
|
+
[*.{yml,yaml}]
|
|
16
|
+
BasedOnStyles = Praxis
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
extends: substitution
|
|
2
|
+
message: "AI vocabulary detected: '%s' — rephrase in plain language."
|
|
3
|
+
level: warning
|
|
4
|
+
ignorecase: true
|
|
5
|
+
swap:
|
|
6
|
+
delve: "examine, explore, investigate"
|
|
7
|
+
tapestry: "combination, mix"
|
|
8
|
+
multifaceted: "complex, varied"
|
|
9
|
+
leverage: "use"
|
|
10
|
+
paradigm: "model, approach"
|
|
11
|
+
synergy: "collaboration, combination"
|
|
12
|
+
holistic: "complete, whole"
|
|
13
|
+
robust: "strong, reliable"
|
|
14
|
+
seamless: "smooth"
|
|
15
|
+
cutting-edge: "modern, latest"
|
|
16
|
+
game-changer: "improvement, breakthrough"
|
|
17
|
+
deep dive: "detailed look, analysis"
|
|
18
|
+
unpack: "explain, examine"
|
|
19
|
+
landscape: "field, area, environment"
|
|
20
|
+
ecosystem: "system, environment"
|
|
21
|
+
streamline: "simplify"
|
|
22
|
+
spearhead: "lead"
|
|
23
|
+
groundbreaking: "new, innovative"
|
|
24
|
+
transformative: "significant"
|
|
25
|
+
empower: "enable, help"
|
|
26
|
+
reimagine: "rethink, redesign"
|
|
27
|
+
cornerstone: "foundation, basis"
|
|
28
|
+
pivotal: "important, key"
|
|
29
|
+
catalyst: "trigger, cause"
|
|
30
|
+
navigate: "manage, handle"
|
|
31
|
+
harness: "use"
|
|
32
|
+
unlock: "enable, reveal"
|
|
33
|
+
bolster: "strengthen, support"
|
|
34
|
+
underscore: "highlight, emphasize"
|
|
35
|
+
illuminate: "explain, clarify"
|
|
36
|
+
forge: "build, create"
|
|
37
|
+
foster: "encourage, develop"
|
|
38
|
+
cultivate: "develop, build"
|
|
39
|
+
paramount: "important, critical"
|
|
40
|
+
meticulous: "careful, thorough"
|
|
41
|
+
intricate: "complex, detailed"
|
|
42
|
+
nuanced: "subtle"
|
|
43
|
+
comprehensive: "complete, thorough"
|
|
44
|
+
invaluable: "very useful, essential"
|
|
45
|
+
instrumental: "important, helpful"
|
|
46
|
+
embark: "start, begin"
|
|
47
|
+
endeavor: "effort, attempt"
|
|
48
|
+
realm: "area, field"
|
|
49
|
+
facet: "aspect, part"
|
|
50
|
+
Moreover: "Also, Additionally"
|
|
51
|
+
Furthermore: "Also"
|
|
52
|
+
Notably: "Note that"
|
|
53
|
+
Importantly: "Note"
|
|
54
|
+
Essentially: "(omit or rewrite)"
|
|
55
|
+
Fundamentally: "(omit or rewrite)"
|
|
56
|
+
Undoubtedly: "(omit or rewrite)"
|
|
57
|
+
Interestingly: "(omit or rewrite)"
|
|
58
|
+
testament: "proof, evidence"
|
|
59
|
+
beacon: "example, guide"
|
|
60
|
+
resonate: "connect, appeal"
|
|
61
|
+
bespoke: "custom"
|
|
62
|
+
elevate: "improve, raise"
|
|
63
|
+
amplify: "increase, strengthen"
|
|
64
|
+
augment: "add to, supplement"
|
|
65
|
+
myriad: "many"
|
|
66
|
+
plethora: "many, excess"
|
|
67
|
+
burgeoning: "growing"
|
|
68
|
+
nascent: "new, early"
|
|
69
|
+
advent: "arrival, start"
|
|
70
|
+
proliferation: "spread, growth"
|
|
71
|
+
propel: "drive, push"
|
|
72
|
+
overarching: "main, overall"
|
|
73
|
+
dovetail: "fit, align"
|
|
74
|
+
tailored: "custom, specific"
|
|
75
|
+
juxtapose: "compare, contrast"
|
|
76
|
+
dichotomy: "contrast, split"
|
|
77
|
+
eponymous: "named after"
|
|
78
|
+
erstwhile: "former"
|
|
79
|
+
zeitgeist: "spirit of the time"
|
|
80
|
+
ethos: "values, character"
|
|
81
|
+
nexus: "connection, link"
|
|
82
|
+
crucible: "test, trial"
|
|
83
|
+
vanguard: "forefront, leading edge"
|
|
84
|
+
linchpin: "key element"
|
|
85
|
+
bedrock: "foundation"
|
|
86
|
+
scaffolding: "framework, structure"
|
|
87
|
+
underpinning: "basis, foundation"
|
|
88
|
+
bulwark: "defense, safeguard"
|
|
89
|
+
bastion: "stronghold"
|
|
90
|
+
harbinger: "sign, indicator"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
extends: existence
|
|
2
|
+
message: "Inflated copula: '%s' — simplify."
|
|
3
|
+
level: suggestion
|
|
4
|
+
ignorecase: true
|
|
5
|
+
tokens:
|
|
6
|
+
- "serves as a"
|
|
7
|
+
- "acts as a"
|
|
8
|
+
- "functions as a"
|
|
9
|
+
- "plays a pivotal role"
|
|
10
|
+
- "plays a crucial role"
|
|
11
|
+
- "plays a vital role"
|
|
12
|
+
- "plays a key role"
|
|
13
|
+
- "plays an important role"
|
|
14
|
+
- "boasts a"
|
|
15
|
+
- "represents a"
|
|
16
|
+
- "constitutes a"
|
|
17
|
+
- "comprises a"
|
|
18
|
+
- "stands as a"
|
|
19
|
+
- "remains a"
|
|
20
|
+
- "proves to be"
|
|
21
|
+
- "turns out to be"
|
|
22
|
+
- "happens to be"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
extends: existence
|
|
2
|
+
message: "Elegant variation: '%s' — use the plain term."
|
|
3
|
+
level: suggestion
|
|
4
|
+
ignorecase: true
|
|
5
|
+
tokens:
|
|
6
|
+
- "the aforementioned"
|
|
7
|
+
- "the above-mentioned"
|
|
8
|
+
- "the previously mentioned"
|
|
9
|
+
- "this individual"
|
|
10
|
+
- "said individual"
|
|
11
|
+
- "the eponymous"
|
|
12
|
+
- "this entity"
|
|
13
|
+
- "the latter"
|
|
14
|
+
- "the former"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
extends: existence
|
|
2
|
+
message: "Hedging detected: '%s' — commit to a position or remove."
|
|
3
|
+
level: suggestion
|
|
4
|
+
ignorecase: true
|
|
5
|
+
tokens:
|
|
6
|
+
- "it could potentially"
|
|
7
|
+
- "it might potentially"
|
|
8
|
+
- "it may potentially"
|
|
9
|
+
- "arguably"
|
|
10
|
+
- "it remains to be seen"
|
|
11
|
+
- "only time will tell"
|
|
12
|
+
- "it is not unreasonable to"
|
|
13
|
+
- "one could argue"
|
|
14
|
+
- "some might say"
|
|
15
|
+
- "it would not be wrong to"
|
|
16
|
+
- "to some extent"
|
|
17
|
+
- "to a certain degree"
|
|
18
|
+
- "in some ways"
|
|
19
|
+
- "more or less"
|
|
20
|
+
- "for the most part"
|
|
21
|
+
- "by and large"
|
|
22
|
+
- "generally speaking"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
extends: existence
|
|
2
|
+
message: "AI phrasing detected: '%s' — rewrite in your natural voice."
|
|
3
|
+
level: warning
|
|
4
|
+
ignorecase: true
|
|
5
|
+
tokens:
|
|
6
|
+
# Exhausted openers
|
|
7
|
+
- "In today's"
|
|
8
|
+
- "In the realm of"
|
|
9
|
+
- "In an era of"
|
|
10
|
+
- "In the world of"
|
|
11
|
+
- "When it comes to"
|
|
12
|
+
- "It's worth noting that"
|
|
13
|
+
- "It's important to note"
|
|
14
|
+
- "It bears mentioning"
|
|
15
|
+
- "It goes without saying"
|
|
16
|
+
- "Needless to say"
|
|
17
|
+
- "As we all know"
|
|
18
|
+
- "At the end of the day"
|
|
19
|
+
- "Let's face it"
|
|
20
|
+
- "The fact of the matter is"
|
|
21
|
+
- "The bottom line is"
|
|
22
|
+
- "The reality is"
|
|
23
|
+
- "The truth is"
|
|
24
|
+
- "The thing is"
|
|
25
|
+
# Sycophantic filler
|
|
26
|
+
- "That's a great question"
|
|
27
|
+
- "That's an excellent question"
|
|
28
|
+
- "Great question"
|
|
29
|
+
- "Excellent question"
|
|
30
|
+
- "What a great"
|
|
31
|
+
- "Absolutely"
|
|
32
|
+
- "That's a fantastic"
|
|
33
|
+
# Filler transitions
|
|
34
|
+
- "With that being said"
|
|
35
|
+
- "Having said that"
|
|
36
|
+
- "That said"
|
|
37
|
+
- "On that note"
|
|
38
|
+
- "Moving forward"
|
|
39
|
+
- "Going forward"
|
|
40
|
+
- "Looking ahead"
|
|
41
|
+
- "To that end"
|
|
42
|
+
- "In light of this"
|
|
43
|
+
- "In light of the above"
|
|
44
|
+
- "With this in mind"
|
|
45
|
+
- "Bearing this in mind"
|
|
46
|
+
- "Taking this into account"
|
|
47
|
+
- "With the above in mind"
|
|
48
|
+
# Performative exploration
|
|
49
|
+
- "Let's explore"
|
|
50
|
+
- "Let's delve"
|
|
51
|
+
- "Let's unpack"
|
|
52
|
+
- "Let's dive into"
|
|
53
|
+
- "Let's take a closer look"
|
|
54
|
+
- "Let's examine"
|
|
55
|
+
- "shall we explore"
|
|
56
|
+
- "shall we delve"
|
|
57
|
+
# Filler conclusions
|
|
58
|
+
- "In conclusion"
|
|
59
|
+
- "To sum up"
|
|
60
|
+
- "To summarize"
|
|
61
|
+
- "In summary"
|
|
62
|
+
- "All in all"
|
|
63
|
+
- "By and large"
|
|
64
|
+
- "On the whole"
|
|
65
|
+
- "When all is said and done"
|
|
66
|
+
# Performative framing
|
|
67
|
+
- "It's crucial to"
|
|
68
|
+
- "It's essential to"
|
|
69
|
+
- "It's vital to"
|
|
70
|
+
- "It's imperative to"
|
|
71
|
+
- "play a crucial role"
|
|
72
|
+
- "play a vital role"
|
|
73
|
+
- "play a pivotal role"
|
|
74
|
+
- "play an important role"
|
|
75
|
+
- "serves as a testament"
|
|
76
|
+
- "serves as a reminder"
|
|
77
|
+
- "serves as a beacon"
|
|
78
|
+
# Unnecessary hedging stacks
|
|
79
|
+
- "might potentially"
|
|
80
|
+
- "could potentially"
|
|
81
|
+
- "may potentially"
|
|
82
|
+
- "would potentially"
|
|
83
|
+
- "seems to suggest"
|
|
84
|
+
- "appears to indicate"
|
|
85
|
+
- "tends to suggest"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
extends: existence
|
|
2
|
+
message: "Vague word: '%s' — be specific."
|
|
3
|
+
level: suggestion
|
|
4
|
+
ignorecase: true
|
|
5
|
+
tokens:
|
|
6
|
+
- "various"
|
|
7
|
+
- "several"
|
|
8
|
+
- "numerous"
|
|
9
|
+
- "a number of"
|
|
10
|
+
- "a wide range of"
|
|
11
|
+
- "a broad range of"
|
|
12
|
+
- "a variety of"
|
|
13
|
+
- "wide array of"
|
|
14
|
+
- "enables"
|
|
15
|
+
- "facilitates"
|
|
16
|
+
- "utilizes"
|
|
17
|
+
- "utilize"
|
|
18
|
+
- "leverages"
|
|
19
|
+
- "comprehensive solution"
|
|
20
|
+
- "innovative solution"
|
|
21
|
+
- "scalable solution"
|
|
22
|
+
- "world-class"
|
|
23
|
+
- "best-in-class"
|
|
24
|
+
- "state-of-the-art"
|
|
25
|
+
- "end-to-end"
|
|
26
|
+
- "full-stack"
|
|
27
|
+
- "next-generation"
|
|
28
|
+
- "enterprise-grade"
|
|
29
|
+
- "mission-critical"
|
|
30
|
+
- "production-ready"
|
|
31
|
+
- "battle-tested"
|
|
32
|
+
- "highly scalable"
|
|
33
|
+
- "highly available"
|
|
34
|
+
- "highly performant"
|
|
35
|
+
- "best practices"
|
|
36
|
+
- "industry standard"
|
|
37
|
+
- "gold standard"
|
|
38
|
+
- "significant"
|
|
39
|
+
- "meaningful"
|
|
40
|
+
- "impactful"
|
|
41
|
+
- "thoughtful"
|
|
42
|
+
- "intentional"
|
|
43
|
+
- "robust"
|
|
44
|
+
- "performant"
|
|
45
|
+
- "elegant"
|
|
46
|
+
- "powerful"
|
|
47
|
+
- "flexible"
|
|
48
|
+
- "extensible"
|
|
49
|
+
- "modular"
|
|
50
|
+
- "opinionated"
|
|
51
|
+
- "batteries-included"
|
|
52
|
+
- "turnkey"
|
|
53
|
+
- "frictionless"
|
|
54
|
+
- "effortless"
|
|
55
|
+
- "painless"
|
|
56
|
+
- "blazing fast"
|
|
57
|
+
- "lightning fast"
|
|
58
|
+
- "blazingly fast"
|
|
59
|
+
- "extremely fast"
|
|
60
|
+
- "incredibly fast"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# file-guard.sh — PreToolUse hook
|
|
3
|
+
# Blocks writes to protected paths. Exit 2 = hard block.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
|
|
8
|
+
|
|
9
|
+
if [ -z "$FILE_PATH" ]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# Default protected patterns
|
|
14
|
+
PROTECTED_PATTERNS=(
|
|
15
|
+
"go\.sum$"
|
|
16
|
+
"go\.mod$"
|
|
17
|
+
"\.lock$"
|
|
18
|
+
"\.lock\.json$"
|
|
19
|
+
"\.github/workflows/"
|
|
20
|
+
"^\.claude/"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Check against protected patterns
|
|
24
|
+
for pattern in "${PROTECTED_PATTERNS[@]}"; do
|
|
25
|
+
if echo "$FILE_PATH" | grep -qE "$pattern"; then
|
|
26
|
+
echo "BLOCKED: $FILE_PATH is protected. Explain the intended change before proceeding."
|
|
27
|
+
exit 2
|
|
28
|
+
fi
|
|
29
|
+
done
|
|
30
|
+
|
|
31
|
+
# Check project-level protected files from CLAUDE.md if it exists
|
|
32
|
+
if [ -f "CLAUDE.md" ]; then
|
|
33
|
+
# Extract paths from ## Protected Files section
|
|
34
|
+
IN_SECTION=false
|
|
35
|
+
while IFS= read -r line; do
|
|
36
|
+
if echo "$line" | grep -qE "^## Protected Files"; then
|
|
37
|
+
IN_SECTION=true
|
|
38
|
+
continue
|
|
39
|
+
fi
|
|
40
|
+
if $IN_SECTION && echo "$line" | grep -qE "^##"; then
|
|
41
|
+
break
|
|
42
|
+
fi
|
|
43
|
+
if $IN_SECTION && echo "$line" | grep -qE "^- "; then
|
|
44
|
+
PROTECTED=$(echo "$line" | sed 's/^- //' | sed 's/ *#.*//' | xargs)
|
|
45
|
+
if [ -n "$PROTECTED" ] && echo "$FILE_PATH" | grep -qE "$PROTECTED"; then
|
|
46
|
+
echo "BLOCKED: $FILE_PATH matches project-protected pattern '$PROTECTED'. Explain the intended change."
|
|
47
|
+
exit 2
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
done < CLAUDE.md
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
# Always exits 0 (never blocks session end).
|
|
4
4
|
set -uo pipefail
|
|
5
5
|
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active // false')" = "true" ]; then
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
6
11
|
CONFIG_FILE="$HOME/.claude/praxis.config.json"
|
|
7
12
|
|
|
8
13
|
# Find project CLAUDE.md by walking up from CWD
|