Skip to content

nonscalea/featherlog

v1.0.0-rc.1MIT

Featherlog — Team rules + my work, from git history. Featherlog MCP, 8 tools: log_decision · log_blocker · log_next · log_correction · get_handoff · get_rules · get_rule_history · get_my_work. log_* writes a one-line note, get_* reads. The server never reads your code; this plugin ships no scripts and reads no files.

featherlog-cli

Featherlog CLI — team rules + my work, from git history. Connects Claude Code to Featherlog, and does two local things on the side (today's commits as markdown, transcript backups). MIT, Node 20+, zero dependencies.

Published on npm as @nonscale/featherlog (the bare name featherlog belongs to an unrelated logging SDK).

npx @nonscale/featherlog connect            # show what it would add to Claude Code (dry-run)
npx @nonscale/featherlog connect --write    # connect: merge into ~/.claude/settings.json (with a backup)
npx @nonscale/featherlog disconnect --write # disconnect: remove only what Featherlog added
npx @nonscale/featherlog today              # today's commits as Korean markdown (since 05:00 KST), local git only
npx @nonscale/featherlog backup             # gzip agent transcripts into ~/.featherlog/archive/<repo>/

init and uninstall still work as hidden aliases of connect and disconnect.

What it does

CommandWhat happensNetwork
connect [--write]Connect Claude Code. Detects ~/.claude, ~/.cursor, ~/.codex, ~/.gemini by directory presence and prints the two Claude Code hooks it would add (below): at session start you get the handoff note, at session end one session line is recorded. --write merges them into ~/.claude/settings.json, keeps every existing hook, and writes settings.json.featherlog-backup-<timestamp> first.none (the hooks themselves post later, see below)
disconnect [--write]Disconnect Claude Code. Removes only hooks carrying the X-Featherlog marker (header or in the curl command). Other hooks are untouched.none
today [--since <git date>] [--repo <dir>]My work today, from local git and nothing else. Reads git log and prints ## YYYY-MM-DD — 요약, a commit table (SHA · scope · message), and file / added / deleted line counts. No LLM.none
backup [--repo <dir>] [--dry-run]Transcript backup (extra, unrelated to the server). Copies ~/.claude/projects/<this repo>/**/*.jsonl, today's ~/.codex/sessions/**/*.jsonl, and ~/.cursor/projects/<this repo>/agent-transcripts/**/*.jsonl into ~/.featherlog/archive/<repo>/<agent>/…​.gz. Idempotent: same size + mtime is skipped.none

The hook contract (verified against the Claude Code hooks reference, 2026-09-13)

EventHook typeWhat happens
SessionEndhttpPOST <INGEST_URL>/hookOne session line. Claude Code POSTs its hook input JSON as-is (session_id, cwd, reason, transcript_path string, …). The server keeps session_id, the cwd basename, reason and the time — it drops transcript_path and every key named transcript/prompt/content/diff/patch. One events row per session (upsert).
SessionStartcommandcurl … <INGEST_URL>/handoff --data-binary @-The handoff note. SessionStart does not support http hooks (only command/mcp_tool), so the CLI installs a curl one-liner. It pipes the hook input to the handoff endpoint and prints the response — {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"…"}} — which Claude Code injects as context: the last 2 decisions, open blockers, the next 3 items, and the last session on your other device. Empty note → {} → nothing injected. -f … || true keeps it silent when offline.

