npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

marmot-logger

v1.0.13

Published

Activity monitoring tool for developer workflows - tracks file changes, terminal commands, git operations, and Claude Code hooks

Downloads

1,218

Readme

Marmot

Activity monitoring and logging tool for developer workflows. Creates tamper-evident audit trails by tracking file changes, terminal commands, git operations, process execution, and Claude Code hooks with optional cryptographic signing.

Project Structure

marmot/
├── bin/
│   └── marmot.js              # CLI entry point (Commander.js)
├── src/
│   ├── index.js               # Public API exports
│   ├── cli/                   # Command handlers
│   │   ├── init.js            # marmot init
│   │   ├── enable.js          # marmot enable <plugin>
│   │   ├── disable.js         # marmot disable <plugin>
│   │   ├── status.js          # marmot status
│   │   ├── logs.js            # marmot logs
│   │   ├── monitor.js         # marmot monitor
│   │   ├── process-monitor.js # marmot process-monitor
│   │   ├── verify.js          # marmot verify
│   │   ├── log.js             # marmot log <event> (internal, used by hooks)
│   │   └── login.js           # marmot login
│   ├── core/
│   │   ├── config.js          # Config management (load/save/paths)
│   │   ├── logger.js          # Log entry persistence
│   │   ├── signer.js          # Remote signing service client
│   │   └── gitignore.js       # .gitignore pattern parsing
│   └── plugins/
│       ├── index.js           # Plugin registry
│       ├── file-monitor.js    # File change detection via rsync + git diff
│       ├── terminal.js        # Bash command logging via PROMPT_COMMAND
│       ├── git-hooks.js       # Git hook integration
│       ├── claude-hooks.js    # Claude Code IDE integration
│       ├── process-monitor.js # Process tracking via /proc filesystem
│       ├── makefile.js        # Makefile target logging (manual setup)
│       ├── vscode.js          # VS Code extension installer
│       └── vscode-extension/  # Bundled VS Code extension
│           ├── package.json   # Extension manifest
│           └── extension.js   # Extension code
├── openapi.yaml               # Signing service API specification
└── package.json

Tech Stack

  • Runtime: Node.js >= 16.0.0
  • Dependencies: commander (CLI), chalk (terminal colors)
  • External Tools: rsync, git (for file-monitor plugin)

Development Setup

# Clone and install
git clone <repo>
cd marmot
npm install

# Link for local development
npm link

# Now 'marmot' command is available globally
marmot --help

Architecture

Configuration Storage

All marmot data is stored in /tmp/marmot/<hash>/ where <hash> is MD5 of the absolute project path. This keeps marmot data completely out of the project directory.

/tmp/marmot/<hash>/
├── .marmotrc.json        # Project config
├── logs/                 # Log files (default location)
│   └── file_events_YYYY-MM-DD.log
├── snapshot/             # File monitor snapshot (rsync copy)
├── process-snapshot.json # Process monitor state
└── terminal-hook.sh      # Generated bash hook script

