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
14 changes: 14 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,19 @@ if [[ -n "${STAGED_FILES}" ]] && [[ -n "${CGW_LINT_CMD:-}" ]]; then
fi
fi

# Optional: typecheck using CGW_TYPECHECK_CMD (non-blocking)
if [[ -n "${STAGED_FILES}" ]] && [[ -n "${CGW_TYPECHECK_CMD:-}" ]]; then
echo ""
echo "Checking types (non-blocking)..."
logfile=/dev/null
if cgw_run_typecheck >/dev/null 2>&1; then
echo " [PASS] Typecheck passed"
else
echo " [WARN] Type errors found"
echo " Run '${CGW_TYPECHECK_CMD} ${CGW_TYPECHECK_CHECK_ARGS:-check}' to see details"
echo " (Commit proceeds — fix types before pushing)"
fi
fi

echo ""
exit 0
15 changes: 15 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,18 @@ The shared module for all binary yes/no confirmation prompts in CGW scripts. Con
- `--non-interactive abort|accept|deny`: explicit non-interactive policy declared at the call site. When `CGW_NON_INTERACTIVE=1`: `abort` prints a message and exits 1; `accept` returns 0 silently; `deny` returns 1 silently. Callers own the `CGW_NON_INTERACTIVE=1` assignment from their `[[ ! -t 0 ]]` check — `cgw_confirm` does not test TTY internally.

**Callers**: every binary confirmation prompt in `bisect_helper.sh`, `branch_cleanup.sh`, `cherry_pick_commits.sh`, `commit_enhanced.sh`, `configure.sh`, `create_release.sh`, `merge_docs.sh`, `merge_with_validation.sh`, `push_validated.sh`, `rebase_safe.sh`, `rollback_merge.sh`, `setup_attributes.sh`, `stash_work.sh`, `sync_branches.sh`, `undo_last.sh`. The 3-way `(yes/no/skip)` prompt in `commit_enhanced.sh` stays inline — the helper is binary only.

---

## remote status

The shared module for querying remote reachability, remote branch existence, and commit distance between two refs. Concentrates a seam previously scattered across ~10 inline `git rev-list --count` and `git ls-remote` call sites in 6+ scripts, several of which were untested (notably `repo_health.sh`).

**Implementation seam** — three silent helpers in `scripts/git/_common.sh`:
- `cgw_rev_count <base> <tip>` — outputs `git rev-list --count "base..tip"` to stdout; exits non-zero on error (bad refs, git failure). No fallback — callers own their own `|| count=0` or `|| exit 1`. Accepts any git ref (branch names, remote tracking refs, SHAs).
- `cgw_remote_reachable <remote>` — exits 0 if the remote is reachable (probes via `git ls-remote HEAD`), non-zero otherwise.
- `cgw_remote_branch_exists <remote> <branch>` — exits 0 if `<branch>` exists on `<remote>`; builds `refs/heads/<branch>` internally so callers pass plain branch names.

All three helpers are silent: no stdout/stderr beyond `cgw_rev_count`'s count. Callers own all user-facing error messages.

**Callers**: `push_validated.sh` (remote reachability + ahead/behind), `sync_branches.sh` (ahead/behind), `create_pr.sh` (remote branch existence + commit distance), `validate_branches.sh` (ahead/behind), `repo_health.sh` (bidirectional ahead/behind per branch), `rebase_safe.sh` (ahead/behind), `undo_last.sh` (ahead/behind).
32 changes: 32 additions & 0 deletions cgw.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,38 @@ CGW_FORMAT_EXCLUDES="--exclude logs --exclude .venv"
# CGW_LINT_CMD=""
# CGW_FORMAT_CMD=""

