@pasajero_0/agent-stack 0.1.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.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse(Bash) guard — enforces hard prohibitions.
3
+ set -euo pipefail
4
+
5
+ cmd=$(jq -r '.tool_input.command // empty')
6
+ [ -z "$cmd" ] && exit 0
7
+
8
+ if printf '%s' "$cmd" | grep -Eq '(^|[&|;]|&&)[[:space:]]*git[[:space:]]+push\b'; then
9
+ echo "Blocked: 'git push' is manual-only — the team pushes by hand." >&2
10
+ exit 2
11
+ fi
12
+
13
+ # Package-manager guard. This repo is {{PM}}-only.
14
+ if printf '%s' "$cmd" | grep -Eq '(^|[&|;]|&&)[[:space:]]*({{FORBIDDEN_PM}})[[:space:]]'; then
15
+ echo "Blocked: this repo uses {{PM}} only." >&2
16
+ exit 2
17
+ fi
18
+
19
+ exit 0
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse(Edit|Write|MultiEdit) guard — protects generated + secret files.
3
+ set -euo pipefail
4
+
5
+ path=$(jq -r '.tool_input.file_path // empty')
6
+ [ -z "$path" ] && exit 0
7
+
8
+ # Auto-generated build outputs — never hand-edit.
9
+ case "$path" in
10
+ {{GEN_GLOBS}})
11
+ echo "Blocked: this file is auto-generated (build output) — never edit it by hand." >&2
12
+ exit 2
13
+ ;;
14
+ esac
15
+
16
+ # Lockfile — regenerate via the package manager, never hand-edit.
17
+ case "$path" in
18
+ *{{LOCKFILE}})
19
+ echo "Blocked: {{LOCKFILE}} is generated by {{PM}} — never edit it by hand." >&2
20
+ exit 2
21
+ ;;
22
+ esac
23
+
24
+ case "$path" in
25
+ *.env|*.env.*)
26
+ jq -n '{
27
+ hookSpecificOutput: {
28
+ hookEventName: "PreToolUse",
29
+ permissionDecision: "ask",
30
+ permissionDecisionReason: "Editing an env file — it may contain secrets. Confirm before writing."
31
+ }
32
+ }'
33
+ exit 0
34
+ ;;
35
+ esac
36
+
37
+ exit 0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # PostToolUse(Edit|Write|MultiEdit) — lint edited source files, surface issues as additionalContext.
3
+ # Does NOT modify the file (no --fix) — keeps Claude's file-state tracking consistent.
4
+ # NOTE: this repo currently has no linter installed, so this hook self-disables
5
+ # (the `[ -x "$LINTER" ] || exit 0` guard). Install a linter or point LINTER at it to enable.
6
+ set -euo pipefail
7
+
8
+ input=$(cat)
9
+ path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
10
+ [ -z "$path" ] && exit 0
11
+
12
+ case "$path" in
13
+ *.ts|*.tsx|*.js|*.jsx|*.mts|*.cts) ;;
14
+ *) exit 0 ;;
15
+ esac
16
+
17
+ case "$path" in
18
+ *.gen.*|*/node_modules/*|*/dist/*|*/build/*|*/storybook-static/*|*/coverage/*) exit 0 ;;
19
+ esac
20
+
21
+ LINTER="${CLAUDE_PROJECT_DIR:-$(pwd)}/node_modules/.bin/eslint"
22
+ [ -x "$LINTER" ] || LINTER="$(command -v eslint || true)"
23
+ [ -x "$LINTER" ] || exit 0
24
+
25
+ CACHE_DIR=/tmp/claude-eslint-cache
26
+ mkdir -p "$CACHE_DIR"
27
+
28
+ output=$(timeout 20 "$LINTER" \
29
+ --cache \
30
+ --cache-location "$CACHE_DIR/" \
31
+ --cache-strategy content \
32
+ --format=json \
33
+ --no-warn-ignored \
34
+ "$path" 2>/dev/null) || true
35
+
36
+ # Parse only if output is valid JSON (eslint may bail on config errors)
37
+ echo "$output" | jq empty 2>/dev/null || exit 0
38
+
39
+ filtered=$(echo "$output" | jq -r '
40
+ .[].messages[]?
41
+ | select(.severity >= 1)
42
+ | " \(if .severity==2 then "✘" else "⚠" end) line \(.line):\(.column) \(.message) (\(.ruleId // "—"))"
43
+ ' 2>/dev/null)
44
+
45
+ if [ -n "$filtered" ]; then
46
+ jq -nc --arg ctx "Linter issues in $(basename "$path"):
47
+ $filtered" '{
48
+ hookSpecificOutput: {
49
+ hookEventName: "PostToolUse",
50
+ additionalContext: $ctx
51
+ }
52
+ }'
53
+ fi
54
+
55
+ exit 0
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+ # PostToolUse(Edit|Write|MultiEdit) — D12 post-apply verify.
3
+ # Active ONLY when at least one migration plan exists in .claude/tmp/.
4
+ # Outside active migrations: zero noise, near-zero latency.
5
+ #
6
+ # Caveat: diff is git working-tree vs HEAD, so it shows ALL uncommitted
7
+ # changes to the file, not only the change from the just-completed tool call.
8
+ # That caveat is surfaced in the additionalContext so Claude does not
9
+ # over-interpret unrelated edits.
10
+ #
11
+ # `set -e` and `pipefail` deliberately omitted: `git diff | head -200`
12
+ # triggers SIGPIPE on diffs >200 lines, which would crash the hook under
13
+ # pipefail. Defensive philosophy: always exit 0.
14
+ #
15
+ # JSON build: python3 (stdin + UTF-8 "replace" decode) preferred — survives
16
+ # non-UTF-8 bytes from arbitrary diff content. Fallback: `jq --rawfile` via
17
+ # temp file if python3 is absent.
18
+ #
19
+ # Historical note: do NOT use `echo "$out" | jq .` to post-process — the hook
20
+ # runner may exec `zsh -c`, and zsh's `echo` interprets `\n` escapes, turning
21
+ # valid escaped JSON back into raw LF before parsing. Use `printf '%s'`.
22
+ set -u
23
+
24
+ # (1) Early-exit if no active migration plan.
25
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
26
+ PLAN_DIR="$PROJECT_DIR/.claude/tmp"
27
+ ls "$PLAN_DIR"/*-plan.md >/dev/null 2>&1 || exit 0
28
+
29
+ # (2) Parse file path from tool_input.
30
+ input=$(cat)
31
+ path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
32
+ [ -z "$path" ] && exit 0
33
+
34
+ # (3) Normalize to absolute path so case-glob matches reliably for relative paths.
35
+ case "$path" in
36
+ /*) abs_path="$path" ;;
37
+ *) abs_path="$PROJECT_DIR/$path" ;;
38
+ esac
39
+
40
+ # (4) Skip noise paths (same filter spirit as post-edit-lint.sh).
41
+ case "$abs_path" in
42
+ *.gen.*|*/node_modules/*|*/dist/*|*/build/*|*/storybook-static/*|*/coverage/*) exit 0 ;;
43
+ esac
44
+
45
+ # (5) Build diff: tracked → git diff -U2 (truncated 200 lines); untracked → head -50.
46
+ cd "$PROJECT_DIR" 2>/dev/null || exit 0
47
+ if git ls-files --error-unmatch -- "$abs_path" >/dev/null 2>&1; then
48
+ diff_output=$(timeout 10 git diff -U2 -- "$abs_path" 2>/dev/null | head -200)
49
+ else
50
+ diff_output=$(head -50 "$abs_path" 2>/dev/null)
51
+ fi
52
+ [ -z "$diff_output" ] && exit 0
53
+
54
+ # (6) Build ctx then emit JSON.
55
+ ctx="POST-APPLY VERIFY (D12) $abs_path:
56
+ $diff_output
57
+
58
+ (diff may include earlier uncommitted changes to this file)
59
+
60
+ Compare against the approved preview table. Report any mismatch before marking the step done."
61
+
62
+ if command -v python3 >/dev/null 2>&1; then
63
+ printf '%s' "$ctx" | python3 -c 'import json,sys; print(json.dumps({"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":sys.stdin.buffer.read().decode("utf-8","replace")}}))'
64
+ else
65
+ tmpf=$(mktemp)
66
+ printf '%s' "$ctx" > "$tmpf"
67
+ jq -nc --rawfile ctx "$tmpf" '{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:$ctx}}'
68
+ rm -f "$tmpf"
69
+ fi
70
+
71
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart — write start-marker for Stop hook + inject git context (only on fresh starts).
3
+ set -euo pipefail
4
+
5
+ input=$(cat)
6
+ session_id=$(echo "$input" | jq -r '.session_id // "unknown"')
7
+ cwd=$(echo "$input" | jq -r '.cwd // "."')
8
+ src=$(echo "$input" | jq -r '.source // "startup"')
9
+
10
+ mkdir -p /tmp/claude-sessions
11
+ # Sweep stale markers (>24h) so /tmp doesn't grow forever
12
+ find /tmp/claude-sessions -maxdepth 1 -type f -name '*.start' -mtime +1 -delete 2>/dev/null || true
13
+
14
+ # Always write a fresh marker for this session — Stop hook compares memory mtimes against it
15
+ touch "/tmp/claude-sessions/$session_id.start"
16
+
17
+ # Heavy git context: only on a truly new context window
18
+ case "$src" in
19
+ startup|clear)
20
+ if git -C "$cwd" rev-parse 2>/dev/null >/dev/null; then
21
+ branch=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)
22
+ last=$(git -C "$cwd" log -1 --pretty='%h %s' 2>/dev/null || echo none)
23
+ changed=$(git -C "$cwd" status --short 2>/dev/null | head -20)
24
+ echo "## Session context"
25
+ echo
26
+ echo "- Branch: \`$branch\`"
27
+ echo "- Last commit: $last"
28
+ if [ -n "$changed" ]; then
29
+ echo "- Working tree (first 20 entries):"
30
+ echo '```'
31
+ echo "$changed"
32
+ echo '```'
33
+ else
34
+ echo "- Working tree: clean"
35
+ fi
36
+ echo
37
+ fi
38
+ ;;
39
+ esac
40
+
41
+ # Active migration plan(s) — fire on every source. Cheap (3 lines); most valuable
42
+ # on `compact` where prior nuances were summarized away.
43
+ if ls "${CLAUDE_PROJECT_DIR:-$cwd}"/.claude/tmp/*-plan.md >/dev/null 2>&1; then
44
+ echo "⚠️ Active migration plan(s):"
45
+ ls "${CLAUDE_PROJECT_DIR:-$cwd}"/.claude/tmp/*-plan.md
46
+ echo "Re-read before acting; use /migration to continue."
47
+ echo
48
+ fi
49
+
50
+ # Pending /reflect notice — always (cheap), regardless of source
51
+ if [ -f /tmp/.claude-reflect-pending ]; then
52
+ echo "💡 Memory was modified in the previous session — consider running \`/reflect\` to consolidate."
53
+ echo
54
+ rm -f /tmp/.claude-reflect-pending
55
+ fi
56
+
57
+ exit 0
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # Stop — fires every assistant turn. Keep work tiny.
3
+ # If any memory file changed since session start, raise the /reflect-pending flag.
4
+ set -euo pipefail
5
+
6
+ input=$(cat)
7
+ session_id=$(echo "$input" | jq -r '.session_id // "unknown"')
8
+ marker="/tmp/claude-sessions/$session_id.start"
9
+ [ -f "$marker" ] || exit 0
10
+
11
+ # Self-derive the auto-memory dir slug from the session cwd (every "/" → "-").
12
+ cwd=$(echo "$input" | jq -r '.cwd // empty')
13
+ [ -z "$cwd" ] && exit 0
14
+ slug="${cwd//\//-}"
15
+ MEM="$HOME/.claude/projects/${slug}/memory"
16
+ [ -d "$MEM" ] || exit 0
17
+
18
+ # -quit stops at first match → ~few ms even if memory has many files
19
+ if find "$MEM" -maxdepth 1 -name '*.md' -newer "$marker" -print -quit 2>/dev/null | grep -q .; then
20
+ touch /tmp/.claude-reflect-pending
21
+ fi
22
+
23
+ # Do NOT remove the marker — Stop fires on every turn within the same session.
24
+ exit 0
@@ -0,0 +1,33 @@
1
+ # Path-scoped rules
2
+
3
+ This directory holds **path-scoped rules** — narrow, machine-checkable conventions that activate
4
+ only when a matching file is touched (frontmatter `paths:` glob).
5
+
6
+ **Rules are NOT shipped or copied — they are generated per-repo.** `agent-stack` deliberately leaves
7
+ this directory empty at deploy time, because rules must reflect *this* codebase's real conventions,
8
+ not another project's.
9
+
10
+ To generate them: open `claude` in this repo and run the **pattern-scout** subagent against your
11
+ code and architecture docs. It drafts rules marked `[DRAFT]`; review them, keep what fits, delete
12
+ the rest.
13
+
14
+ Each rule file looks like:
15
+
16
+ ```markdown
17
+ ---
18
+ description: <one line — what this rule enforces and when it fires>
19
+ paths:
20
+ - "<glob, e.g. src/**/*.ts>"
21
+ ---
22
+
23
+ # <Rule name>
24
+
25
+ ## Do
26
+ - <convention> — detect: `<grep regex>` — severity: critical/warning/suggestion
27
+
28
+ ## Don't
29
+ - <banned pattern> — detect: `<grep regex>` — severity: ...
30
+
31
+ ## Why
32
+ <the failure mode this rule prevents>
33
+ ```
@@ -0,0 +1,22 @@
1
+ # CI / Testing Patterns
2
+
3
+ Validated knowledge from this project's test + CI setup. Replace with **your project's** runner and
4
+ CI provider as you learn them.
5
+
6
+ ## Test runner
7
+
8
+ - Config: `<test config file>`; run focused with `<runner> <substring>`.
9
+ - Test the logic-heavy modules; mock external processes/IO, not internal modules.
10
+
11
+ ## CI
12
+
13
+ A minimal pipeline on PR:
14
+ - install dependencies (frozen lockfile)
15
+ - typecheck
16
+ - test
17
+ - lint commit messages (if the repo enforces a commit convention)
18
+
19
+ ## Gotchas
20
+
21
+ Keep a running list of tool issues that bit CI (version quirks, cache keys, runtime drift), one
22
+ line each, so the next debugging session starts from known causes.
@@ -0,0 +1,35 @@
1
+ ---
2
+ description: Drive a multi-step migration or refactor against a plan file in `.claude/tmp/<task>-plan.md`. Loads the active plan into context, walks the next pending step under the plan's invariants, marks completed steps. Trigger when the user says "start migration", "continue migration", "next migration step", or similar.
3
+ disable-model-invocation: false
4
+ ---
5
+
6
+ ## Active plan(s)
7
+
8
+ <!-- Path resolution: `${CLAUDE_SKILL_DIR%/skills/*}/tmp` strips `/skills/<name>` from this skill's directory to get `<repo>/.claude/`, then appends `/tmp`. Requires this SKILL.md to live at `<repo>/.claude/skills/migration/SKILL.md` — moving the directory breaks the resolver. -->
9
+
10
+ !`d="${CLAUDE_SKILL_DIR%/skills/*}/tmp"; if ls "$d"/*-plan.md >/dev/null 2>&1; then for f in "$d"/*-plan.md; do printf '\n=== %s ===\n' "$f"; cat "$f"; done; else echo "(no active plan in $d — create one first)"; fi`
11
+
12
+ ## How to drive a migration
13
+
14
+ 1. **If no plan is loaded above** — draft one from the decisions in the current conversation.
15
+ Capture every committed invariant (numbered `Dn` style: scope, format, deviation policy,
16
+ completion gate), plus a top-level `## Open questions` section recording every unresolved
17
+ question raised by the user; it stays open until that question is explicitly closed. Save to
18
+ `.claude/tmp/<task>-plan.md`, show it to the user, and wait for explicit approval. Re-read the
19
+ saved plan from disk before proposing the first step.
20
+
21
+ 2. **If a plan is loaded above** — identify the next pending step from the checklist at the bottom
22
+ of the plan. Re-read the affected source files (do NOT cite from memory). Open the diff preview
23
+ with a compliance header that names which invariants are honoured — for example:
24
+ `Plan check: D1, D4 — ok; D3 — n/a`. Each invariant touched must be either honoured or flagged
25
+ as a **deviation** (`ОТКЛОНЕНИЕ ОТ ПЛАНА`) with rationale, awaiting explicit approval before the
26
+ diff appears.
27
+
28
+ 3. **After applying a step** — verify the actual diff against the approved preview (call this the
29
+ D12 verify: run `git diff` or re-read the changed lines; a divergence in either direction must
30
+ be surfaced and explicitly accepted before the step is marked done). Then mark its checklist
31
+ entry as `[x]` in the plan file with a one-line note about what landed. Run any gate the plan
32
+ specifies (e.g. a `grep` clean of old paths) before declaring the step done.
33
+
34
+ 4. **When every step is checked off** — propose deleting the plan file, after confirming with the
35
+ user that the migration is fully landed and verified.
@@ -0,0 +1,56 @@
1
+ ---
2
+ description: Draft a GitHub pull request description for the current branch following a compact template (what / scenarios / verification). Use when the user asks to write, draft, or generate a PR description for the current branch.
3
+ allowed-tools: Bash(git rev-parse:*), Bash(git log:*), Bash(git diff:*)
4
+ ---
5
+
6
+ ## Current branch state
7
+
8
+ - Branch: !`git rev-parse --abbrev-ref HEAD`
9
+ - Commits ahead of {{MAIN_BRANCH}} (origin first, local fallback):
10
+
11
+ !`git log origin/{{MAIN_BRANCH}}..HEAD --oneline 2>/dev/null | head -30 || git log {{MAIN_BRANCH}}..HEAD --oneline 2>/dev/null | head -30 || echo "(no upstream or local {{MAIN_BRANCH}} branch found)"`
12
+
13
+ - Files changed (top 20):
14
+
15
+ !`git diff origin/{{MAIN_BRANCH}}...HEAD --stat --stat-count=20 2>/dev/null || git diff {{MAIN_BRANCH}}...HEAD --stat --stat-count=20 2>/dev/null || echo "(diff unavailable)"`
16
+
17
+ ## Style rules
18
+
19
+ - "What" block ≤ 2 sentences. State user-visible behaviour / CLI surface change; no implementation
20
+ detail. If you need more than 2 sentences, the scope is too wide — split.
21
+ - "Scenarios" use imperative + arrow notation: `<command> → <expected>`. Not full sentences.
22
+ - For a CLI, scenarios are commands the reviewer runs (e.g. `agent-stack detect → lists Claude
23
+ Code`); state which directory / fixture to run them in if it matters.
24
+ - Link the GitHub issue narratively or with `Closes #NN` — this repo has no Jira.
25
+
26
+ ## Delivery format
27
+
28
+ Wrap the entire PR body in a **single fenced code block** (use four backticks if the body itself
29
+ contains code fences). The user copies the block once; GitHub renders markdown on paste. Do NOT
30
+ render markdown inline in the reply — bold/lists/headers won't survive the clipboard hop.
31
+
32
+ ## Template
33
+
34
+ ````markdown
35
+ **What this PR does / why we need it:**
36
+
37
+ <One- or two-sentence summary of the user-visible / CLI behaviour change.>
38
+
39
+ **Scenarios**
40
+
41
+ 1. **<Scenario name>** — `<command>` → <expected outcome>.
42
+ 2. **<Scenario name>** — `<command>` → <expected outcome>.
43
+
44
+ **Verification**
45
+
46
+ - `{{PM}} typecheck && {{PM}} test` — green.
47
+
48
+ Closes #<issue>
49
+ ````
50
+
51
+ ## When to deviate
52
+
53
+ - **Bug-fix PR:** add a "**Reproduction**" line above "Scenarios" with exact repro steps, then
54
+ scenarios verify the fix.
55
+ - **Pure refactor / chore:** drop "Scenarios"; replace with "No behaviour change — verified by
56
+ `{{PM}} test`".
@@ -0,0 +1,22 @@
1
+ # Repository Structure
2
+
3
+ Template for documenting this repo's layout so agents can locate code and respect boundaries. Fill
4
+ the tables with **this project's** actual structure.
5
+
6
+ ## Layout
7
+
8
+ | Path | Purpose |
9
+ | -------- | ---------------- |
10
+ | `<path>` | <what lives here> |
11
+
12
+ ## Tooling
13
+
14
+ - **Package manager**: {{PM}} only.
15
+ - **Build**: `<build command>` → `<output dir>`.
16
+ - **Typecheck / static gate**: `<typecheck command>`.
17
+ - **Test**: `<test command>`.
18
+
19
+ ## Where new files go
20
+
21
+ - New <unit> → `<path>` (+ register in `<entry>`).
22
+ - New test → mirror the source path under the test dir.
@@ -0,0 +1,31 @@
1
+ # Team Conventions
2
+
3
+ Fill each section with **this project's** actual rules. The structure below is the transferable
4
+ part; the values are placeholders the deploying agent (or you) should replace.
5
+
6
+ ## Commits
7
+
8
+ Format: `type(scope): message` (Conventional Commits if the repo runs commitlint).
9
+
10
+ **Types**: feat, fix, chore, refactor, test, docs, style, perf, ci, build, revert
11
+ **Scopes**: enumerate the areas the project uses as scopes.
12
+ **Issue reference**: how this repo links work (e.g. `Closes #NN`).
13
+
14
+ ## Branches
15
+
16
+ Main branch: `{{MAIN_BRANCH}}`. `git push` is manual-only (the guard-bash hook blocks the agent).
17
+
18
+ ## Git hooks
19
+
20
+ - `pre-commit`: <linter/typecheck on staged files, or empty>.
21
+ - `commit-msg`: <commit-message linting, if any>.
22
+
23
+ ## Code style
24
+
25
+ Capture the load-bearing rules: typing strictness, import conventions, formatter settings, naming.
26
+
27
+ ## Hard prohibitions
28
+
29
+ - **{{PM}} only** — other package managers are blocked by the guard-bash hook.
30
+ - **Never `git push`** from the agent — manual-only.
31
+ - **Never hand-edit generated files** ({{GEN_GLOBS}}, {{LOCKFILE}}).