Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

work-memory

Project work memory for AI coding agents. Captures what you and your assistants actually did — git activity, session traces, decisions, blockers — and synthesizes it into per-day work logs and shareable reports.

Built as a Claude Code plugin first. Zero runtime dependencies on the user's machine (just Node 20+, which the future Electron host bundles). Discovery strategies are agent-agnostic, so a Codex / Cursor / aider plugin can reuse the same playbook later by changing only the packaging.

Why

In agentic coding workflows, real work happens fast and context evaporates fast. Daily standup notes get skipped. Weekly reports turn into "let me grep my own chat history for an hour." This project closes that loop:

  • A synthesizer subagent mines git activity and recent coding-agent sessions for actual evidence of work units — not what you said you'd do, but what shipped.
  • A canonical log stores those units losslessly with full timestamps, work intervals, and references (commits, PRs, specs, tasks).
  • A drafter subagent projects the log into reports — daily, weekly (later), spec retro (later) — in your configured language.
  • Uploads (sending reports / entries / sessions to a server) live in the host application, not the plugin. The reference Electron host in electron-example/ ships an embedded preview server and POSTs straight to it.

The principle from spec §3.1: discovery strategies are coding-agent-agnostic; plugin packaging is coding-agent-specific. Different plugins exist because different agents host the work, not because they bring different evidence.

Quick start

# 1. Install Claude Code plugin (dev mode — works without any global install)
claude --plugin-dir /path/to/tanka-work-memory-plugin/work-memory-claude-plugin

# 2. In Claude Code, inside any git repo:
/work-memory:work-log-init
# Interactive: pick project_id, display name, target language
# (Y for daily-reports is the default)

# 3. Work normally — open sessions, write code, ship commits, make decisions.

# 4. Capture entries + draft reports manually whenever you want:
/work-memory:work-log                       # capture work-so-far in this session
/work-memory:work-report                    # today's report
/work-memory:work-report --date 2026-05-07  # any past date

# (For unattended daily/weekly synth + boot/wake catch-up + uploads, run
#  the electron-example host — its in-process scheduler covers that path.)

Reports land in ~/.work-memory/<userId>/<orgId>/projects/<projectId>/reports/ — outside any git repo by construction, so the default mode never pollutes your repo root. Raw entries, the canonical index, and the report files all live under ~/.work-memory/<userId>/<orgId>/projects/<projectId>/. Nothing is written into your cwd — no pointer file, no .work-memory directory. The cwd → project mapping lives in the project's own config.cwds[] list and is resolved on demand by work-memory whoami (reverse-scans every project's config.cwds[]).

Loop automation across all projects. Every work-memory init records its cwd in the project's config.json.cwds[]. To run a cron-like daily-report sweep over every project on the box, iterate the projects directory:

for pid in $(work-memory list-projects --ids-only); do
  cwd=$(work-memory list-projects --json | jq -r ".projects[\"$pid\"].cwds[0]")
  ( cd "$cwd" && /work-memory:work-report ) || true
done

If the same (userId, orgId, projectId) triple is init'd in two cwds (e.g. one for code, one for docs), both append to the same config.json.cwds[] and they share one project root under user home — so query and the daily-report drafter automatically see all entries, regardless of which cwd they were appended from. Multi-cwd projects produce one logical report, not two.

Concepts

Concept Where it lives Role
Project ~/.work-memory/<userId>/<orgId>/projects/<projectId>/config.json Aggregate root. Multiple physical cwds sharing one project key register their paths in config.json.cwds[] and share the same project root under user home.
Cwd registration config.cwds[] inside the project's config.json The only place cwd → project is recorded. Nothing is written into the cwd itself. work-memory whoami reverse-scans every project's config.cwds[] to resolve the current cwd's project key on demand.
Entry ~/.work-memory/<userId>/<orgId>/projects/<projectId>/log/entries/<YYYY-MM>/<ts>_<id>.md An append-only record of one work unit. Frontmatter (JSON between ---) carries kind, refs, tags, summary, work_started_at / work_ended_at; body is markdown narrative.
Index ~/.work-memory/<userId>/<orgId>/projects/<projectId>/index.json Cache: every entry's frontmatter (no narrative) sorted by effective start date. Cheap to query without loading bodies. Rebuildable from entries/.
Report ~/.work-memory/<userId>/<orgId>/projects/<projectId>/reports/<window>-<format>.md A derived projection over the canonical log. Currently only daily format is shipped.