# ============================================================================
# TYPECHECK CONFIGURATION
# ============================================================================
# Optional non-blocking typecheck step run by the pre-commit hook.
# Set CGW_TYPECHECK_CMD="" to disable (default).
#
# ── Python / pyrefly (recommended for new Python projects) ───────────────────
# CGW_TYPECHECK_CMD="pyrefly"
# CGW_TYPECHECK_CHECK_ARGS="check"
# CGW_TYPECHECK_EXCLUDES=""
#
# ── Python / pyright ─────────────────────────────────────────────────────────
# CGW_TYPECHECK_CMD="pyright"
# CGW_TYPECHECK_CHECK_ARGS=""
# CGW_TYPECHECK_EXCLUDES=""
#
# ── Python / mypy ────────────────────────────────────────────────────────────
# CGW_TYPECHECK_CMD="mypy"
# CGW_TYPECHECK_CHECK_ARGS="."
# CGW_TYPECHECK_EXCLUDES=""
#
# ── TypeScript / tsc ─────────────────────────────────────────────────────────
# CGW_TYPECHECK_CMD="tsc"
# CGW_TYPECHECK_CHECK_ARGS="--noEmit"
# CGW_TYPECHECK_EXCLUDES=""
#
# Skip at runtime with CGW_SKIP_TYPECHECK=1.

CGW_TYPECHECK_CMD=""
CGW_TYPECHECK_CHECK_ARGS="check"
CGW_TYPECHECK_EXCLUDES=""

# ============================================================================
# MODIFIED-ONLY LINT FILE EXTENSIONS (check_lint.sh, fix_lint.sh)
# ============================================================================
Expand Down
59 changes: 7 additions & 52 deletions command/auto-git-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ git diff --quiet && git diff --cached --quiet
./scripts/git/check_lint.sh
```

- Ignore errors in local-only files (CLAUDE.md, MEMORY.md, etc.) — never committed
- Lint failures in local-only files (CLAUDE.md, MEMORY.md, etc.) are safe to ignore — see SKILL.md Rule 3.
- If still fails: Stop workflow
- If passes: Continue to Phase 2

Expand Down Expand Up @@ -123,12 +123,7 @@ git add .
```

Replace message with appropriate conventional commit (feat:, fix:, docs:, chore:, test:).

`commit_enhanced.sh` automatically:
- Unstages local-only files (configured via `CGW_LOCAL_FILES`)
- Validates commit message format
- Runs lint check
- Respects pre-staged files: if you staged specific files and have other unstaged changes, commits pre-staged only (use `--all` to override)
For staging intent and local-file protection behavior, see SKILL.md Rules 3 and 5.

**Step 2.3: Capture commit info for final report**

Expand Down Expand Up @@ -173,14 +168,8 @@ echo "${CGW_MERGE_MODE:-direct}"
./scripts/git/merge_with_validation.sh --non-interactive
```

`merge_with_validation.sh` automatically:
- Creates backup tag
- Handles modify/delete conflicts (auto-resolved)
- Stops on content conflicts (requires manual resolution)
- Validates docs CI policy (if configured)

- If exit code 0: Continue to Phase 5
- If exit code ≠ 0: Check output for conflict type, stop workflow
- If exit code ≠ 0: Check output for conflict type, stop workflow and see `references/error-recovery.md`

---

Expand Down Expand Up @@ -265,45 +254,11 @@ instead of bypassing the wrappers — the Core Rules in SKILL.md are mandatory.

---

## Error Handling

### Lint Failures

```bash
./scripts/git/fix_lint.sh
./scripts/git/check_lint.sh
```

### Local-Only Files Staged

`commit_enhanced.sh` auto-unstages these. If using raw git:
```bash
git reset HEAD CLAUDE.md MEMORY.md
```

### Modify/Delete Conflicts

**Status**: EXPECTED — auto-resolved by `merge_with_validation.sh`

### Content Conflicts (UU)

Manual resolution required:
```bash
# Edit files to resolve
git add <resolved-files>
git commit
## Error Recovery