FEATHERLOG_INGEST_URL is the base URL (default placeholder https://featherlog.app/api/ingest; a legacy value ending in /hook is accepted). The connect key travels as Authorization: Bearer $FEATHERLOG_TOKEN and the device name as X-Featherlog-Device: $FEATHERLOG_DEVICE; both are resolved by Claude Code at hook time via allowedEnvVars (http) or by the shell (command), and neither value is ever written into settings.json. Both hooks set timeout: 10, which also raises Claude Code's default 1.5 s SessionEnd budget.

Stop and PostToolUse hooks are intentionally not installed: turns are not stored, and commits come from the GitHub connection, not from your shell.

What it never sends

  • Transcript bodies never leave your machine. backup writes to your own home directory only.
  • The SessionEnd hook that connect installs posts Claude Code's hook input: session_id, cwd, reason, and transcript_path as a string (which the server discards). Not the transcript, not your code, not diffs, not your prompts.
  • today reads git locally and prints to stdout. Nothing is uploaded.
  • The CLI itself makes no network calls. Only the hooks (executed by Claude Code, not by this CLI) do.

Keeping transcripts longer

Claude Code deletes local transcripts after cleanupPeriodDays (30 by default). featherlog backup is a gz copy for the day it is run; it is not a scheduler. If you want the originals kept longer, raise the value in ~/.claude/settings.json:

{ "cleanupPeriodDays": 90 }

Other agents (templates, unverified)

hooks/templates/ holds curl-based templates that post the generic envelope for Cursor, Codex CLI, Gemini CLI and Copilot CLI via featherlog-envelope.sh. They follow each vendor's public docs and are marked unverified against live agent; see hooks/templates/README.md for locations, events and what each one sends.

Connect agent, per agent (에이전트 연결 — 에이전트별)

One repository, four manifests. Every agent talks to the same Featherlog MCP (https://featherlog.app/api/mcp, 8 tools: log_decision · log_blocker · log_next · log_correction write a one-line note, get_handoff · get_rules · get_rule_history · get_my_work read) with your team's connect key (fl_…, Featherlog → Settings → Connect agent → Create connect key). Only the Claude Code CLI path (connect --write) is verified against official docs; every plugin/extension manifest below was written from the vendor's public docs on 2026-09-13 and is unverified against a live agent — each file says so in its own description / _unverified line.

AgentOne-linerManifest in this repo
Claude Code/plugin marketplace add nonscaleA/featherlog-cli/plugin install featherlog@featherlog (asks for the connect key via userConfig) — or MCP only: claude mcp add --transport http featherlog https://featherlog.app/api/mcp --header 'Authorization: Bearer fl_…' — or hooks only: npx @nonscale/featherlog connect --write.claude-plugin/plugin.json + .mcp.json + hooks/hooks.json
Cursor 2.5/add-plugin nonscaleA/featherlog-cli (or cursor.com/marketplace once listed), then set FEATHERLOG_TOKEN under Plugins → Configure.cursor-plugin/plugin.json + mcp.json + hooks/cursor.json
Codex CLIpaste [mcp_servers.featherlog] into ~/.codex/config.toml (+ optional experimental hooks) — see codex/README.mdcodex/README.md + hooks/templates/codex/hooks.json
Gemini CLIgemini extensions install https://github.com/nonscaleA/featherlog-cli (prompts for FEATHERLOG_TOKEN)gemini-extension.json + hooks/hooks.json
Copilot CLIcopilot plugin marketplace add nonscaleA/featherlog-clicopilot plugin install featherlog@featherlog (Copilot CLI reads .claude-plugin/ as-is).claude-plugin/plugin.json + .mcp.json
Agent Plugins 1.0 clients (VS Code, Kiro, …)point the client at this repositoryplugin.json + mcp.json (spec: agent-plugins.org, type: "streamable-http")

Connect-key substitution differs per ecosystem and is the least-verified part: Claude Code ${user_config.TOKEN} / $CLAUDE_PLUGIN_OPTION_TOKEN, Cursor ${FEATHERLOG_TOKEN} via variables, Gemini settings[].envVar, Codex bearer_token_env_var, Agent Plugins 1.0 leaves ${FEATHERLOG_TOKEN} in mcp.json headers to the client. Prefer the CLI (connect --write) or claude mcp add when a manifest misbehaves.

hooks/hooks.json is one file for Claude Code, Gemini CLI and Copilot CLI: command hooks only (Gemini supports nothing else), no timeout key (Gemini counts ms, Claude Code seconds — curl -m bounds each call instead), SessionStart/handoff, SessionEnd/hook. Inside a plugin the SessionEnd budget stays at Claude Code's default 1.5 s, hence curl -m 1.

What leaves your machine

PathSentNever sent
connect, disconnect, today, backup (this CLI)nothing — no network calls
SessionEnd hook (CLI-installed http, or plugin command)Claude Code's hook input: session_id, cwd (server keeps the basename), reason, timestamps, transcript_path as a string (server discards it)transcript body, prompts, code, diffs, file contents
SessionStart hook{hook_event_name, session_id, cwd} → receives the handoff noteanything else
Other-agent templates (featherlog-envelope.sh)source, event, session_id, device, occurred_at, project.{repo,cwd,branch} (from git in cwd)transcript body, prompts, code, diffs
Featherlog MCP (/api/mcp)only what you type into a tool call (a decision, a blocker, a next item, a correction) and the tool nameyour files — the server has no tool that reads them
HeadersAuthorization: Bearer <connect key>, X-Featherlog: <installer>/<version>, X-Featherlog-Device (optional device name)

Development

npm ci
npm test          # node --test
npm run lint      # node --check on every file
npm pack --dry-run

Set FEATHERLOG_TEST_HOME=/some/dir to point backup / connect at a fake home; tests use this so they never touch your real ~/.claude.


한국어

깃털로그 CLI — git 로그를 사람이 읽는 팀 규칙과 내가 한 일로. Claude Code 를 깃털로그에 연결하고, 곁다리로 로컬 일 두 가지(오늘 커밋 마크다운, 대화 기록 백업)를 합니다. MIT, Node 20+, 의존성 0.

  • connect — Claude Code 연결. 홈 폴더에서 에이전트를 찾고 ~/.claude/settings.json 에 넣을 내용을 보여 줍니다(기본 dry-run). --write 면 병합하고 이전 설정은 백업 파일로 남깁니다. 연결하면:
    • 세션이 끝날 때 메타 한 줄이 남습니다 — SessionEndhttp 훅으로 POST <INGEST_URL>/hook. Claude Code 가 훅 입력 JSON 을 그대로 보내고, 서버는 세션 ID·cwd 이름·종료 이유·시각만 남깁니다(transcript_path 는 서버가 버림). Claude Code 세션 1건 = 1행.
    • 시작할 때 이어하기 메모를 받습니다 — SessionStartcommand 훅(curl). SessionStart 는 http 훅을 지원하지 않아(공식 문서 2026-09-13 확인) curl 한 줄이 /handoff 를 호출하고 응답 {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":…}} 을 stdout 으로 흘려 이어하기 메모(최근 정한 것 2·아직 막힌 것·다음 할 일 3·다른 기기 마지막 세션)를 컨텍스트로 넣습니다. 기록이 없으면 {}.
    • 환경변수: 연결 키 FEATHERLOG_TOKEN(필수, settings.json 에 쓰이지 않음) · FEATHERLOG_INGEST_URL(서버 기본 URL) · 기기 이름 FEATHERLOG_DEVICE(선택, 내가 붙인 별명, 예: macB).
    • initconnect 의 숨김 별칭입니다(예전 문서·스크립트 호환).
  • disconnect — Claude Code 연결 해제. X-Featherlog 마커가 붙은 훅만 지웁니다. uninstall 은 숨김 별칭.
  • today — 오늘 내가 한 일 (로컬 git, 서버 없이). 오늘(05:00 KST 기준) 커밋을 ## YYYY-MM-DD — 요약 / 커밋 목록(SHA·scope·메시지) / 파일 수·추가·삭제 줄로 출력. LLM 없음.
  • backup — 대화 기록 압축 (부가, 서버와 무관). Claude Code·Codex·Cursor 대화 기록(.jsonl)을 ~/.featherlog/archive/<리포>/ 에 gz 로 복사. 같은 크기+수정시각이면 건너뜀.
  • hooks/templates/ — Cursor·Codex·Gemini CLI·Copilot CLI 용 curl 템플릿(범용 봉투). 라이브 에이전트 미검증.

보내지 않는 것. 대화 기록 본문은 이 기기를 떠나지 않습니다. 훅은 세션 ID·cwd 이름·종료 이유·시각만 보냅니다. 코드·diff·프롬프트·본문은 보내지 않습니다. Stop·PostToolUse 훅은 설치하지 않습니다(턴은 저장하지 않고, 커밋은 GitHub 연결이 정본).

참고. Claude Code 는 cleanupPeriodDays(기본 30일) 뒤에 로컬 대화 기록을 지웁니다. 더 오래 보관하려면 ~/.claude/settings.json 에서 값을 올리세요. backup 은 실행한 날의 복사본일 뿐 스케줄러가 아닙니다.

에이전트 연결(에이전트별). 위 "Connect agent, per agent" 표. 모든 에이전트가 같은 깃털로그 MCP(도구 8개: log_* 로 한 줄 메모를 남기고 get_* 로 읽음)에 팀의 연결 키(fl_…, 깃털로그 → 설정 → 에이전트 연결 → 연결 키 만들기)로 붙습니다. Claude Code 는 /plugin marketplace add nonscaleA/featherlog-cli/plugin install featherlog@featherlog, Cursor 는 /add-plugin nonscaleA/featherlog-cli, Gemini CLI 는 gemini extensions install https://github.com/nonscaleA/featherlog-cli, Codex 는 codex/README.md, Copilot CLI 는 .claude-plugin/ 을 그대로 읽습니다. 매니페스트 4개는 공식 문서 기준(2026-09-13)이며 라이브 에이전트 미검증입니다.

설치: npx @nonscale/featherlog connect (npm 이름 featherlog 는 다른 패키지가 쓰고 있어 @nonscale/featherlog 로 배포합니다).