Two cross-layer invariants: L2 single write gate (the CLI is the only thing that writes entries) and canonical is lossless (reports are derived files; modifying entries to fit a report is a layering violation).

Sending reports/entries/sessions to a server is a host concern — the plugin produces canonical artifacts on disk; the host (e.g. Electron) decides when to upload them and where. There's no submitter abstraction in the plugin itself.

CLI surface

The CLI is at work-memory-claude-plugin/bin/work-memory (a tiny shell/cmd wrapper that exec's node on bin/work-memory.js, the single-file TypeScript bundle built by tsup). Stdlib only at runtime — no third-party imports.

Command Purpose
init [--user-id <slug>] [--org-id <slug>] [--project-id <slug>] [--display-name <…>] [--report-language <code>] Provision the user-home root at ~/.work-memory/<userId>/<orgId>/projects/<projectId>/ with config.json + log/, reports/, .logs/ directories and register the current cwd in that project's config.cwds[]. Nothing is written into the cwd. Idempotent: a second init in the same cwd (already in some project's cwds[]) no-ops if the same project key, errors if a different one; a second init in a sibling cwd with the same project key joins the existing project (appends to config.json.cwds[], doesn't overwrite).
whoami Reverse-scan every project's config.cwds[] for one that contains the current cwd. On match, prints the project key <userId>/<orgId>/<projectId> and exits 0. Exits non-zero with an error on stderr if no project owns this cwd. This is how skills and the host UI resolve cwd → project — there's no pointer file.
`append-entry --file --stdin`
`query [--window …] [--kind …] [--task …] [--spec …] [--include-body] --format json md
write-report --content <…> --meta <…> [--force] Save a report markdown to ~/.work-memory/<userId>/<orgId>/projects/<projectId>/reports/<window>-<format>.md with JSON frontmatter.
list-pending-reports --type daily [--lookback-days N] Print JSON {"pending": ["YYYY-MM-DD", …]} of real-work days that lack a daily report. Used by the Electron scheduler's catch-up sweep.
list-projects [--json] [--ids-only] Enumerate every project under ~/.work-memory/*/*/projects/*/ (three-tier user/org/project walk) by reading each one's config.json. Default: human-readable <userId>/<orgId>/<projectId> (display) + indented cwds (from config.json.cwds[]). --ids-only prints just keys (loop-friendly); --json dumps the project map.
config get <dotted.key> Read a value from the project's config.json by dotted path (schedule.mode, report.language, report.auto_submit, …).
schedule get / schedule set --mode … Read or write the top-level schedule block (Electron scheduler's source of truth).

Slash commands (Claude Code)

All slash commands are invoked as /work-memory:<name>.

Command Behavior
/work-memory:work-log-init Interactive bootstrap. Suggests project_id from cwd, asks for display name + language. Provisions the user-home project root with config.json and registers this cwd in config.cwds[]. Nothing is written into the cwd itself.
/work-memory:work-log Manually trigger the synthesizer. Mines git + recent sessions, appends 0+ entries.
/work-memory:work-report [--date YYYY-MM-DD] [--language CODE] Manually trigger the daily report drafter. Defaults to today and the project's configured language.

Trigger paths

Two trigger paths produce entries / reports:

Path Surface Behavior
Manual /work-memory:work-log, /work-memory:work-report The user calls them from Claude Code. Synth context comes from the current conversation + git activity.
Scheduled electron-example/src/main/scheduler.ts One in-process cron job per project (top-level schedule), plus boot/wake catch-up. Spawns claude -p --plugin-dir … headlessly. Also owns all HTTP uploads (reports, raw entries, sessions).

Earlier Claude Code SessionStart / SessionEnd hooks were retired (2026-05-18): under the Electron host they only fired inside the app's own --plugin-dir subprocesses and double-fired alongside the scheduler. See CHANGELOG.md.

Configuration (config.json)

init writes everything under the project's user-home root — nothing is written into the cwd. The only file is config.json:

{
  "project_id": "my-project",
  "display_name": "My Project",
  "cwds": [
    "/Users/pandazki/Codes/my-project"
  ],
  "first_registered": "2026-05-09T12:00:00+00:00",
  "last_updated": "2026-05-09T12:00:00+00:00",
  "report": {
    "catchup_lookback_days": 14,
    "auto_submit": false,
    "language": "zh-CN"
  },
  "schedule": {
    "mode": "manual",
    "time": null,
    "weekday": null
  },
  "schema": {
    "custom_kinds": ["customer_meeting"],
    "custom_fields": [{ "name": "customer", "type": "string" }]
  }
}

Field meanings:

  • cwds[] is the only place cwd → project is recorded — there's no pointer file.
  • report.catchup_lookback_days controls how far back the synthesizer + drafter look on a catch-up run.
  • report.auto_submit is a host-only flag: when true, the Electron host fires its uploader automatically after a scheduler-driven draft. The plugin itself never reads this.
  • report.language is the BCP-47 code the drafter writes in (defaults to en).
  • schedule.mode is honored exclusively by Electron's in-process scheduler — manual skips the cron job; daily / weekly arm one. The cron always fires in the host's local timezone.
  • schema is optional — omit when you don't need to extend the kind enum or add custom fields beyond the system preset.

report is always present in fresh projects (the --no-daily-reports opt-out was retired on 2026-05-18). Legacy config.json files where report is null or absent are auto-upgraded to defaults the first time they're read — no manual migration needed.

Architecture (5 layers)

┌──────────────────────────────────────────────────────────────┐
│ L1 · Domain                                                  │
│   Project / Canonical Log / Entry / Index / Report           │
├──────────────────────────────────────────────────────────────┤
│ L2 · Storage IO  (mechanical, agent-agnostic, CLI)           │
│   read_project / read_canonical / append_entry /             │
│   query / write_report                                       │
├──────────────────────────────────────────────────────────────┤
│ L3 · Agent Adapter  (per coding-agent thin packaging)        │
│   discovery strategies are agent-agnostic;                   │
│   only the packaging is agent-specific (§3.1)                │
├──────────────────────────────────────────────────────────────┤
│ L4 · Projection                                              │
│   query → (optional agent drafting) → Report                 │
├──────────────────────────────────────────────────────────────┤
│ L5 · Transform & Submit                                      │
│   transform(report, target_config) → payload                 │
│   submit(payload, target_config) → receipt                   │
└──────────────────────────────────────────────────────────────┘

Full design in docs/superpowers/specs/2026-05-07-work-memory-plugin-design.md (with §11 acceptance criteria), docs/superpowers/plans/2026-05-07-work-memory-plugin.md, and the Node-migration spec at docs/superpowers/specs/2026-05-13-node-migration-design.md. Future-facing collaboration observability is captured in docs/collaboration-observability-design.md.

Repo layout

tanka-work-memory-plugin/
├── package.json                  # npm workspace root
├── tsconfig.base.json            # strict TS settings shared by all packages
├── packages/
│   ├── core/                     # @work-memory/core (L1/L2/L4/L5 business logic, ~1500 LOC TS)
│   │   ├── src/                  # models, paths, storage, reports, datetime
│   │   └── test/                 # node:test unit tests
│   └── cli/                      # @work-memory/cli (argv → core router, tsup-bundled)
│       ├── src/                  # 10 subcommands matching the Python era 1:1
│       └── test/                 # node:test integration tests (CLI in-process)
├── docs/
│   ├── superpowers/specs/        # design specs (incl. 2026-05-13 Node migration)
│   ├── superpowers/plans/        # implementation plans
│   ├── superpowers/issues/       # playground feedback log
│   ├── superpowers/acceptance/   # MVP acceptance record
│   └── refs/                     # cached Claude Code plugin/skill/hook/agent docs
├── scripts/
│   └── sync-plugin-bin.js        # postinstall step: copies cli bundle into the plugin's bin/
├── electron-example/             # Reference Electron host (TypeScript + Electron 33)
├── example-server/               # Bun + TS reference backend for the Electron uploader
└── work-memory-claude-plugin/
    ├── .claude-plugin/plugin.json
    ├── bin/
    │   ├── work-memory           # Unix wrapper (exec node on work-memory.js)
    │   ├── work-memory.cmd       # Windows wrapper
    │   └── work-memory.js        # postinstall-generated CJS bundle (gitignored)
    ├── agents/                   # work-log-synthesizer + daily-report-drafter
    └── skills/                   # 3 user-facing skills

Status & known limitations

Done (MVP, dogfooded on this repo + pneuma-skills):

  • Full CLI: init / append-entry / query / write-report / list-pending-reports / config get / schedule
  • Plugin: 3 skills + 2 subagents
  • Synthesizer: mines git + Claude Code sessions + Codex CLI sessions + Claude Cowork sessions, filtered to current project
  • Drafter: range-overlap window match, multi-day entry support, configurable target language
  • Electron host: in-process scheduler with boot/wake catch-up; direct HTTP uploads (reports, raw entries, sessions) to the embedded preview server

In progress (real-send shipped against a reference backend; team-specific contracts pending):

  • Electron uploader (electron-example/src/main/submit-uploads.ts) — POSTs reports, raw work-log entries, and raw coding-agent sessions to a preview server. Body shapes are documented in electron-example/INTEGRATION.md. The repo ships an example-server/ (Bun + TypeScript) implementing one possible backend — filesystem-only storage, token-to-username mapping in data/tokens.json. See example-server/README.md. The wire body for reports includes an optional source_entries[] field carrying the structured thinking-layer of every entry the report references; the example server persists them as a sidecar <username>.entries.json. The real wire format your team standardizes on may differ; the example serves as a reference and integration test target.

Deploying the example server (single-host or k8s):

  • example-server/Dockerfile + docker-compose.yml — cp .env.example .env, set ADMIN_TOKEN, docker compose up -d --build.
  • example-server/k8s/ — Deployment + Service + ConfigMap (tokens.json) + Secret (admin token) + Ingress templates. Edit the placeholders for your registry / hostname / ingress controller, then kubectl apply -f k8s/. Single-replica with a ReadWriteOnce PVC for data/reports/.
  • The /admin/* endpoints (token mgmt + data deletion + wipe-all) are off by default — set ADMIN_TOKEN to enable. The web UI exposes them via a low-key footer link to /#/admin. See example-server/README.md for the full deploy walkthrough.

Not yet (non-blocking for daily use, listed for honesty):

  • Cursor / aider / Copilot CLI session discovery — synthesizer prompt has a forward-looking placeholder; recipes get added as plugin packaging lands
  • Codex CLI plugin packaging — same code paths, different host; current Codex coverage is read-only (synthesizer mines Codex sessions even when invoked from Claude Code)
  • Claude Cowork sessions are ephemeral — Cowork garbage-collects its local-agent-mode-sessions/ dirs, so the synthesizer only sees sessions that still exist at synth time. A session GC'd before any synth run is lost. "Snapshot-before-cleanup" capture is not built yet
  • Weekly / spec-retro report formats — only daily ships in MVP
  • work-memory reindex — no rebuild command; index drift requires manual cleanup so far
  • Concurrent multi-user merging — index.json is JSON array, full read-modify-write per append; safe for single-user, future ndjson/sqlite migration tracked in spec §12

Development

npm install                                   # builds packages/cli + syncs bin/work-memory.js
npm run typecheck                             # tsc strict-mode across both packages
npm run test                                  # node:test, 60+ tests across core + cli
node work-memory-claude-plugin/hooks/test-hooks.js   # 3 hook smoke tests
./work-memory-claude-plugin/bin/work-memory --help    # try the CLI directly

CI runs the full matrix on macos-latest, ubuntu-latest, and windows-latest. The plugin loads in dev mode via claude --plugin-dir <path>. After plugin / skill / agent / hook file edits, run /reload-plugins inside Claude Code to pick up changes without restarting.

License

TBD.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages