@claudemini/shit-cli 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/status.js CHANGED
@@ -5,20 +5,10 @@
5
5
  * Similar to 'entire status' - displays active session info
6
6
  */
7
7
 
8
- import { existsSync, readFileSync } from 'fs';
8
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
9
9
  import { join } from 'path';
10
10
  import { execSync } from 'child_process';
11
-
12
- function findProjectRoot() {
13
- let dir = process.cwd();
14
- while (dir !== '/') {
15
- if (existsSync(join(dir, '.git'))) {
16
- return dir;
17
- }
18
- dir = join(dir, '..');
19
- }
20
- throw new Error('Not in a git repository');
21
- }
11
+ import { getProjectRoot, SESSION_ID_REGEX } from './config.js';
22
12
 
23
13
  function getCurrentSession(projectRoot) {
24
14
  const shitLogsDir = join(projectRoot, '.shit-logs');
@@ -27,9 +17,11 @@ function getCurrentSession(projectRoot) {
27
17
  }
28
18
 
29
19
  // Find the most recent session directory
30
- const { readdirSync, statSync } = await import('fs');
31
20
  const sessions = readdirSync(shitLogsDir)
32
- .filter(name => name.match(/^\d{4}-\d{2}-\d{2}-[a-f0-9-]+$/)) // session format
21
+ .filter(name => {
22
+ const fullPath = join(shitLogsDir, name);
23
+ return SESSION_ID_REGEX.test(name) && statSync(fullPath).isDirectory();
24
+ })
33
25
  .map(name => ({
34
26
  name,
35
27
  path: join(shitLogsDir, name),
@@ -90,11 +82,45 @@ function formatDuration(startTime) {
90
82
  }
91
83
  }
92
84
 
85
+ function getTouchedFileCount(state) {
86
+ const ops = state?.file_ops;
87
+ if (!ops || typeof ops !== 'object') {
88
+ return 0;
89
+ }
90
+
91
+ const touched = new Set([
92
+ ...(Array.isArray(ops.write) ? ops.write : []),
93
+ ...(Array.isArray(ops.edit) ? ops.edit : []),
94
+ ...(Array.isArray(ops.read) ? ops.read : []),
95
+ ].filter(Boolean));
96
+
97
+ return touched.size;
98
+ }
99
+
100
+ function hasShitHooks(settings) {
101
+ if (!settings?.hooks || typeof settings.hooks !== 'object') {
102
+ return false;
103
+ }
104
+
105
+ return Object.values(settings.hooks).some(value => {
106
+ if (typeof value === 'string') {
107
+ return value.includes('shit log');
108
+ }
109
+ if (!Array.isArray(value)) {
110
+ return false;
111
+ }
112
+ return value.some(entry =>
113
+ Array.isArray(entry?.hooks) &&
114
+ entry.hooks.some(hook => typeof hook?.command === 'string' && hook.command.includes('shit log'))
115
+ );
116
+ });
117
+ }
118
+
93
119
  export default async function status(args) {
94
120
  try {
95
- const projectRoot = findProjectRoot();
121
+ const projectRoot = getProjectRoot();
96
122
  const gitInfo = getGitInfo(projectRoot);
97
- const currentSession = await getCurrentSession(projectRoot);
123
+ const currentSession = getCurrentSession(projectRoot);
98
124
 
99
125
  console.log('📊 shit-cli Status\n');
100
126
 
@@ -121,7 +147,7 @@ export default async function status(args) {
121
147
  console.log(` Started: ${new Date(state.start_time).toLocaleString()}`);
122
148
  console.log(` Duration: ${formatDuration(state.start_time)}`);
123
149
  console.log(` Events: ${state.event_count || 0}`);
124
- console.log(` Files: ${Object.keys(state.files || {}).length}`);
150
+ console.log(` Files: ${getTouchedFileCount(state)}`);
125
151
 
126
152
  if (state.shadow_branch) {
127
153
  console.log(` Shadow: ${state.shadow_branch}`);
@@ -149,8 +175,7 @@ export default async function status(args) {
149
175
  if (existsSync(claudeSettings)) {
150
176
  try {
151
177
  const settings = JSON.parse(readFileSync(claudeSettings, 'utf-8'));
152
- const hasHooks = settings.hooks &&
153
- (settings.hooks.session_start || settings.hooks.session_end);
178
+ const hasHooks = hasShitHooks(settings);
154
179
 
155
180
  console.log();
156
181
  if (hasHooks) {
package/lib/summarize.js CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { existsSync, readFileSync, writeFileSync } from 'fs';
10
10
  import { join } from 'path';
11
- import { execSync } from 'child_process';
11
+ import { getProjectRoot } from './config.js';
12
12
 
13
13
  // Default configuration
14
14
  const DEFAULT_CONFIG = {
@@ -294,7 +294,7 @@ export async function summarizeSession(projectRoot, sessionId, sessionDir) {
294
294
  * CLI command for manual summarization
295
295
  */
296
296
  export default async function summarize(args) {
297
- const projectRoot = findProjectRoot();
297
+ const projectRoot = getProjectRoot();
298
298
  const sessionId = args[0];
299
299
 
300
300
  if (!sessionId) {
@@ -329,14 +329,3 @@ export default async function summarize(args) {
329
329
  process.exit(1);
330
330
  }
331
331
  }
332
-
333
- function findProjectRoot() {
334
- let dir = process.cwd();
335
- while (dir !== '/') {
336
- if (existsSync(join(dir, '.git'))) {
337
- return dir;
338
- }
339
- dir = join(dir, '..');
340
- }
341
- throw new Error('Not in a git repository');
342
- }
package/package.json CHANGED
@@ -1,20 +1,37 @@
1
1
  {
2
2
  "name": "@claudemini/shit-cli",
3
- "version": "1.4.0",
4
- "description": "Session-based Hook Intelligence Tracker - CLI tool for logging Claude Code hooks",
3
+ "version": "1.5.0",
4
+ "description": "Session-based Hook Intelligence Tracker - Zero-dependency memory system for human-AI coding sessions",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "shit": "./bin/shit.js"
8
8
  },
9
+ "files": [
10
+ "bin/",
11
+ "lib/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18.0.0"
17
+ },
9
18
  "scripts": {
10
19
  "test": "echo \"Error: no test specified\" && exit 1"
11
20
  },
12
21
  "keywords": [
13
22
  "claude-code",
23
+ "gemini-cli",
24
+ "cursor",
25
+ "ai-coding",
14
26
  "hooks",
15
- "logging",
16
- "session-tracking"
27
+ "session-tracking",
28
+ "code-review",
29
+ "checkpoint"
17
30
  ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/anthropics/shit-cli.git"
34
+ },
18
35
  "author": "",
19
36
  "license": "MIT",
20
37
  "dependencies": {},
@@ -1,81 +0,0 @@
1
- {
2
- "hooks": {
3
- "SessionStart": [
4
- {
5
- "matcher": "",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "shit log session-start"
10
- }
11
- ]
12
- }
13
- ],
14
- "SessionEnd": [
15
- {
16
- "matcher": "",
17
- "hooks": [
18
- {
19
- "type": "command",
20
- "command": "shit log session-end"
21
- }
22
- ]
23
- }
24
- ],
25
- "UserPromptSubmit": [
26
- {
27
- "matcher": "",
28
- "hooks": [
29
- {
30
- "type": "command",
31
- "command": "shit log user-prompt-submit"
32
- }
33
- ]
34
- }
35
- ],
36
- "PreToolUse": [
37
- {
38
- "matcher": "",
39
- "hooks": [
40
- {
41
- "type": "command",
42
- "command": "shit log pre-tool-use"
43
- }
44
- ]
45
- }
46
- ],
47
- "PostToolUse": [
48
- {
49
- "matcher": "",
50
- "hooks": [
51
- {
52
- "type": "command",
53
- "command": "shit log post-tool-use"
54
- }
55
- ]
56
- }
57
- ],
58
- "Notification": [
59
- {
60
- "matcher": "",
61
- "hooks": [
62
- {
63
- "type": "command",
64
- "command": "shit log notification"
65
- }
66
- ]
67
- }
68
- ],
69
- "Stop": [
70
- {
71
- "matcher": "",
72
- "hooks": [
73
- {
74
- "type": "command",
75
- "command": "shit log stop"
76
- }
77
- ]
78
- }
79
- ]
80
- }
81
- }
@@ -1,20 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(git remote add:*)",
5
- "Bash(git push:*)",
6
- "Bash(npm publish:*)",
7
- "Bash(npm whoami:*)",
8
- "WebFetch(domain:www.npmjs.com)",
9
- "Bash(npm config delete:*)",
10
- "Bash(sudo chown:*)",
11
- "Bash(npm cache clean:*)",
12
- "Bash(npm install:*)",
13
- "Bash(git add:*)",
14
- "Bash(git commit -m \"$\\(cat <<''EOF''\n更新包名和版本号\n\n- 包名改为 @cluademini/shit-cli\n- 版本号升至 1.0.3\nEOF\n\\)\")",
15
- "Bash(git commit:*)",
16
- "Bash(npm config set:*)",
17
- "WebFetch(domain:github.com)"
18
- ]
19
- }
20
- }
package/COMPARISON.md DELETED
@@ -1,92 +0,0 @@
1
- # Project Structure Comparison
2
-
3
- ## Directory Layout
4
-
5
- ```
6
- your-project/
7
- ├── .claude/ # Claude Code configuration
8
- │ └── settings.json # Hook configurations
9
- ├── .entire/ # Entire tool (session management)
10
- │ ├── metadata/
11
- │ │ └── <session-id>/
12
- │ │ ├── full.jsonl # Complete transcript
13
- │ │ ├── prompt.txt # User prompts
14
- │ │ ├── context.md # Session context
15
- │ │ └── summary.txt # Session summary
16
- │ ├── logs/
17
- │ └── settings.json
18
- └── .shit-logs/ # shit-cli (hook event logging)
19
- ├── <session-id>/
20
- │ ├── events.jsonl # Hook events only
21
- │ ├── prompts.txt # User prompts
22
- │ ├── context.md # Session context
23
- │ ├── summary.txt # Session summary
24
- │ └── metadata.json # Session metadata
25
- └── index.txt # Global index
26
- ```
27
-
28
- ## Feature Comparison
29
-
30
- | Feature | `.entire` | `.shit-logs` |
31
- |---------|-----------|--------------|
32
- | **Scope** | Complete session transcript | Hook events only |
33
- | **Location** | Project root | Project root ✓ |
34
- | **Session-based** | ✓ | ✓ |
35
- | **Transcript** | `full.jsonl` (all messages) | `events.jsonl` (hooks only) |
36
- | **Prompts** | `prompt.txt` | `prompts.txt` |
37
- | **Context** | `context.md` | `context.md` |
38
- | **Summary** | `summary.txt` | `summary.txt` |
39
- | **Checkpoints** | Git shadow branches | Not implemented |
40
- | **CLI** | `entire` commands | `shit` commands |
41
- | **Auto-init** | `entire enable` | `shit init` |
42
-
43
- ## Key Differences
44
-
45
- ### `.entire` (Entire Tool)
46
- - **Purpose**: Complete session management and checkpointing
47
- - **Data**: Full conversation transcript (user + assistant messages)
48
- - **Features**: Git integration, checkpoints, session replay
49
- - **Trigger**: Automatic (entire daemon)
50
-
51
- ### `.shit-logs` (shit-cli)
52
- - **Purpose**: Hook event logging and analysis
53
- - **Data**: Hook events only (tool calls, session events)
54
- - **Features**: Session aggregation, event filtering, cleanup
55
- - **Trigger**: Hook-based (Claude Code hooks)
56
-
57
- ## Use Cases
58
-
59
- ### Use `.entire` when you need:
60
- - Complete session history
61
- - Git-based checkpointing
62
- - Session replay and analysis
63
- - Cross-session context
64
-
65
- ### Use `.shit-logs` when you need:
66
- - Hook event debugging
67
- - Tool usage analysis
68
- - Session statistics
69
- - Lightweight logging
70
-
71
- ## Both Together
72
-
73
- Running both systems provides:
74
- - **Complete coverage**: Full transcript + hook events
75
- - **Different perspectives**: Conversation flow + tool execution
76
- - **Complementary data**: `.entire` for context, `.shit-logs` for debugging
77
-
78
- ## .gitignore
79
-
80
- Both directories are typically excluded from git:
81
-
82
- ```gitignore
83
- # Entire tool
84
- .entire/store/
85
- .entire/monitor.pid
86
- .entire/monitor.sessions.json
87
-
88
- # shit-cli logs
89
- .shit-logs/
90
- ```
91
-
92
- Note: `.entire/settings.json` and `.entire/.gitignore` are usually committed.
@@ -1,138 +0,0 @@
1
- # shit-cli Design Philosophy
2
-
3
- ## Core Vision
4
-
5
- ### 1. Human-AI Interaction Memory System
6
- Long-term memory storage for human-AI interactions, not just temporary logs. Each session is analyzed for semantic meaning — what was the intent, what changed, what's the risk.
7
-
8
- ### 2. Code Review Bot Data Support
9
- Provide reliable, structured data to support code review automation. The bot doesn't need to parse raw events — it gets pre-classified changes, risk assessments, and review hints.
10
-
11
- ## Architecture
12
-
13
- ```
14
- Human ↔ AI (Claude Code)
15
- ↓ (hooks)
16
- Event Ingestion (log.js)
17
-
18
- Semantic Extraction (extract.js)
19
-
20
- Session State (session.js) + Reports (report.js)
21
-
22
- Memory System (.shit-logs + index.json)
23
-
24
- Code Review Bot / Human Queries
25
- ```
26
-
27
- ### Three-Layer Architecture
28
-
29
- 1. **Ingestion Layer** — `log.js` reads stdin, parses events, dispatches
30
- 2. **Intelligence Layer** — `extract.js` classifies intent, changes, risk
31
- 3. **Storage Layer** — `session.js` manages state, `report.js` generates outputs
32
-
33
- ## Semantic Model
34
-
35
- ### Intent Extraction
36
- From user prompts, extract:
37
- - **Goal**: What the user is trying to accomplish
38
- - **Type**: bugfix, feature, refactor, debug, test, docs, etc.
39
- - **Scope**: Which domains are involved (auth, api, database, etc.)
40
-
41
- ### Change Extraction
42
- From tool events, extract:
43
- - **Files**: Categorized (source, test, config, doc, script, infra, deps, migration)
44
- - **Operations**: What was done to each file (read, write, edit)
45
- - **Commands**: Categorized (test, build, git, deploy, install, lint, database)
46
-
47
- ### Session Classification
48
- Combining intent + changes:
49
- - **Type**: Dominant activity category
50
- - **Risk**: Assessment based on file count, config changes, test coverage
51
- - **Review Hints**: Actionable flags for code review bots
52
-
53
- ## File Structure
54
-
55
- ```
56
- .shit-logs/
57
- ├── index.json # Cross-session index (file history, session types)
58
- └── <session-id>/
59
- ├── events.jsonl # Raw hook events (complete history)
60
- ├── state.json # Incremental processing state
61
- ├── summary.json # Bot data interface (v2 schema)
62
- ├── summary.txt # Human-readable semantic report
63
- ├── prompts.txt # User prompts with timestamps
64
- └── metadata.json # Lightweight session metadata
65
- ```
66
-
67
- ## Key Design Decisions
68
-
69
- ### 1. Rule-Based Classification (No AI Dependency)
70
- Intent and change classification use simple pattern matching rules. This ensures:
71
- - Zero latency — runs during hook processing
72
- - Zero cost — no API calls
73
- - Predictable — deterministic output
74
- - Offline — works without network
75
-
76
- ### 2. Cross-Session Index
77
- The `index.json` file enables:
78
- - File history queries ("how often was this file changed?")
79
- - Session type filtering ("show all bugfix sessions")
80
- - Risk tracking over time
81
- - Bot can query without scanning all sessions
82
-
83
- ### 3. summary.json v2 Schema
84
- Upgraded from v1 (statistics-only) to v2 (semantic):
85
- - **v1**: event counts, file lists, tool usage
86
- - **v2**: intent, type, risk, review_hints, categorized changes/commands
87
-
88
- ### 4. Incremental Processing
89
- State is maintained incrementally in `state.json`. Each event updates the state, and reports are regenerated from the latest state. This means:
90
- - Fast processing per event (~5ms)
91
- - Reports always reflect current session state
92
- - No need to re-read events.jsonl
93
-
94
- ### 5. Best-Effort Shadow Branches
95
- Git shadow branches and index updates are best-effort — failures don't affect core logging. This ensures hooks never block Claude Code.
96
-
97
- ## Bot Integration Patterns
98
-
99
- ### Direct File Read
100
- ```javascript
101
- const summary = JSON.parse(fs.readFileSync('.shit-logs/<id>/summary.json'));
102
- // Use summary.review_hints for automated review decisions
103
- ```
104
-
105
- ### Cross-Session Query
106
- ```javascript
107
- const index = JSON.parse(fs.readFileSync('.shit-logs/index.json'));
108
- // Find all sessions that touched a specific file
109
- const sessions = index.file_history['src/auth/service.ts'];
110
- // Filter by type or risk
111
- const risky = index.sessions.filter(s => s.risk === 'high');
112
- ```
113
-
114
- ### CLI Query (for humans)
115
- ```bash
116
- shit query --file=src/auth/service.ts # File history
117
- shit query --type=bugfix --recent=5 # Recent bugfixes
118
- shit query --risk=high --json # High-risk as JSON
119
- ```
120
-
121
- ## Comparison with `.entire`
122
-
123
- | Aspect | `.entire` | `.shit-logs` |
124
- |--------|-----------|--------------|
125
- | **Data Source** | Full transcript | Hook events |
126
- | **Intelligence** | Raw messages | Semantic extraction |
127
- | **Bot Interface** | Parse messages | Structured JSON (v2) |
128
- | **Cross-Session** | No | Index with file history |
129
- | **Review Hints** | No | Tests, risk, coverage |
130
- | **Purpose** | Conversation memory | Operation intelligence |
131
-
132
- ## Future Enhancements
133
-
134
- - AI-powered intent extraction (optional, when available)
135
- - Diff-level change tracking (what exactly changed in each edit)
136
- - Session linking (detect related sessions across time)
137
- - Anomaly detection (unusual patterns in session activity)
138
- - Bot API endpoint (serve session data over HTTP)
package/QUICKSTART.md DELETED
@@ -1,109 +0,0 @@
1
- # Quick Start Guide
2
-
3
- ## Installation
4
-
5
- ```bash
6
- # Clone or download shit-cli
7
- cd ~/Desktop/shit-cli
8
-
9
- # Install globally
10
- npm link
11
-
12
- # Or add to PATH
13
- echo 'export PATH="$HOME/Desktop/shit-cli/bin:$PATH"' >> ~/.zshrc
14
- source ~/.zshrc
15
- ```
16
-
17
- ## Setup (One-time)
18
-
19
- ```bash
20
- # Navigate to your project
21
- cd /path/to/your/project
22
-
23
- # Initialize hooks
24
- shit init
25
- ```
26
-
27
- This creates `.claude/settings.json` with all necessary hooks.
28
-
29
- ## Verify Installation
30
-
31
- ```bash
32
- # Check if shit is available
33
- shit help
34
-
35
- # Should output:
36
- # shit-cli - Session-based Hook Intelligence Tracker
37
- # Usage: shit <command> [options]
38
- # ...
39
- ```
40
-
41
- ## Usage
42
-
43
- Once initialized, hooks will automatically log events to `~/.shit-logs/`.
44
-
45
- ### View your sessions
46
-
47
- ```bash
48
- # List all sessions
49
- shit list
50
-
51
- # View specific session
52
- shit view <session-id>
53
- ```
54
-
55
- ### Clean up old logs
56
-
57
- ```bash
58
- # Preview what will be deleted
59
- shit clean --days=7 --dry-run
60
-
61
- # Actually delete
62
- shit clean --days=7
63
- ```
64
-
65
- ## Troubleshooting
66
-
67
- ### Command not found: shit
68
-
69
- If `npm link` doesn't work, use the full path:
70
-
71
- ```bash
72
- node ~/Desktop/shit-cli/bin/shit.js init
73
- ```
74
-
75
- Or create an alias:
76
-
77
- ```bash
78
- echo 'alias shit="node ~/Desktop/shit-cli/bin/shit.js"' >> ~/.zshrc
79
- source ~/.zshrc
80
- ```
81
-
82
- ### Hooks not working
83
-
84
- 1. Check if `.claude/settings.json` exists in your project
85
- 2. Verify hooks are registered: `cat .claude/settings.json`
86
- 3. Restart Claude Code
87
-
88
- ### Logs not appearing
89
-
90
- 1. Check log directory: `ls -la ~/.shit-logs/`
91
- 2. Verify hooks are being triggered (check Claude Code output)
92
- 3. Test manually:
93
- ```bash
94
- echo '{"session_id":"test-123","hook_event_name":"Test"}' | shit log test
95
- shit list
96
- ```
97
-
98
- ## Uninstall
99
-
100
- ```bash
101
- # Remove global link
102
- npm unlink -g shit-cli
103
-
104
- # Remove logs
105
- rm -rf ~/.shit-logs
106
-
107
- # Remove hooks from project
108
- # Edit .claude/settings.json and remove shit-related hooks
109
- ```