lwchat is a zero-dependency Node.js CLI that bridges your issue tracker (Redmine) and Google Chat: locate the thread for any issue, read the discussion, post replies with auto-resolved
@mentions, send direct messages, and search across spaces — all from one terminal command, with first-class JSON output for AI agents.
Each Redmine issue at our team gets discussed in a Google Chat thread (the first message of the thread contains the issue URL). lwchat exploits that convention to wire the two together — and adds a clean general-purpose Chat surface on top:
lwchat find 126235 # → which Chat thread(s) discuss this issue?
lwchat read 126235 # → show the full discussion, with sender names
lwchat reply 126235 "deployed @Alice" # → post back, with @mention auto-resolved
lwchat post myspace "Hi team" # → top-level message to any space
lwchat post myspace "..." --thread <name> # → reply to any thread (not just Redmine)
lwchat dm alice@example.com "ping" # → DM by email/name/id
lwchat search "folio bug" --space cicd # → client-side search across spaces
lwchat me # → who am I, which spaces am I in
lwchat doctor # → one-shot runtime self-testEvery command takes --json and emits a stable shape so AI agents can consume it without parsing pretty text. See SKILL.md for the full agent contract.
git clone <your-fork-url> ~/my-works/lwchat
cd ~/my-works/lwchat
node install.mjs installThat single command:
npm links thelwchatbinary onto your PATH- Snapshots the agent skill (
SKILL.md+recipes/) to~/.lwchat/skill/ - Symlinks the snapshot into every detected AI tool: Claude Code, Codex CLI, GitHub Copilot, Gemini Antigravity
- Grants Claude Code
Read(~/.lwchat/**)andBash(lwchat:*)permissions (no more permission prompts mid-session)
Other lifecycle commands:
| Command | What it does |
|---|---|
node install.mjs update |
Pull latest + re-link binary + refresh skill snapshot |
node install.mjs install-skill |
Skills only — snapshot + symlink, skip binary re-link |
node install.mjs update-skill |
Same as install-skill (alias for the common case) |
node install.mjs status |
What's installed where, with freshness |
node install.mjs uninstall |
Remove links + npm unlink (preserves ~/.lwchat data) |
Runtime self-test after auth: lwchat doctor — Node version, data-dir writable, tokens present + refreshable, Google Chat API reachable + identity, me.md freshness.
You need a Google Cloud OAuth client. Two paths:
-
Create a Cloud project (or reuse one): https://console.cloud.google.com/projectcreate
-
Enable the Google Chat API and People API.
-
OAuth consent screen: User type Internal (Workspace) or External (gmail), App name
lwchat. -
Credentials → Create OAuth client ID: type Desktop app.
-
Chat API → Configuration: App name
lwchat, Pub/Sub topic nameprojects/<your-project-id>/topics/lwchat(topic does not need to exist), Save. -
Then:
lwchat auth login --client-id <CLIENT_ID> --client-secret <CLIENT_SECRET>
If you already use gws, it has credentials you can reuse:
lwchat auth login --import-gwsAfter login, lwchat auto-generates ~/.lwchat/me.md (your identity + every space you're in, with member counts and last-active dates) and auto-aliases your spaces.
lwchat me # print your context (~/.lwchat/me.md)
lwchat me --refresh # re-fetch identity + spaces# discover & alias spaces (one-time)
lwchat spaces fetch
lwchat spaces add eng spaces/AAAAYourSpaceID
# Redmine ↔ Chat
lwchat find 126235
lwchat read 126235
lwchat read 126235 --json | jq '.threads[].messages[].text'
lwchat reply 126235 "deployed @Alice"
# Generic Chat
lwchat post myspace "test"
lwchat post myspace "answer" --thread spaces/.../threads/abc
lwchat search "folio bug" --space cicd --limit 5
lwchat dm alice@example.com "ping"
# Operate
lwchat doctor # health check
lwchat cache show # what's cached
lwchat cache clear # drop thread cache
lwchat backup release-prep # snapshot config + tokens + me.md + cachesFull command reference: see SKILL.md (it's also what AI agents read).
| Doc | For |
|---|---|
| SKILL.md | The agent contract — what every command does, JSON shapes, multi-space rules, safety guidance. Loaded automatically by every AI agent on this machine. |
| docs/ARCHITECTURE.md | How the system works inside — data dir layout, cache + TTL, multi-space resolution, mention engine, OAuth flow, install model. |
| docs/DECISIONS.md | Architecture decision records (ADRs) — why a fork instead of a runtime overlay, why no JS plugin surface, why these scope choices, naming. |
| docs/ROADMAP.md | What's done, what might come next, known limitations. |
| docs/DEVELOPMENT.md | How to extend lwchat — project structure, conventions, "how do I add a new command" walkthrough, safe testing patterns. |
| recipes/ | Agent patterns — gather-context, reply-patterns, post/dm/search workflows. |
| CHANGELOG.md | Version history. |
~/.lwchat/
config.json spaces, default_spaces, redmine_url_pattern,
cache_ttl_seconds, page_limit
tokens.json OAuth client_id/secret/refresh_token (mode 600)
me.md generated identity + spaces snapshot
cache/thread-index.json issue_id → { space_alias → { space, thread, indexed_at } }
cache/members.json space → { user_id → name }, refreshed every 24 h
backups/ timestamped snapshots (config + tokens + me.md + caches)
skill/ canonical SKILL.md + recipes (managed by install.mjs)
Source-of-truth lives outside the repo — uninstalling lwchat doesn't lose your data. See docs/ARCHITECTURE.md for what each file is for and why.
install.mjs Zero-dep installer (npm link + skill snapshot + AI-tool symlinks + Claude perms)
bin/lwchat.js CLI entry: arg parsing, global-flag stripping, dispatch
lib/auth.js OAuth2 loopback flow + token refresh + --import-gws
lib/chat-api.js Google Chat + People API client; mention resolution engine
lib/commands.js Every command's implementation (one big module by design)
lib/me.js me.md generation
lib/config.js ~/.lwchat data dir: config, tokens, cache, backups
lib/redmine.js Optional `lwr` (lw-redmine) enrichment
SKILL.md Agent contract — primary surface for Claude/Codex/Copilot/Antigravity
recipes/ Composable agent patterns referenced from SKILL.md
docs/ Deep human + agent reference (architecture, decisions, roadmap, dev guide)
Zero npm dependencies. Only Node.js stdlib: http, fs, path, url, child_process. The entire tool is plain ESM JavaScript — no build step, no transpiler.
v0.1.2 — see CHANGELOG.md and docs/ROADMAP.md.
