mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-17 23:53:30 +08:00
Compare commits
11 Commits
3bc8672432
...
b2285e870a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2285e870a | ||
|
|
daff6c7445 | ||
|
|
c95ac2c7c3 | ||
|
|
75ab8e6194 | ||
|
|
0f1597dccf | ||
|
|
422467dbe0 | ||
|
|
87d19f97a6 | ||
|
|
5febfc84f5 | ||
|
|
9406ffbfc9 | ||
|
|
fb449eae0d | ||
|
|
e41ee0c858 |
@@ -23,8 +23,8 @@ Display all sessions with metadata, filtering, and pagination.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const result = sm.getAllSessions({ limit: 20 });
|
const result = sm.getAllSessions({ limit: 20 });
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
@@ -62,8 +62,8 @@ Load and display a session's content (by ID or alias).
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
|
|
||||||
// First try to resolve as alias
|
// First try to resolve as alias
|
||||||
@@ -123,8 +123,8 @@ Create a memorable alias for a session.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const sessionId = process.argv[1];
|
const sessionId = process.argv[1];
|
||||||
const aliasName = process.argv[2];
|
const aliasName = process.argv[2];
|
||||||
@@ -163,7 +163,7 @@ Delete an existing alias.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliasName = process.argv[1];
|
const aliasName = process.argv[1];
|
||||||
if (!aliasName) {
|
if (!aliasName) {
|
||||||
@@ -192,8 +192,8 @@ Show detailed information about a session.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
const resolved = aa.resolveAlias(id);
|
const resolved = aa.resolveAlias(id);
|
||||||
@@ -239,7 +239,7 @@ Show all session aliases.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
console.log('Session Aliases (' + aliases.length + '):');
|
console.log('Session Aliases (' + aliases.length + '):');
|
||||||
|
|||||||
13
.github/workflows/security-scan.yml
vendored
13
.github/workflows/security-scan.yml
vendored
@@ -25,11 +25,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Run AgentShield Security Scan
|
||||||
uses: actions/setup-node@v4
|
uses: affaan-m/agentshield@v1
|
||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
path: '.'
|
||||||
|
min-severity: 'medium'
|
||||||
- name: Run AgentShield security scan
|
format: 'terminal'
|
||||||
run: npx ecc-agentshield scan --path . --min-severity medium --format terminal
|
fail-on-findings: 'false'
|
||||||
continue-on-error: true # Informational only — ECC contains intentional config examples
|
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -117,19 +117,22 @@ Get up and running in under 2 minutes:
|
|||||||
|
|
||||||
> ⚠️ **Important:** Claude Code plugins cannot distribute `rules` automatically. Install them manually:
|
> ⚠️ **Important:** Claude Code plugins cannot distribute `rules` automatically. Install them manually:
|
||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repo first
|
# Clone the repo first
|
||||||
git clone https://github.com/affaan-m/everything-claude-code.git
|
git clone https://github.com/affaan-m/everything-claude-code.git
|
||||||
|
cd everything-claude-code
|
||||||
|
|
||||||
# Install common rules (required)
|
# Recommended: use the installer (handles common + language rules safely)
|
||||||
cp -r everything-claude-code/rules/common/* ~/.claude/rules/
|
./install.sh typescript # or python or golang
|
||||||
|
# You can pass multiple languages:
|
||||||
# Install language-specific rules (pick your stack)
|
# ./install.sh typescript python golang
|
||||||
cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/
|
# or target cursor:
|
||||||
cp -r everything-claude-code/rules/python/* ~/.claude/rules/
|
# ./install.sh --target cursor typescript
|
||||||
cp -r everything-claude-code/rules/golang/* ~/.claude/rules/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For manual install instructions see the README in the `rules/` folder.
|
||||||
|
|
||||||
### Step 3: Start Using
|
### Step 3: Start Using
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ node tests/hooks/hooks.test.js
|
|||||||
- **详细指南(高级):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **详细指南(高级):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
- **关注:** [@affaanmustafa](https://x.com/affaanmustafa)
|
- **关注:** [@affaanmustafa](https://x.com/affaanmustafa)
|
||||||
- **zenith.chat:** [zenith.chat](https://zenith.chat)
|
- **zenith.chat:** [zenith.chat](https://zenith.chat)
|
||||||
|
- **技能目录:** [awesome-agent-skills](https://github.com/JackyST0/awesome-agent-skills)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: build-error-resolver
|
name: build-error-resolver
|
||||||
description: Build and TypeScript error resolution specialist. Use PROACTIVELY when build fails or type errors occur. Fixes build/type errors only with minimal diffs, no architectural edits. Focuses on getting the build green quickly.
|
description: Build and TypeScript error resolution specialist. Use PROACTIVELY when build fails or type errors occur. Fixes build/type errors only with minimal diffs, no architectural edits. Focuses on getting the build green quickly.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# Build Error Resolver
|
# Build Error Resolver
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: code-reviewer
|
name: code-reviewer
|
||||||
description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code. MUST BE USED for all code changes.
|
description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code. MUST BE USED for all code changes.
|
||||||
tools: ["Read", "Grep", "Glob", "Bash"]
|
tools: ["Read", "Grep", "Glob", "Bash"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a senior code reviewer ensuring high standards of code quality and security.
|
You are a senior code reviewer ensuring high standards of code quality and security.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: database-reviewer
|
name: database-reviewer
|
||||||
description: PostgreSQL database specialist for query optimization, schema design, security, and performance. Use PROACTIVELY when writing SQL, creating migrations, designing schemas, or troubleshooting database performance. Incorporates Supabase best practices.
|
description: PostgreSQL database specialist for query optimization, schema design, security, and performance. Use PROACTIVELY when writing SQL, creating migrations, designing schemas, or troubleshooting database performance. Incorporates Supabase best practices.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# Database Reviewer
|
# Database Reviewer
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: doc-updater
|
name: doc-updater
|
||||||
description: Documentation and codemap specialist. Use PROACTIVELY for updating codemaps and documentation. Runs /update-codemaps and /update-docs, generates docs/CODEMAPS/*, updates READMEs and guides.
|
description: Documentation and codemap specialist. Use PROACTIVELY for updating codemaps and documentation. Runs /update-codemaps and /update-docs, generates docs/CODEMAPS/*, updates READMEs and guides.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: haiku
|
||||||
---
|
---
|
||||||
|
|
||||||
# Documentation & Codemap Specialist
|
# Documentation & Codemap Specialist
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: e2e-runner
|
name: e2e-runner
|
||||||
description: End-to-end testing specialist using Vercel Agent Browser (preferred) with Playwright fallback. Use PROACTIVELY for generating, maintaining, and running E2E tests. Manages test journeys, quarantines flaky tests, uploads artifacts (screenshots, videos, traces), and ensures critical user flows work.
|
description: End-to-end testing specialist using Vercel Agent Browser (preferred) with Playwright fallback. Use PROACTIVELY for generating, maintaining, and running E2E tests. Manages test journeys, quarantines flaky tests, uploads artifacts (screenshots, videos, traces), and ensures critical user flows work.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# E2E Test Runner
|
# E2E Test Runner
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: go-build-resolver
|
name: go-build-resolver
|
||||||
description: Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal changes. Use when Go builds fail.
|
description: Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal changes. Use when Go builds fail.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# Go Build Error Resolver
|
# Go Build Error Resolver
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: go-reviewer
|
name: go-reviewer
|
||||||
description: Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and performance. Use for all Go code changes. MUST BE USED for Go projects.
|
description: Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and performance. Use for all Go code changes. MUST BE USED for Go projects.
|
||||||
tools: ["Read", "Grep", "Glob", "Bash"]
|
tools: ["Read", "Grep", "Glob", "Bash"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a senior Go code reviewer ensuring high standards of idiomatic Go and best practices.
|
You are a senior Go code reviewer ensuring high standards of idiomatic Go and best practices.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: python-reviewer
|
name: python-reviewer
|
||||||
description: Expert Python code reviewer specializing in PEP 8 compliance, Pythonic idioms, type hints, security, and performance. Use for all Python code changes. MUST BE USED for Python projects.
|
description: Expert Python code reviewer specializing in PEP 8 compliance, Pythonic idioms, type hints, security, and performance. Use for all Python code changes. MUST BE USED for Python projects.
|
||||||
tools: ["Read", "Grep", "Glob", "Bash"]
|
tools: ["Read", "Grep", "Glob", "Bash"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a senior Python code reviewer ensuring high standards of Pythonic code and best practices.
|
You are a senior Python code reviewer ensuring high standards of Pythonic code and best practices.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: refactor-cleaner
|
name: refactor-cleaner
|
||||||
description: Dead code cleanup and consolidation specialist. Use PROACTIVELY for removing unused code, duplicates, and refactoring. Runs analysis tools (knip, depcheck, ts-prune) to identify dead code and safely removes it.
|
description: Dead code cleanup and consolidation specialist. Use PROACTIVELY for removing unused code, duplicates, and refactoring. Runs analysis tools (knip, depcheck, ts-prune) to identify dead code and safely removes it.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# Refactor & Dead Code Cleaner
|
# Refactor & Dead Code Cleaner
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: security-reviewer
|
name: security-reviewer
|
||||||
description: Security vulnerability detection and remediation specialist. Use PROACTIVELY after writing code that handles user input, authentication, API endpoints, or sensitive data. Flags secrets, SSRF, injection, unsafe crypto, and OWASP Top 10 vulnerabilities.
|
description: Security vulnerability detection and remediation specialist. Use PROACTIVELY after writing code that handles user input, authentication, API endpoints, or sensitive data. Flags secrets, SSRF, injection, unsafe crypto, and OWASP Top 10 vulnerabilities.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
# Security Reviewer
|
# Security Reviewer
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: tdd-guide
|
name: tdd-guide
|
||||||
description: Test-Driven Development specialist enforcing write-tests-first methodology. Use PROACTIVELY when writing new features, fixing bugs, or refactoring code. Ensures 80%+ test coverage.
|
description: Test-Driven Development specialist enforcing write-tests-first methodology. Use PROACTIVELY when writing new features, fixing bugs, or refactoring code. Ensures 80%+ test coverage.
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep"]
|
||||||
model: opus
|
model: sonnet
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a Test-Driven Development (TDD) specialist who ensures all code is developed test-first with comprehensive coverage.
|
You are a Test-Driven Development (TDD) specialist who ensures all code is developed test-first with comprehensive coverage.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ REPEAT → Next test case
|
|||||||
|
|
||||||
## Example Session
|
## Example Session
|
||||||
|
|
||||||
```text
|
````
|
||||||
User: /go-test I need a function to validate email addresses
|
User: /go-test I need a function to validate email addresses
|
||||||
|
|
||||||
Agent:
|
Agent:
|
||||||
@@ -167,7 +167,7 @@ ok project/validator 0.003s
|
|||||||
✓ Coverage: 100%
|
✓ Coverage: 100%
|
||||||
|
|
||||||
## TDD Complete!
|
## TDD Complete!
|
||||||
```
|
````
|
||||||
|
|
||||||
## Test Patterns
|
## Test Patterns
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ Display all sessions with metadata, filtering, and pagination.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const result = sm.getAllSessions({ limit: 20 });
|
const result = sm.getAllSessions({ limit: 20 });
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
@@ -62,8 +62,8 @@ Load and display a session's content (by ID or alias).
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
|
|
||||||
// First try to resolve as alias
|
// First try to resolve as alias
|
||||||
@@ -123,8 +123,8 @@ Create a memorable alias for a session.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const sessionId = process.argv[1];
|
const sessionId = process.argv[1];
|
||||||
const aliasName = process.argv[2];
|
const aliasName = process.argv[2];
|
||||||
@@ -163,7 +163,7 @@ Delete an existing alias.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliasName = process.argv[1];
|
const aliasName = process.argv[1];
|
||||||
if (!aliasName) {
|
if (!aliasName) {
|
||||||
@@ -192,8 +192,8 @@ Show detailed information about a session.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
const resolved = aa.resolveAlias(id);
|
const resolved = aa.resolveAlias(id);
|
||||||
@@ -239,7 +239,7 @@ Show all session aliases.
|
|||||||
**Script:**
|
**Script:**
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
console.log('Session Aliases (' + aliases.length + '):');
|
console.log('Session Aliases (' + aliases.length + '):');
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const result = sm.getAllSessions({ limit: 20 });
|
const result = sm.getAllSessions({ limit: 20 });
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
@@ -64,8 +64,8 @@ for (const s of result.sessions) {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
|
|
||||||
// First try to resolve as alias
|
// First try to resolve as alias
|
||||||
@@ -126,8 +126,8 @@ if (session.metadata.lastUpdated) {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const sessionId = process.argv[1];
|
const sessionId = process.argv[1];
|
||||||
const aliasName = process.argv[2];
|
const aliasName = process.argv[2];
|
||||||
@@ -167,7 +167,7 @@ if (result.success) {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliasName = process.argv[1];
|
const aliasName = process.argv[1];
|
||||||
if (!aliasName) {
|
if (!aliasName) {
|
||||||
@@ -197,8 +197,8 @@ if (result.success) {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const sm = require('./scripts/lib/session-manager');
|
const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const id = process.argv[1];
|
const id = process.argv[1];
|
||||||
const resolved = aa.resolveAlias(id);
|
const resolved = aa.resolveAlias(id);
|
||||||
@@ -245,7 +245,7 @@ if (aliases.length > 0) {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
node -e "
|
node -e "
|
||||||
const aa = require('./scripts/lib/session-aliases');
|
const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
|
||||||
|
|
||||||
const aliases = aa.listAliases();
|
const aliases = aa.listAliases();
|
||||||
console.log('Session Aliases (' + aliases.length + '):');
|
console.log('Session Aliases (' + aliases.length + '):');
|
||||||
|
|||||||
@@ -464,6 +464,7 @@ node tests/hooks/hooks.test.js
|
|||||||
- **完整指南(進階):** [Everything Claude Code 完整指南](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **完整指南(進階):** [Everything Claude Code 完整指南](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
- **追蹤:** [@affaanmustafa](https://x.com/affaanmustafa)
|
- **追蹤:** [@affaanmustafa](https://x.com/affaanmustafa)
|
||||||
- **zenith.chat:** [zenith.chat](https://zenith.chat)
|
- **zenith.chat:** [zenith.chat](https://zenith.chat)
|
||||||
|
- **技能目錄:** [awesome-agent-skills](https://github.com/JackyST0/awesome-agent-skills)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -3,47 +3,47 @@
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"PreToolUse": [
|
"PreToolUse": [
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Bash\" && tool_input.command matches \"(npm run dev|pnpm( run)? dev|yarn dev|bun run dev)\"",
|
"matcher": "Bash",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"console.error('[Hook] BLOCKED: Dev server must run in tmux for log access');console.error('[Hook] Use: tmux new-session -d -s dev \\\"npm run dev\\\"');console.error('[Hook] Then: tmux attach -t dev');process.exit(1)\""
|
"command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const cmd=i.tool_input?.command||'';if(/(npm run dev|pnpm( run)? dev|yarn dev|bun run dev)/.test(cmd)){console.error('[Hook] BLOCKED: Dev server must run in tmux for log access');console.error('[Hook] Use: tmux new-session -d -s dev \\\"npm run dev\\\"');console.error('[Hook] Then: tmux attach -t dev');process.exit(2)}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Block dev servers outside tmux - ensures you can access logs"
|
"description": "Block dev servers outside tmux - ensures you can access logs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Bash\" && tool_input.command matches \"(npm (install|test)|pnpm (install|test)|yarn (install|test)?|bun (install|test)|cargo build|make|docker|pytest|vitest|playwright)\"",
|
"matcher": "Bash",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"if(!process.env.TMUX){console.error('[Hook] Consider running in tmux for session persistence');console.error('[Hook] tmux new -s dev | tmux attach -t dev')}\""
|
"command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const cmd=i.tool_input?.command||'';if(!process.env.TMUX&&/(npm (install|test)|pnpm (install|test)|yarn (install|test)?|bun (install|test)|cargo build|make\\b|docker\\b|pytest|vitest|playwright)/.test(cmd)){console.error('[Hook] Consider running in tmux for session persistence');console.error('[Hook] tmux new -s dev | tmux attach -t dev')}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Reminder to use tmux for long-running commands"
|
"description": "Reminder to use tmux for long-running commands"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Bash\" && tool_input.command matches \"git push\"",
|
"matcher": "Bash",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"console.error('[Hook] Review changes before push...');console.error('[Hook] Continuing with push (remove this hook to add interactive review)')\""
|
"command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const cmd=i.tool_input?.command||'';if(/git push/.test(cmd)){console.error('[Hook] Review changes before push...');console.error('[Hook] Continuing with push (remove this hook to add interactive review)')}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Reminder before git push to review changes"
|
"description": "Reminder before git push to review changes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Write\" && tool_input.file_path matches \"\\\\.(md|txt)$\" && !(tool_input.file_path matches \"README\\\\.md|CLAUDE\\\\.md|AGENTS\\\\.md|CONTRIBUTING\\\\.md\")",
|
"matcher": "Write",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path||'';if(/\\.(md|txt)$/.test(p)&&!/(README|CLAUDE|AGENTS|CONTRIBUTING)\\.md$/.test(p)){console.error('[Hook] BLOCKED: Unnecessary documentation file creation');console.error('[Hook] File: '+p);console.error('[Hook] Use README.md for documentation instead');process.exit(1)}console.log(d)})\""
|
"command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path||'';if(/\\.(md|txt)$/.test(p)&&!/(README|CLAUDE|AGENTS|CONTRIBUTING)\\.md$/.test(p)){console.error('[Hook] BLOCKED: Unnecessary documentation file creation');console.error('[Hook] File: '+p);console.error('[Hook] Use README.md for documentation instead');process.exit(2)}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Block creation of random .md files - keeps docs consolidated"
|
"description": "Block creation of random .md files - keeps docs consolidated"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Edit\" || tool == \"Write\"",
|
"matcher": "Edit|Write",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
],
|
],
|
||||||
"PostToolUse": [
|
"PostToolUse": [
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Bash\"",
|
"matcher": "Bash",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
@@ -89,11 +89,11 @@
|
|||||||
"description": "Log PR URL and provide review command after PR creation"
|
"description": "Log PR URL and provide review command after PR creation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Bash\" && tool_input.command matches \"(npm run build|pnpm build|yarn build)\"",
|
"matcher": "Bash",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{console.error('[Hook] Build completed - async analysis running in background');console.log(d)})\"",
|
"command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const cmd=i.tool_input?.command||'';if(/(npm run build|pnpm build|yarn build)/.test(cmd)){console.error('[Hook] Build completed - async analysis running in background')}console.log(d)})\"",
|
||||||
"async": true,
|
"async": true,
|
||||||
"timeout": 30
|
"timeout": 30
|
||||||
}
|
}
|
||||||
@@ -101,31 +101,31 @@
|
|||||||
"description": "Example: async hook for build analysis (runs in background without blocking)"
|
"description": "Example: async hook for build analysis (runs in background without blocking)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx|js|jsx)$\"",
|
"matcher": "Edit",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"const{execFileSync}=require('child_process');const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&fs.existsSync(p)){try{execFileSync('npx',['prettier','--write',p],{stdio:['pipe','pipe','pipe']})}catch(e){}}console.log(d)})\""
|
"command": "node -e \"const{execFileSync}=require('child_process');const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&/\\.(ts|tsx|js|jsx)$/.test(p)&&fs.existsSync(p)){try{execFileSync('npx',['prettier','--write',p],{stdio:['pipe','pipe','pipe']})}catch(e){}}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Auto-format JS/TS files with Prettier after edits"
|
"description": "Auto-format JS/TS files with Prettier after edits"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx)$\"",
|
"matcher": "Edit",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"const{execSync}=require('child_process');const fs=require('fs');const path=require('path');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&fs.existsSync(p)){let dir=path.dirname(p);while(dir!==path.dirname(dir)&&!fs.existsSync(path.join(dir,'tsconfig.json'))){dir=path.dirname(dir)}if(fs.existsSync(path.join(dir,'tsconfig.json'))){try{const r=execSync('npx tsc --noEmit --pretty false 2>&1',{cwd:dir,encoding:'utf8',stdio:['pipe','pipe','pipe']});const lines=r.split('\\n').filter(l=>l.includes(p)).slice(0,10);if(lines.length)console.error(lines.join('\\n'))}catch(e){const lines=(e.stdout||'').split('\\n').filter(l=>l.includes(p)).slice(0,10);if(lines.length)console.error(lines.join('\\n'))}}}console.log(d)})\""
|
"command": "node -e \"const{execFileSync}=require('child_process');const fs=require('fs');const path=require('path');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&/\\.(ts|tsx)$/.test(p)&&fs.existsSync(p)){let dir=path.dirname(p);while(dir!==path.dirname(dir)&&!fs.existsSync(path.join(dir,'tsconfig.json'))){dir=path.dirname(dir)}if(fs.existsSync(path.join(dir,'tsconfig.json'))){try{const r=execFileSync('npx',['tsc','--noEmit','--pretty','false'],{cwd:dir,encoding:'utf8',stdio:['pipe','pipe','pipe']});const lines=r.split('\\n').filter(l=>l.includes(p)).slice(0,10);if(lines.length)console.error(lines.join('\\n'))}catch(e){const lines=(e.stdout||'').split('\\n').filter(l=>l.includes(p)).slice(0,10);if(lines.length)console.error(lines.join('\\n'))}}}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "TypeScript check after editing .ts/.tsx files"
|
"description": "TypeScript check after editing .ts/.tsx files"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx|js|jsx)$\"",
|
"matcher": "Edit",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&fs.existsSync(p)){const c=fs.readFileSync(p,'utf8');const lines=c.split('\\n');const matches=[];lines.forEach((l,idx)=>{if(/console\\.log/.test(l))matches.push((idx+1)+': '+l.trim())});if(matches.length){console.error('[Hook] WARNING: console.log found in '+p);matches.slice(0,5).forEach(m=>console.error(m));console.error('[Hook] Remove console.log before committing')}}console.log(d)})\""
|
"command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&/\\.(ts|tsx|js|jsx)$/.test(p)&&fs.existsSync(p)){const c=fs.readFileSync(p,'utf8');const lines=c.split('\\n');const matches=[];lines.forEach((l,idx)=>{if(/console\\.log/.test(l))matches.push((idx+1)+': '+l.trim())});if(matches.length){console.error('[Hook] WARNING: console.log found in '+p);matches.slice(0,5).forEach(m=>console.error(m));console.error('[Hook] Remove console.log before committing')}}console.log(d)})\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Warn about console.log statements after edits"
|
"description": "Warn about console.log statements after edits"
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "**/go.mod"
|
||||||
|
- "**/go.sum"
|
||||||
|
---
|
||||||
# Go Coding Style
|
# Go Coding Style
|
||||||
|
|
||||||
> This file extends [common/coding-style.md](../common/coding-style.md) with Go specific content.
|
> This file extends [common/coding-style.md](../common/coding-style.md) with Go specific content.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "**/go.mod"
|
||||||
|
- "**/go.sum"
|
||||||
|
---
|
||||||
# Go Hooks
|
# Go Hooks
|
||||||
|
|
||||||
> This file extends [common/hooks.md](../common/hooks.md) with Go specific content.
|
> This file extends [common/hooks.md](../common/hooks.md) with Go specific content.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "**/go.mod"
|
||||||
|
- "**/go.sum"
|
||||||
|
---
|
||||||
# Go Patterns
|
# Go Patterns
|
||||||
|
|
||||||
> This file extends [common/patterns.md](../common/patterns.md) with Go specific content.
|
> This file extends [common/patterns.md](../common/patterns.md) with Go specific content.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "**/go.mod"
|
||||||
|
- "**/go.sum"
|
||||||
|
---
|
||||||
# Go Security
|
# Go Security
|
||||||
|
|
||||||
> This file extends [common/security.md](../common/security.md) with Go specific content.
|
> This file extends [common/security.md](../common/security.md) with Go specific content.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "**/go.mod"
|
||||||
|
- "**/go.sum"
|
||||||
|
---
|
||||||
# Go Testing
|
# Go Testing
|
||||||
|
|
||||||
> This file extends [common/testing.md](../common/testing.md) with Go specific content.
|
> This file extends [common/testing.md](../common/testing.md) with Go specific content.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.py"
|
||||||
|
- "**/*.pyi"
|
||||||
|
---
|
||||||
# Python Coding Style
|
# Python Coding Style
|
||||||
|
|
||||||
> This file extends [common/coding-style.md](../common/coding-style.md) with Python specific content.
|
> This file extends [common/coding-style.md](../common/coding-style.md) with Python specific content.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.py"
|
||||||
|
- "**/*.pyi"
|
||||||
|
---
|
||||||
# Python Hooks
|
# Python Hooks
|
||||||
|
|
||||||
> This file extends [common/hooks.md](../common/hooks.md) with Python specific content.
|
> This file extends [common/hooks.md](../common/hooks.md) with Python specific content.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.py"
|
||||||
|
- "**/*.pyi"
|
||||||
|
---
|
||||||
# Python Patterns
|
# Python Patterns
|
||||||
|
|
||||||
> This file extends [common/patterns.md](../common/patterns.md) with Python specific content.
|
> This file extends [common/patterns.md](../common/patterns.md) with Python specific content.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.py"
|
||||||
|
- "**/*.pyi"
|
||||||
|
---
|
||||||
# Python Security
|
# Python Security
|
||||||
|
|
||||||
> This file extends [common/security.md](../common/security.md) with Python specific content.
|
> This file extends [common/security.md](../common/security.md) with Python specific content.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.py"
|
||||||
|
- "**/*.pyi"
|
||||||
|
---
|
||||||
# Python Testing
|
# Python Testing
|
||||||
|
|
||||||
> This file extends [common/testing.md](../common/testing.md) with Python specific content.
|
> This file extends [common/testing.md](../common/testing.md) with Python specific content.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.ts"
|
||||||
|
- "**/*.tsx"
|
||||||
|
- "**/*.js"
|
||||||
|
- "**/*.jsx"
|
||||||
|
---
|
||||||
# TypeScript/JavaScript Coding Style
|
# TypeScript/JavaScript Coding Style
|
||||||
|
|
||||||
> This file extends [common/coding-style.md](../common/coding-style.md) with TypeScript/JavaScript specific content.
|
> This file extends [common/coding-style.md](../common/coding-style.md) with TypeScript/JavaScript specific content.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.ts"
|
||||||
|
- "**/*.tsx"
|
||||||
|
- "**/*.js"
|
||||||
|
- "**/*.jsx"
|
||||||
|
---
|
||||||
# TypeScript/JavaScript Hooks
|
# TypeScript/JavaScript Hooks
|
||||||
|
|
||||||
> This file extends [common/hooks.md](../common/hooks.md) with TypeScript/JavaScript specific content.
|
> This file extends [common/hooks.md](../common/hooks.md) with TypeScript/JavaScript specific content.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.ts"
|
||||||
|
- "**/*.tsx"
|
||||||
|
- "**/*.js"
|
||||||
|
- "**/*.jsx"
|
||||||
|
---
|
||||||
# TypeScript/JavaScript Patterns
|
# TypeScript/JavaScript Patterns
|
||||||
|
|
||||||
> This file extends [common/patterns.md](../common/patterns.md) with TypeScript/JavaScript specific content.
|
> This file extends [common/patterns.md](../common/patterns.md) with TypeScript/JavaScript specific content.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.ts"
|
||||||
|
- "**/*.tsx"
|
||||||
|
- "**/*.js"
|
||||||
|
- "**/*.jsx"
|
||||||
|
---
|
||||||
# TypeScript/JavaScript Security
|
# TypeScript/JavaScript Security
|
||||||
|
|
||||||
> This file extends [common/security.md](../common/security.md) with TypeScript/JavaScript specific content.
|
> This file extends [common/security.md](../common/security.md) with TypeScript/JavaScript specific content.
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/*.ts"
|
||||||
|
- "**/*.tsx"
|
||||||
|
- "**/*.js"
|
||||||
|
- "**/*.jsx"
|
||||||
|
---
|
||||||
# TypeScript/JavaScript Testing
|
# TypeScript/JavaScript Testing
|
||||||
|
|
||||||
> This file extends [common/testing.md](../common/testing.md) with TypeScript/JavaScript specific content.
|
> This file extends [common/testing.md](../common/testing.md) with TypeScript/JavaScript specific content.
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
*
|
*
|
||||||
* Cross-platform (Windows, macOS, Linux)
|
* Cross-platform (Windows, macOS, Linux)
|
||||||
*
|
*
|
||||||
* Runs when Claude session ends. Creates/updates session log file
|
* Runs when Claude session ends. Extracts a meaningful summary from
|
||||||
* with timestamp for continuity tracking.
|
* the session transcript (via CLAUDE_TRANSCRIPT_PATH) and saves it
|
||||||
|
* to a session file for cross-session continuity.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
@@ -16,35 +17,114 @@ const {
|
|||||||
getTimeString,
|
getTimeString,
|
||||||
getSessionIdShort,
|
getSessionIdShort,
|
||||||
ensureDir,
|
ensureDir,
|
||||||
|
readFile,
|
||||||
writeFile,
|
writeFile,
|
||||||
replaceInFile,
|
replaceInFile,
|
||||||
log
|
log
|
||||||
} = require('../lib/utils');
|
} = require('../lib/utils');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a meaningful summary from the session transcript.
|
||||||
|
* Reads the JSONL transcript and pulls out key information:
|
||||||
|
* - User messages (tasks requested)
|
||||||
|
* - Tools used
|
||||||
|
* - Files modified
|
||||||
|
*/
|
||||||
|
function extractSessionSummary(transcriptPath) {
|
||||||
|
const content = readFile(transcriptPath);
|
||||||
|
if (!content) return null;
|
||||||
|
|
||||||
|
const lines = content.split('\n').filter(Boolean);
|
||||||
|
const userMessages = [];
|
||||||
|
const toolsUsed = new Set();
|
||||||
|
const filesModified = new Set();
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
try {
|
||||||
|
const entry = JSON.parse(line);
|
||||||
|
|
||||||
|
// Collect user messages (first 200 chars each)
|
||||||
|
if (entry.type === 'user' || entry.role === 'user') {
|
||||||
|
const text = typeof entry.content === 'string'
|
||||||
|
? entry.content
|
||||||
|
: Array.isArray(entry.content)
|
||||||
|
? entry.content.map(c => c.text || '').join(' ')
|
||||||
|
: '';
|
||||||
|
if (text.trim()) {
|
||||||
|
userMessages.push(text.trim().slice(0, 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect tool names and modified files
|
||||||
|
if (entry.type === 'tool_use' || entry.tool_name) {
|
||||||
|
const toolName = entry.tool_name || entry.name || '';
|
||||||
|
if (toolName) toolsUsed.add(toolName);
|
||||||
|
|
||||||
|
const filePath = entry.tool_input?.file_path || entry.input?.file_path || '';
|
||||||
|
if (filePath && (toolName === 'Edit' || toolName === 'Write')) {
|
||||||
|
filesModified.add(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Skip unparseable lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userMessages.length === 0) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
userMessages: userMessages.slice(-10), // Last 10 user messages
|
||||||
|
toolsUsed: Array.from(toolsUsed).slice(0, 20),
|
||||||
|
filesModified: Array.from(filesModified).slice(0, 30),
|
||||||
|
totalMessages: userMessages.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const sessionsDir = getSessionsDir();
|
const sessionsDir = getSessionsDir();
|
||||||
const today = getDateString();
|
const today = getDateString();
|
||||||
const shortId = getSessionIdShort();
|
const shortId = getSessionIdShort();
|
||||||
// Include session ID in filename for unique per-session tracking
|
|
||||||
const sessionFile = path.join(sessionsDir, `${today}-${shortId}-session.tmp`);
|
const sessionFile = path.join(sessionsDir, `${today}-${shortId}-session.tmp`);
|
||||||
|
|
||||||
ensureDir(sessionsDir);
|
ensureDir(sessionsDir);
|
||||||
|
|
||||||
const currentTime = getTimeString();
|
const currentTime = getTimeString();
|
||||||
|
|
||||||
// If session file exists for today, update the end time
|
// Try to extract summary from transcript
|
||||||
|
const transcriptPath = process.env.CLAUDE_TRANSCRIPT_PATH;
|
||||||
|
let summary = null;
|
||||||
|
|
||||||
|
if (transcriptPath && fs.existsSync(transcriptPath)) {
|
||||||
|
summary = extractSessionSummary(transcriptPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (fs.existsSync(sessionFile)) {
|
if (fs.existsSync(sessionFile)) {
|
||||||
const success = replaceInFile(
|
// Update existing session file
|
||||||
|
replaceInFile(
|
||||||
sessionFile,
|
sessionFile,
|
||||||
/\*\*Last Updated:\*\*.*/,
|
/\*\*Last Updated:\*\*.*/,
|
||||||
`**Last Updated:** ${currentTime}`
|
`**Last Updated:** ${currentTime}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (success) {
|
// If we have a new summary and the file still has the blank template, replace it
|
||||||
log(`[SessionEnd] Updated session file: ${sessionFile}`);
|
if (summary) {
|
||||||
|
const existing = readFile(sessionFile);
|
||||||
|
if (existing && existing.includes('[Session context goes here]')) {
|
||||||
|
const updatedContent = existing.replace(
|
||||||
|
/## Current State\n\n\[Session context goes here\]\n\n### Completed\n- \[ \]\n\n### In Progress\n- \[ \]\n\n### Notes for Next Session\n-\n\n### Context to Load\n```\n\[relevant files\]\n```/,
|
||||||
|
buildSummarySection(summary)
|
||||||
|
);
|
||||||
|
writeFile(sessionFile, updatedContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log(`[SessionEnd] Updated session file: ${sessionFile}`);
|
||||||
} else {
|
} else {
|
||||||
// Create new session file with template
|
// Create new session file
|
||||||
|
const summarySection = summary
|
||||||
|
? buildSummarySection(summary)
|
||||||
|
: `## Current State\n\n[Session context goes here]\n\n### Completed\n- [ ]\n\n### In Progress\n- [ ]\n\n### Notes for Next Session\n-\n\n### Context to Load\n\`\`\`\n[relevant files]\n\`\`\``;
|
||||||
|
|
||||||
const template = `# Session: ${today}
|
const template = `# Session: ${today}
|
||||||
**Date:** ${today}
|
**Date:** ${today}
|
||||||
**Started:** ${currentTime}
|
**Started:** ${currentTime}
|
||||||
@@ -52,23 +132,7 @@ async function main() {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Current State
|
${summarySection}
|
||||||
|
|
||||||
[Session context goes here]
|
|
||||||
|
|
||||||
### Completed
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
### In Progress
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
### Notes for Next Session
|
|
||||||
-
|
|
||||||
|
|
||||||
### Context to Load
|
|
||||||
\`\`\`
|
|
||||||
[relevant files]
|
|
||||||
\`\`\`
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
writeFile(sessionFile, template);
|
writeFile(sessionFile, template);
|
||||||
@@ -78,6 +142,35 @@ async function main() {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildSummarySection(summary) {
|
||||||
|
let section = '## Session Summary\n\n';
|
||||||
|
|
||||||
|
// Tasks (from user messages)
|
||||||
|
section += '### Tasks\n';
|
||||||
|
for (const msg of summary.userMessages) {
|
||||||
|
section += `- ${msg}\n`;
|
||||||
|
}
|
||||||
|
section += '\n';
|
||||||
|
|
||||||
|
// Files modified
|
||||||
|
if (summary.filesModified.length > 0) {
|
||||||
|
section += '### Files Modified\n';
|
||||||
|
for (const f of summary.filesModified) {
|
||||||
|
section += `- ${f}\n`;
|
||||||
|
}
|
||||||
|
section += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tools used
|
||||||
|
if (summary.toolsUsed.length > 0) {
|
||||||
|
section += `### Tools Used\n${summary.toolsUsed.join(', ')}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
section += `### Stats\n- Total user messages: ${summary.totalMessages}\n`;
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch(err => {
|
||||||
console.error('[SessionEnd] Error:', err.message);
|
console.error('[SessionEnd] Error:', err.message);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -4,16 +4,20 @@
|
|||||||
*
|
*
|
||||||
* Cross-platform (Windows, macOS, Linux)
|
* Cross-platform (Windows, macOS, Linux)
|
||||||
*
|
*
|
||||||
* Runs when a new Claude session starts. Checks for recent session
|
* Runs when a new Claude session starts. Loads the most recent session
|
||||||
* files and notifies Claude of available context to load.
|
* summary into Claude's context via stdout, and reports available
|
||||||
|
* sessions and learned skills.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const {
|
const {
|
||||||
getSessionsDir,
|
getSessionsDir,
|
||||||
getLearnedSkillsDir,
|
getLearnedSkillsDir,
|
||||||
findFiles,
|
findFiles,
|
||||||
ensureDir,
|
ensureDir,
|
||||||
log
|
readFile,
|
||||||
|
log,
|
||||||
|
output
|
||||||
} = require('../lib/utils');
|
} = require('../lib/utils');
|
||||||
const { getPackageManager, getSelectionPrompt } = require('../lib/package-manager');
|
const { getPackageManager, getSelectionPrompt } = require('../lib/package-manager');
|
||||||
const { listAliases } = require('../lib/session-aliases');
|
const { listAliases } = require('../lib/session-aliases');
|
||||||
@@ -27,13 +31,19 @@ async function main() {
|
|||||||
ensureDir(learnedDir);
|
ensureDir(learnedDir);
|
||||||
|
|
||||||
// Check for recent session files (last 7 days)
|
// Check for recent session files (last 7 days)
|
||||||
// Match both old format (YYYY-MM-DD-session.tmp) and new format (YYYY-MM-DD-shortid-session.tmp)
|
|
||||||
const recentSessions = findFiles(sessionsDir, '*-session.tmp', { maxAge: 7 });
|
const recentSessions = findFiles(sessionsDir, '*-session.tmp', { maxAge: 7 });
|
||||||
|
|
||||||
if (recentSessions.length > 0) {
|
if (recentSessions.length > 0) {
|
||||||
const latest = recentSessions[0];
|
const latest = recentSessions[0];
|
||||||
log(`[SessionStart] Found ${recentSessions.length} recent session(s)`);
|
log(`[SessionStart] Found ${recentSessions.length} recent session(s)`);
|
||||||
log(`[SessionStart] Latest: ${latest.path}`);
|
log(`[SessionStart] Latest: ${latest.path}`);
|
||||||
|
|
||||||
|
// Read and inject the latest session content into Claude's context
|
||||||
|
const content = readFile(latest.path);
|
||||||
|
if (content && !content.includes('[Session context goes here]')) {
|
||||||
|
// Only inject if the session has actual content (not the blank template)
|
||||||
|
output(`Previous session summary:\n${content}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for learned skills
|
// Check for learned skills
|
||||||
@@ -56,8 +66,8 @@ async function main() {
|
|||||||
const pm = getPackageManager();
|
const pm = getPackageManager();
|
||||||
log(`[SessionStart] Package manager: ${pm.name} (${pm.source})`);
|
log(`[SessionStart] Package manager: ${pm.name} (${pm.source})`);
|
||||||
|
|
||||||
// If package manager was detected via fallback, show selection prompt
|
// If no explicit package manager config was found, show selection prompt
|
||||||
if (pm.source === 'fallback' || pm.source === 'default') {
|
if (pm.source === 'default') {
|
||||||
log('[SessionStart] No package manager preference found.');
|
log('[SessionStart] No package manager preference found.');
|
||||||
log(getSelectionPrompt());
|
log(getSelectionPrompt());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ function detectFromPackageJson(projectDir = process.cwd()) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get available package managers (installed on system)
|
* Get available package managers (installed on system)
|
||||||
|
*
|
||||||
|
* WARNING: This spawns child processes (where.exe on Windows, which on Unix)
|
||||||
|
* for each package manager. Do NOT call this during session startup hooks —
|
||||||
|
* it can exceed Bun's spawn limit on Windows and freeze the plugin.
|
||||||
|
* Use detectFromLockFile() or detectFromPackageJson() for hot paths.
|
||||||
*/
|
*/
|
||||||
function getAvailablePackageManagers() {
|
function getAvailablePackageManagers() {
|
||||||
const available = [];
|
const available = [];
|
||||||
@@ -149,7 +154,7 @@ function getAvailablePackageManagers() {
|
|||||||
* 3. package.json packageManager field
|
* 3. package.json packageManager field
|
||||||
* 4. Lock file detection
|
* 4. Lock file detection
|
||||||
* 5. Global user preference (in ~/.claude/package-manager.json)
|
* 5. Global user preference (in ~/.claude/package-manager.json)
|
||||||
* 6. First available package manager (by priority)
|
* 6. Default to npm (no child processes spawned)
|
||||||
*
|
*
|
||||||
* @param {object} options - { projectDir, fallbackOrder }
|
* @param {object} options - { projectDir, fallbackOrder }
|
||||||
* @returns {object} - { name, config, source }
|
* @returns {object} - { name, config, source }
|
||||||
@@ -215,19 +220,13 @@ function getPackageManager(options = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Use first available package manager
|
// 6. Default to npm (always available with Node.js)
|
||||||
const available = getAvailablePackageManagers();
|
// NOTE: Previously this called getAvailablePackageManagers() which spawns
|
||||||
for (const pmName of fallbackOrder) {
|
// child processes (where.exe/which) for each PM. This caused plugin freezes
|
||||||
if (available.includes(pmName)) {
|
// on Windows (see #162) because session-start hooks run during Bun init,
|
||||||
return {
|
// and the spawned processes exceed Bun's spawn limit.
|
||||||
name: pmName,
|
// Steps 1-5 already cover all config-based and file-based detection.
|
||||||
config: PACKAGE_MANAGERS[pmName],
|
// If none matched, npm is the safe default.
|
||||||
source: 'fallback'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to npm (always available with Node.js)
|
|
||||||
return {
|
return {
|
||||||
name: 'npm',
|
name: 'npm',
|
||||||
config: PACKAGE_MANAGERS.npm,
|
config: PACKAGE_MANAGERS.npm,
|
||||||
@@ -306,22 +305,18 @@ function getExecCommand(binary, args = '', options = {}) {
|
|||||||
/**
|
/**
|
||||||
* Interactive prompt for package manager selection
|
* Interactive prompt for package manager selection
|
||||||
* Returns a message for Claude to show to user
|
* Returns a message for Claude to show to user
|
||||||
|
*
|
||||||
|
* NOTE: Does NOT spawn child processes to check availability.
|
||||||
|
* Lists all supported PMs and shows how to configure preference.
|
||||||
*/
|
*/
|
||||||
function getSelectionPrompt() {
|
function getSelectionPrompt() {
|
||||||
const available = getAvailablePackageManagers();
|
let message = '[PackageManager] No package manager preference detected.\n';
|
||||||
const current = getPackageManager();
|
message += 'Supported package managers: ' + Object.keys(PACKAGE_MANAGERS).join(', ') + '\n';
|
||||||
|
|
||||||
let message = '[PackageManager] Available package managers:\n';
|
|
||||||
|
|
||||||
for (const pmName of available) {
|
|
||||||
const indicator = pmName === current.name ? ' (current)' : '';
|
|
||||||
message += ` - ${pmName}${indicator}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
message += '\nTo set your preferred package manager:\n';
|
message += '\nTo set your preferred package manager:\n';
|
||||||
message += ' - Global: Set CLAUDE_PACKAGE_MANAGER environment variable\n';
|
message += ' - Global: Set CLAUDE_PACKAGE_MANAGER environment variable\n';
|
||||||
message += ' - Or add to ~/.claude/package-manager.json: {"packageManager": "pnpm"}\n';
|
message += ' - Or add to ~/.claude/package-manager.json: {"packageManager": "pnpm"}\n';
|
||||||
message += ' - Or add to package.json: {"packageManager": "pnpm@8"}\n';
|
message += ' - Or add to package.json: {"packageManager": "pnpm@8"}\n';
|
||||||
|
message += ' - Or add a lock file to your project (e.g., pnpm-lock.yaml)\n';
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,20 +56,20 @@ if [ -z "$INPUT_JSON" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Parse using python (more reliable than jq for complex JSON)
|
# Parse using python via stdin pipe (safe for all JSON payloads)
|
||||||
PARSED=$(python3 << EOF
|
PARSED=$(echo "$INPUT_JSON" | python3 -c '
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads('''$INPUT_JSON''')
|
data = json.load(sys.stdin)
|
||||||
|
|
||||||
# Extract fields - Claude Code hook format
|
# Extract fields - Claude Code hook format
|
||||||
hook_type = data.get('hook_type', 'unknown') # PreToolUse or PostToolUse
|
hook_type = data.get("hook_type", "unknown") # PreToolUse or PostToolUse
|
||||||
tool_name = data.get('tool_name', data.get('tool', 'unknown'))
|
tool_name = data.get("tool_name", data.get("tool", "unknown"))
|
||||||
tool_input = data.get('tool_input', data.get('input', {}))
|
tool_input = data.get("tool_input", data.get("input", {}))
|
||||||
tool_output = data.get('tool_output', data.get('output', ''))
|
tool_output = data.get("tool_output", data.get("output", ""))
|
||||||
session_id = data.get('session_id', 'unknown')
|
session_id = data.get("session_id", "unknown")
|
||||||
|
|
||||||
# Truncate large inputs/outputs
|
# Truncate large inputs/outputs
|
||||||
if isinstance(tool_input, dict):
|
if isinstance(tool_input, dict):
|
||||||
@@ -83,20 +83,19 @@ try:
|
|||||||
tool_output_str = str(tool_output)[:5000]
|
tool_output_str = str(tool_output)[:5000]
|
||||||
|
|
||||||
# Determine event type
|
# Determine event type
|
||||||
event = 'tool_start' if 'Pre' in hook_type else 'tool_complete'
|
event = "tool_start" if "Pre" in hook_type else "tool_complete"
|
||||||
|
|
||||||
print(json.dumps({
|
print(json.dumps({
|
||||||
'parsed': True,
|
"parsed": True,
|
||||||
'event': event,
|
"event": event,
|
||||||
'tool': tool_name,
|
"tool": tool_name,
|
||||||
'input': tool_input_str if event == 'tool_start' else None,
|
"input": tool_input_str if event == "tool_start" else None,
|
||||||
'output': tool_output_str if event == 'tool_complete' else None,
|
"output": tool_output_str if event == "tool_complete" else None,
|
||||||
'session': session_id
|
"session": session_id
|
||||||
}))
|
}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(json.dumps({'parsed': False, 'error': str(e)}))
|
print(json.dumps({"parsed": False, "error": str(e)}))
|
||||||
EOF
|
')
|
||||||
)
|
|
||||||
|
|
||||||
# Check if parsing succeeded
|
# Check if parsing succeeded
|
||||||
PARSED_OK=$(echo "$PARSED" | python3 -c "import json,sys; print(json.load(sys.stdin).get('parsed', False))")
|
PARSED_OK=$(echo "$PARSED" | python3 -c "import json,sys; print(json.load(sys.stdin).get('parsed', False))")
|
||||||
@@ -104,7 +103,11 @@ PARSED_OK=$(echo "$PARSED" | python3 -c "import json,sys; print(json.load(sys.st
|
|||||||
if [ "$PARSED_OK" != "True" ]; then
|
if [ "$PARSED_OK" != "True" ]; then
|
||||||
# Fallback: log raw input for debugging
|
# Fallback: log raw input for debugging
|
||||||
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
echo "{\"timestamp\":\"$timestamp\",\"event\":\"parse_error\",\"raw\":$(echo "$INPUT_JSON" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()[:1000]))')}" >> "$OBSERVATIONS_FILE"
|
echo "$INPUT_JSON" | python3 -c "
|
||||||
|
import json, sys
|
||||||
|
raw = sys.stdin.read()[:2000]
|
||||||
|
print(json.dumps({'timestamp': '$timestamp', 'event': 'parse_error', 'raw': raw}))
|
||||||
|
" >> "$OBSERVATIONS_FILE"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -121,10 +124,10 @@ fi
|
|||||||
# Build and write observation
|
# Build and write observation
|
||||||
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
python3 << EOF
|
echo "$PARSED" | python3 -c "
|
||||||
import json
|
import json, sys
|
||||||
|
|
||||||
parsed = json.loads('''$PARSED''')
|
parsed = json.load(sys.stdin)
|
||||||
observation = {
|
observation = {
|
||||||
'timestamp': '$timestamp',
|
'timestamp': '$timestamp',
|
||||||
'event': parsed['event'],
|
'event': parsed['event'],
|
||||||
@@ -139,7 +142,7 @@ if parsed['output']:
|
|||||||
|
|
||||||
with open('$OBSERVATIONS_FILE', 'a') as f:
|
with open('$OBSERVATIONS_FILE', 'a') as f:
|
||||||
f.write(json.dumps(observation) + '\n')
|
f.write(json.dumps(observation) + '\n')
|
||||||
EOF
|
"
|
||||||
|
|
||||||
# Signal observer if running
|
# Signal observer if running
|
||||||
OBSERVER_PID_FILE="${CONFIG_DIR}/.observer.pid"
|
OBSERVER_PID_FILE="${CONFIG_DIR}/.observer.pid"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: django-verification
|
name: django-verification
|
||||||
description: Verification loop for Django projects: migrations, linting, tests with coverage, security scans, and deployment readiness checks before release or PR.
|
description: "Verification loop for Django projects: migrations, linting, tests with coverage, security scans, and deployment readiness checks before release or PR."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Django Verification Loop
|
# Django Verification Loop
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: java-coding-standards
|
name: java-coding-standards
|
||||||
description: Java coding standards for Spring Boot services: naming, immutability, Optional usage, streams, exceptions, generics, and project layout.
|
description: "Java coding standards for Spring Boot services: naming, immutability, Optional usage, streams, exceptions, generics, and project layout."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Java Coding Standards
|
# Java Coding Standards
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: project-guidelines-example
|
||||||
|
description: "Example project-specific skill template based on a real production application."
|
||||||
|
---
|
||||||
|
|
||||||
# Project Guidelines Skill (Example)
|
# Project Guidelines Skill (Example)
|
||||||
|
|
||||||
This is an example of a project-specific skill. Use this as a template for your own projects.
|
This is an example of a project-specific skill. Use this as a template for your own projects.
|
||||||
|
|
||||||
Based on a real production application: [Zenith](https://zenith.chat) - AI-powered customer discovery platform.
|
Based on a real production application: [Zenith](https://zenith.chat) - AI-powered customer discovery platform.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|
||||||
Reference this skill when working on the specific project it's designed for. Project skills contain:
|
Reference this skill when working on the specific project it's designed for. Project skills contain:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: springboot-verification
|
name: springboot-verification
|
||||||
description: Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR.
|
description: "Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Spring Boot Verification Loop
|
# Spring Boot Verification Loop
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: verification-loop
|
||||||
|
description: "A comprehensive verification system for Claude Code sessions."
|
||||||
|
---
|
||||||
|
|
||||||
# Verification Loop Skill
|
# Verification Loop Skill
|
||||||
|
|
||||||
A comprehensive verification system for Claude Code sessions.
|
A comprehensive verification system for Claude Code sessions.
|
||||||
|
|||||||
Reference in New Issue
Block a user