Config Resolution Priority

  1. Environment variables (MARMOT_API_KEY, MARMOT_URL)
  2. .env file in project root
  3. Cached values in .marmotrc.json
  4. Defaults (https://logging.drazou.net, logs in marmot temp dir)

Core Modules

config.js - Configuration management

  • loadConfig(projectDir) / saveConfig(config, projectDir) - JSON persistence
  • getSigningUrl(projectDir) / getApiKey(projectDir) - Multi-source resolution
  • getMarmotDir(projectDir) - Returns /tmp/marmot/<hash>
  • getLogDir(config, projectDir) - Returns log directory (default: /tmp/marmot/<hash>/logs)
  • enablePlugin(config, name) / disablePlugin(config, name) - Toggle plugins

logger.js - Activity logging

  • logEvent(eventType, path, size, extra, projectDir) - Main logging API
  • readLogs(logFile, options) - Parse JSON Lines log files
  • verifyLogs(logFile, projectDir) - Verify signatures with backend
  • Logs to <marmotDir>/logs/file_events_YYYY-MM-DD.log (JSON Lines format)
  • Falls back to unsigned entries if signing service unavailable

signer.js - Remote signing client

  • getToken(projectDir) / refreshToken(projectDir) - Token management
  • sign(entry, projectDir) - Sign entry with backend service
  • verify(entry, projectDir) - Verify entry signature
  • healthCheck(projectDir) - Service connectivity check
  • 10-second request timeout, automatic token refresh on 401

gitignore.js - Exclusion patterns

  • parseGitignore(projectDir) - Parse .gitignore file
  • shouldExclude(filePath, projectDir) - Check if path matches exclusions
  • buildRsyncExcludes(projectDir) - Generate rsync --exclude args
  • Always excludes: .git, logs/

Plugin System

All plugins export enable(projectConfig) and disable(projectConfig) functions.

file-monitor.js

  • Uses rsync to create snapshot, git diff to detect changes
  • Installs cron job: * * * * * cd <project> && marmot monitor
  • Events: created, modified, deleted, monitor_initialized
  • Modified events include additions and deletions line counts

terminal.js

  • Generates bash hook using PROMPT_COMMAND
  • Adds source line to ~/.bashrc
  • De-duplicates consecutive identical commands
  • Events: terminal with command field

git-hooks.js

  • Installs hooks in .git/hooks/: post-commit, pre-push, post-checkout, post-merge
  • Can coexist with existing hooks (appends marmot commands)
  • Events: git_commit, git_push, git_checkout, git_merge

claude-hooks.js

  • Configures hooks in .claude/settings.local.json
  • Events: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit, Notification, PreCompact, SessionStart, SessionEnd
  • Logged as claude_hook_<EventType>
  • Tool events include tool_name, tool_input (full parameters), and description
  • UserPromptSubmit logs full untruncated prompt in prompt field

process-monitor.js

  • Tracks processes launched from project directory via /proc filesystem
  • Polls at configurable interval (default: 15 seconds)
  • Cron job runs every minute, performs multiple polls within each minute
  • Automatically filters out marmot's own processes
  • Events: process_started, process_ended, process_monitor_initialized
  • process_ended includes duration in seconds

makefile.js

  • Non-intrusive: only prints manual setup instructions
  • Users add logging commands to their Makefile
  • Events: make_command

vscode.js

  • Installs VS Code extension to ~/.vscode/extensions/ or ~/.vscode-server/extensions/
  • Tracks multiple Marmot-watched directories in shared config (~/.config/marmot/vscode-projects.json)
  • Extension logs file events for any registered project
  • Events: vscode_file_opened, vscode_file_closed, vscode_file_saved, vscode_file_editor-changed, vscode_file_created, vscode_file_deleted, vscode_file_renamed
  • File events include languageId, size, or oldPath where applicable

CLI Commands

| Command | Handler | Description | |---------|---------|-------------| | marmot init | init.js | Create config and log directory | | marmot enable <plugin> | enable.js | Enable plugin and run setup | | marmot disable <plugin> | disable.js | Disable plugin and cleanup | | marmot status | status.js | Show config, plugin status, signing health | | marmot logs [--today] [--last N] | logs.js | Display log entries with color coding | | marmot monitor | monitor.js | Run file monitor once (for cron) | | marmot process-monitor | process-monitor.js | Run process monitor once (for cron) | | marmot verify [--file PATH] | verify.js | Verify log signatures | | marmot log <event> [-d JSON] | log.js | Log event (used internally by hooks) | | marmot login | login.js | Set API key interactively |

Log Format

JSON Lines format - one JSON object per line:

{"timestamp":"2025-12-22T14:30:00Z","uuid":"550e8400-e29b-41d4-a716-446655440000","event":"modified","path":"/project/src/index.js","size":1234,"additions":10,"deletions":5,"signed":true}
{"timestamp":"2025-12-22T14:31:00Z","uuid":"...","event":"terminal","path":"/project","command":"npm test","size":0,"signed":true}
{"timestamp":"2025-12-22T14:32:00Z","uuid":"...","event":"git_commit","path":"abc1234: Fix bug","size":0,"signed":true}
{"timestamp":"2025-12-22T14:33:00Z","uuid":"...","event":"process_started","path":"/project","pid":12345,"cmdline":"node server.js","ppid":1234,"signed":true}
{"timestamp":"2025-12-22T14:35:00Z","uuid":"...","event":"process_ended","path":"/project","pid":12345,"cmdline":"node server.js","ppid":1234,"duration":120,"signed":true}
{"timestamp":"2025-12-22T14:36:00Z","uuid":"...","event":"claude_hook_PreToolUse","path":"/project/src/file.js","tool_name":"Edit","tool_input":{"file_path":"/project/src/file.js","old_string":"...","new_string":"..."},"signed":true}
{"timestamp":"2025-12-22T14:37:00Z","uuid":"...","event":"claude_hook_UserPromptSubmit","path":"/project","prompt":"Full user prompt text here...","signed":true}

Fields added by signing service: timestamp, uuid, signed: true

Signing Service API

See openapi.yaml for full specification. Endpoints:

| Endpoint | Method | Auth | Description | |----------|--------|------|-------------| | /token | POST | None | Exchange API key for bearer token | | /sign | POST | Bearer | Sign a log entry, returns entry with uuid/timestamp | | /verify | POST | Bearer | Verify a signed entry | | /health | GET | None | Service health check |

Programmatic API

const marmot = require('marmot-logger');

// Configuration
const config = marmot.loadConfig();
marmot.saveConfig(config);
marmot.createDefaultConfig();

// Logging
await marmot.log({
  event: 'custom_event',
  path: '/path/to/file',
  size: 1234,
  metadata: { custom: 'data' }
});

const entries = marmot.readLogs('/tmp/marmot/<hash>/logs/file_events_2025-12-22.log');
const results = await marmot.verifyLogs('/tmp/marmot/<hash>/logs/file_events_2025-12-22.log');

// Signing
const signed = await marmot.sign(entry);
const result = await marmot.verify(entry);
const health = await marmot.healthCheck();

Environment Variables

| Variable | Description | Default | |----------|-------------|---------| | MARMOT_API_KEY | API key for signing service | (required for signing) | | MARMOT_URL | Signing service URL | https://logging.drazou.net |

Default Config

{
  "logDir": null,
  "bearerToken": "broken-bearer",
  "plugins": {
    "file-monitor": { "enabled": false },
    "terminal": { "enabled": false },
    "git-hooks": {
      "enabled": false,
      "events": ["commit", "push", "checkout", "merge"]
    },
    "makefile": { "enabled": false },
    "claude-hooks": {
      "enabled": false,
      "events": ["PreToolUse", "PostToolUse", "Stop", "SubagentStop", "UserPromptSubmit", "Notification", "PreCompact", "SessionStart", "SessionEnd"]
    },
    "process-monitor": {
      "enabled": false,
      "intervalSeconds": 15
    },
    "vscode": {
      "enabled": false,
      "events": ["opened", "closed", "saved", "editor-changed", "created", "deleted", "renamed"]
    }
  }
}

Note: When logDir is null (default), logs are stored in /tmp/marmot/<hash>/logs/. Set a custom path to override.

Adding a New Plugin

  1. Create src/plugins/my-plugin.js:
const chalk = require('chalk');

async function enable(projectConfig) {
  const projectDir = process.cwd();
  // Setup logic: install hooks, cron jobs, etc.
  console.log(chalk.bold('My Plugin Setup:'));
  console.log('  Plugin enabled successfully');
}

async function disable(projectConfig) {
  // Cleanup logic: remove hooks, cron jobs, etc.
  console.log(chalk.bold('My Plugin Disabled'));
}

module.exports = { enable, disable };
  1. Register in src/plugins/index.js:
module.exports = {
  // ... existing plugins
  'my-plugin': require('./my-plugin')
};
  1. Add default config in src/core/config.js DEFAULT_CONFIG.plugins:
'my-plugin': {
  enabled: false,
  // plugin-specific options
}
  1. Add to VALID_PLUGINS in src/cli/enable.js and src/cli/disable.js.

  2. Update CLI help in bin/marmot.js (enable command description).

Adding a New CLI Command

  1. Create handler in src/cli/my-command.js:
const chalk = require('chalk');
const config = require('../core/config');

module.exports = async function myCommand(options) {
  const projectConfig = config.loadConfig();
  if (!projectConfig) {
    console.log(chalk.red('Marmot not initialized. Run: marmot init'));
    process.exit(1);
  }
  // Command logic
};
  1. Register in bin/marmot.js:
const myCommand = require('../src/cli/my-command');

program
  .command('my-command')
  .description('Description of my command')
  .option('-f, --flag', 'Option description')
  .action(myCommand);

Event Types Reference

| Event | Source | Extra Fields | |-------|--------|--------------| | created | file-monitor | - | | modified | file-monitor | additions, deletions | | deleted | file-monitor | - | | monitor_initialized | file-monitor | - | | terminal | terminal | command | | git_commit | git-hooks | - | | git_push | git-hooks | - | | git_checkout | git-hooks | - | | git_merge | git-hooks | - | | make_command | makefile | - | | process_monitor_initialized | process-monitor | processCount | | process_started | process-monitor | pid, cmdline, ppid | | process_ended | process-monitor | pid, cmdline, ppid, duration | | claude_hook_PreToolUse | claude-hooks | tool_name, tool_input, description | | claude_hook_PostToolUse | claude-hooks | tool_name, tool_input, description | | claude_hook_Stop | claude-hooks | stop_reason | | claude_hook_SubagentStop | claude-hooks | stop_reason | | claude_hook_UserPromptSubmit | claude-hooks | prompt (full untruncated) | | claude_hook_Notification | claude-hooks | message | | claude_hook_PreCompact | claude-hooks | - | | claude_hook_SessionStart | claude-hooks | session_id | | claude_hook_SessionEnd | claude-hooks | session_id | | vscode_file_opened | vscode | size, languageId | | vscode_file_closed | vscode | - | | vscode_file_saved | vscode | size | | vscode_file_editor-changed | vscode | languageId | | vscode_file_created | vscode | - | | vscode_file_deleted | vscode | - | | vscode_file_renamed | vscode | oldPath |

Data Flow

Plugin detects activity
    │
    ▼
logger.logEvent(type, path, size, extra)
    │
    ▼
signer.sign(entry) ──────► Signing Service
    │                           │
    │ (fallback if unavailable) │
    ▼                           ▼
Unsigned entry             Signed entry with uuid/timestamp
    │                           │
    └───────────┬───────────────┘
                │
                ▼
    Append to /tmp/marmot/<hash>/logs/file_events_YYYY-MM-DD.log

License

MIT