# Or abort
git merge --abort
git checkout "${CGW_SOURCE_BRANCH:-development}"
```

### Push Failures

```bash
./scripts/git/sync_branches.sh # sync with remote first
./scripts/git/push_validated.sh # retry push
```
For lint failures, conflict types (modify/delete vs content), push errors, and
lock-file issues — see
[`references/error-recovery.md`](../skills/auto-git-workflow/references/error-recovery.md).

---

Expand Down
44 changes: 44 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ cp cgw.conf.example .cgw.conf
| `CGW_MARKDOWNLINT_ARGS` | `**/*.md !CLAUDE.md !MEMORY.md` | Arguments passed to markdown lint tool |
| `CGW_SKIP_LINT` | `(unset)` | Set to `1` to skip all lint checks at runtime |
| `CGW_SKIP_MD_LINT` | `(unset)` | Set to `1` to skip only the markdown lint step |
| `CGW_TYPECHECK_CMD` | `` | Typecheck tool; set to e.g. `pyrefly` to enable (`""` to disable) |
| `CGW_TYPECHECK_CHECK_ARGS` | `check` | Arguments passed to the typecheck tool |
| `CGW_TYPECHECK_EXCLUDES` | `` | Exclusion flags appended to the typecheck command |
| `CGW_SKIP_TYPECHECK` | `(unset)` | Set to `1` to skip the typecheck step at runtime |
| `CGW_STAGED_ONLY` | `0` | Set to `1` to commit only pre-staged files (`commit_enhanced.sh`) |
| `CGW_ALL` | `(unset)` | Set to `1` to force-stage all tracked changes, overriding pre-staged-only logic (`commit_enhanced.sh`) |
| `CGW_EXTRA_PREFIXES` | `` | Extra commit prefixes (pipe-separated, e.g. `cuda\|tensorrt`) |
Expand Down Expand Up @@ -160,3 +164,43 @@ CGW_FORMAT_FIX_ARGS="-i -r ."
CGW_LINT_CMD=""
CGW_FORMAT_CMD=""
```

---

## Typecheck

The pre-commit hook runs a non-blocking typecheck step when `CGW_TYPECHECK_CMD` is set. Like the lint step, it surfaces warnings but never blocks the commit. Set `CGW_SKIP_TYPECHECK=1` to skip it at runtime (e.g. in CI where a dedicated type-check job runs separately).

### Python / pyrefly (recommended)

```bash
CGW_TYPECHECK_CMD="pyrefly"
CGW_TYPECHECK_CHECK_ARGS="check"
```

### Python / pyright

```bash
CGW_TYPECHECK_CMD="pyright"
CGW_TYPECHECK_CHECK_ARGS=""
```

### Python / mypy

```bash
CGW_TYPECHECK_CMD="mypy"
CGW_TYPECHECK_CHECK_ARGS="."
```

### TypeScript / tsc

```bash
CGW_TYPECHECK_CMD="tsc"
CGW_TYPECHECK_CHECK_ARGS="--noEmit"
```

### Disable typecheck

```bash
CGW_TYPECHECK_CMD=""
```
14 changes: 14 additions & 0 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,19 @@ if [[ -n "${STAGED_FILES}" ]] && [[ -n "${CGW_LINT_CMD:-}" ]]; then
fi
fi

# Optional: typecheck using CGW_TYPECHECK_CMD (non-blocking)
if [[ -n "${STAGED_FILES}" ]] && [[ -n "${CGW_TYPECHECK_CMD:-}" ]]; then
echo ""
echo "Checking types (non-blocking)..."
logfile=/dev/null
if cgw_run_typecheck >/dev/null 2>&1; then
echo " [PASS] Typecheck passed"
else
echo " [WARN] Type errors found"
echo " Run '${CGW_TYPECHECK_CMD} ${CGW_TYPECHECK_CHECK_ARGS:-check}' to see details"
echo " (Commit proceeds — fix types before pushing)"
fi
fi

echo ""
exit 0
20 changes: 20 additions & 0 deletions scripts/dev/sync-skill.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# scripts/dev/sync-skill.sh
# Re-sync the git-ignored local .claude/ install from the canonical sources.
# Run this after editing skill/ or command/ to refresh the active in-project
# Claude Code skill and slash command without re-running the full configure.sh.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

echo "Syncing skill/ and command/ → .claude/ ..."

mkdir -p "$REPO_ROOT/.claude/skills/auto-git-workflow/references"
mkdir -p "$REPO_ROOT/.claude/commands"

cp "$REPO_ROOT/skill/SKILL.md" "$REPO_ROOT/.claude/skills/auto-git-workflow/SKILL.md"
cp "$REPO_ROOT/skill/references/"*.md "$REPO_ROOT/.claude/skills/auto-git-workflow/references/"
cp "$REPO_ROOT/command/auto-git-workflow.md" "$REPO_ROOT/.claude/commands/auto-git-workflow.md"

echo "Done. Local install matches source."
53 changes: 52 additions & 1 deletion scripts/git/_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,28 @@ validate_branch_pair() {
fi
}

# cgw_rev_count <base> <tip>
# Outputs the number of commits reachable from <tip> but not from <base>.
# Accepts any git ref (branch names, remote-tracking refs, SHAs).
# Exits non-zero on git failure; callers own their fallback (e.g. || echo "0").
cgw_rev_count() {
git rev-list --count "${1}..${2}" 2>/dev/null
}

# cgw_remote_reachable <remote>
# Exits 0 when <remote> is reachable, non-zero otherwise. Silent.
# Uses git ls-remote with no ref pattern — exits non-zero on any connection failure.
cgw_remote_reachable() {
git ls-remote "${1}" >/dev/null 2>&1
}

# cgw_remote_branch_exists <remote> <branch>
# Exits 0 when <branch> exists on <remote>, non-zero otherwise. Silent.
# Accepts a plain branch name; builds refs/heads/ internally.
cgw_remote_branch_exists() {
git ls-remote --exit-code "${1}" "refs/heads/${2}" >/dev/null 2>&1
}

# ensure_no_stale_index_lock - Detect and auto-remove abandoned .git/index.lock files.
#
# Stale locks (left by crashed/killed git processes) cause:
Expand Down Expand Up @@ -693,7 +715,7 @@ cgw_print_conflict_summary() {

# ── lint pipeline module ───────────────────────────────────────────────────────
# Shared helpers for venv-aware binary resolution, file-list selection, lint
# check, format check, lint/format fix, and markdownlint. Callers:
# check, format check, lint/format fix, markdownlint, and typecheck. Callers:
# commit_enhanced.sh, check_lint.sh, fix_lint.sh, .githooks/pre-commit.
#
# cgw_resolve_lint_binary <cmd>
Expand Down Expand Up @@ -840,6 +862,35 @@ cgw_run_markdownlint_check() {
fi
}

# cgw_run_typecheck [files...]
# Runs ${CGW_TYPECHECK_CMD} against the project (no files) or a given file
# list (strips trailing path token from CGW_TYPECHECK_CHECK_ARGS when files
# given). Honors CGW_SKIP_TYPECHECK=1 and empty CGW_TYPECHECK_CMD (returns 0,
# emits skip line). Reads ${logfile} from caller scope. Returns 0 = clean,
# 1 = errors found.
cgw_run_typecheck() {
if [[ "${CGW_SKIP_TYPECHECK:-0}" == "1" ]]; then
echo " (typecheck skipped -- CGW_SKIP_TYPECHECK=1)"
return 0
fi
if [[ -z "${CGW_TYPECHECK_CMD:-}" ]]; then
echo " (typecheck skipped -- CGW_TYPECHECK_CMD not set)"
return 0
fi
get_python_path 2>/dev/null || true
local tc_bin
tc_bin=$(cgw_resolve_lint_binary "${CGW_TYPECHECK_CMD}")
if [[ $# -gt 0 ]]; then
local stripped_args
stripped_args=$(cgw_strip_path_arg "${CGW_TYPECHECK_CHECK_ARGS-check}")
# shellcheck disable=SC2086 # Word splitting intentional: stripped_args contains multiple flags
run_tool_with_logging "TYPECHECK" "${logfile}" "${tc_bin}" ${stripped_args} "$@"
else
# shellcheck disable=SC2086 # Word splitting intentional: CGW_TYPECHECK_CHECK_ARGS/CGW_TYPECHECK_EXCLUDES contain multiple flags
run_tool_with_logging "TYPECHECK" "${logfile}" "${tc_bin}" ${CGW_TYPECHECK_CHECK_ARGS-check} ${CGW_TYPECHECK_EXCLUDES:-}
fi
}

# ── commit-message format module ───────────────────────────────────────────────
# Validates commit messages against the conventional-commit prefix grammar.
#
Expand Down
Loading
Loading