Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .claude/hooks-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
# "git" — git コマンドブロック (jj 利用プロジェクト向け)
# "jj-immutable" — jj --ignore-immutable
# "jj-main-guard" — jj new main / jj edit main
# "jj-push-guard" — jj git push / jj push 禁止 (pnpm push に誘導)
# "electron" — Electron GUI 実行ブロック
# プリセット名以外の文字列は正規表現としてカスタムパターン扱い
blocked_patterns = [
"default",
"git",
"jj-immutable",
"jj-main-guard",
"jj-push-guard",
"electron",
]

Expand Down Expand Up @@ -69,3 +71,32 @@ cmd = "pnpm test:e2e"
[[stop_quality.steps]]
name = "build"
cmd = "pnpm build"

# ─── Push Pipeline: push 前パイプライン ───
#
# pnpm push で起動されるパイプライン。
# PreToolUse の "jj-push-guard" プリセットにより直接の jj git push はブロックされ、
# このパイプラインを通すことが強制される。
#
# ステップの type:
# "command" — シェルコマンドを実行。exit code 0 で成功判定
# "ai" — AI 処理が必要なステップ (将来実装、現在は placeholder)

[push_pipeline]
step_timeout = 120
push_cmd = "jj git push"

[[push_pipeline.steps]]
name = "integration_test"
type = "command"
cmd = "pnpm test"

[[push_pipeline.steps]]
name = "ai_review"
type = "ai"
prompt = "review_changes"

[[push_pipeline.steps]]
name = "commit_refactor"
type = "ai"
prompt = "optimize_commit_structure"
41 changes: 39 additions & 2 deletions .claude/hooks-pre-tool-validate/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,39 @@ GUI アプリケーションは Claude Code のヘッドレス環境では動作
]
}

/// プリセット: jj-push-guard (jj git push / jj push を禁止し pnpm push に誘導)
fn preset_jj_push_guard() -> Vec<BlockedPattern> {
vec![
BlockedPattern {
pattern: Regex::new(r#"(?im)(^|&&|;|\|\||\||&)\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)*jj\s+git\s+push(\s|$)"#).unwrap(),
message: r#"**jj git push がブロックされました**

直接の push は禁止されています。push 前パイプライン(テスト・レビュー)を通す必要があります。

**代わりに以下を実行してください:**
```
pnpm push
```

これにより、テスト実行 → レビュー → push が一括で行われます。"#,
},
BlockedPattern {
pattern: Regex::new(r#"(?im)(^|&&|;|\|\||\||&)\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)*jj\s+push(\s|$)"#).unwrap(),
message: r#"**jj push がブロックされました**

`jj push` は非推奨です。代わりに `jj git push` を使用しますが、
直接の push は禁止されています。push 前パイプラインを通す必要があります。

**代わりに以下を実行してください:**
```
pnpm push
```

これにより、テスト実行 → レビュー → push が一括で行われます。"#,
},
]
}

/// 設定ファイルに基づいてブロックパターンを構築
fn build_blocked_patterns(config: &Config) -> Vec<BlockedPattern> {
let preset_names: Vec<String> = config
Expand All @@ -245,6 +278,7 @@ fn build_blocked_patterns(config: &Config) -> Vec<BlockedPattern> {
"git".to_string(),
"jj-immutable".to_string(),
"jj-main-guard".to_string(),
"jj-push-guard".to_string(),
"electron".to_string(),
]
});
Expand All @@ -256,6 +290,7 @@ fn build_blocked_patterns(config: &Config) -> Vec<BlockedPattern> {
"git" => patterns.extend(preset_git()),
"jj-immutable" => patterns.extend(preset_jj_immutable()),
"jj-main-guard" => patterns.extend(preset_jj_main_guard()),
"jj-push-guard" => patterns.extend(preset_jj_push_guard()),
"electron" => patterns.extend(preset_electron()),
custom => {
// プリセット名以外はカスタム正規表現として扱う
Expand Down Expand Up @@ -630,8 +665,10 @@ mod tests {
// --- git: allowed commands (should NOT block) ---

#[test]
fn allows_jj_git_push() {
assert!(!is_blocked("jj git push"));
fn blocks_jj_git_push() {
// jj-push-guard プリセットにより、直接の jj git push はブロックされる
// pnpm push 経由でのみ push を許可する設計
assert!(is_blocked("jj git push"));
}

#[test]
Expand Down
189 changes: 189 additions & 0 deletions .claude/hooks-push-pipeline/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .claude/hooks-push-pipeline/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "hooks-push-pipeline"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"

[profile.release]
opt-level = 3
lto = true
strip = true
Loading