Skip to content
Back to skills

Ccm Load

ASecurity

Load cc-memory for this project — verify the plugin is globally enabled, ensure the project's .ccm/ directory is initialized, and report end-to-end health. The cc-memory equivalent of "make sure I'm wired up here".

  • 7 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 2, 2026
ai-agentspythongoshellbashtestinggitapidatabase

Works with

  • claude code
  • terminal
  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 3, 2026

npx -y skills add skymanbp/cc-memory --skill ccm-load --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ccm Load?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ccm Load
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/skymanbp-ccm-load/badge)](https://www.skillsdirectory.com/skills/skymanbp-ccm-load)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ccm-load
description: Load cc-memory for this project — verify the plugin is globally enabled, ensure the project's .ccm/ directory is initialized, and report end-to-end health. The cc-memory equivalent of "make sure I'm wired up here".
---

## /ccm-load — Load cc-memory into this project

Run this once in any new project to confirm cc-memory is **active**, the
project's `.ccm/` is initialized, and PROGRESS.md/MEMORY.md are generated.
Idempotent — safe to re-run.

### What this skill does

This skill covers **activation and project bootstrap** — the things no other
entry point does. Ongoing diagnostics belong to `/cc-mem status`; the two are
deliberately disjoint (see the table at the end of this file).

1. **Verify activation, PER INSTALL LAYOUT.** The two shipped layouts are
   activated by *different* mechanisms, so one check cannot serve both:
   - **marketplace / dev checkout** — `~/.claude/settings.json`
     `enabledPlugins` holds an enabled `cc-memory@<marketplace>` key (any
     marketplace name, as `cli/mem.py:_marketplace_enabled_key` matches), root from
     `extraKnownMarketplaces.cc-memory` **or** `plugins/installed_plugins.json`
     (a `/plugin marketplace add <github-repo>` install has no local path),
     hooks declared by `<root>/hooks/hooks.json`.
   - **standalone / `.exe` installer** — never appears in `enabledPlugins` at
     all. `ui/installer.py:_merge_into_settings` writes **only** the `hooks`
     key, so activation means `settings.json["hooks"]` registers cc-memory for
     all five events; the tree it lays down is **flat**.

   Either layout activated ⇒ **proceed to bootstrap**. Nothing activated ⇒
   print the fix for the layout this machine actually has (never tell an
   `.exe` user to add a marketplace — they have no repo). The rule is mirrored
   from `cc_memory/cli/mem.py`'s `_detect_install_layouts` / `_inspect_layout`;
   `/cc-mem status` reports the same layouts but never bootstraps, so this is
   the only entry point that *gates project init* on the verdict.
2. **Resolve the installed package tree** across both layouts (nested
   marketplace/dev checkout, flat standalone install) and fail loudly with
   actionable instructions if neither resolves.
3. **Auto-initialize this project's `.ccm/`** — if `.ccm/memory.db` is
   absent, create the directory tree + DB + `.gitignore`. (This also happens
   on first UserPromptSubmit; this skill makes it explicit.)
4. **Seed PROGRESS.md** — write a current snapshot from the (possibly empty)
   `progress` row so the file exists from day one.
5. **Print quick DB counts** (memories / sessions / topics / observations).
   This is **not** the `/cc-mem status` health check — install-layout
   inspection, hook-registration verdicts, API-key resolution and last-save
   staleness all live there and are NOT run by this skill.
6. **Report status** to the user in 1-2 sentences.

### Step 1 — Run this script

```bash
python3 -c "
# NO BACKTICKS ANYWHERE BELOW, not even inside a comment. This whole body is
# a shell DOUBLE-quoted string, so a backtick opens command substitution and
# bash executes the text between them before python ever sees it. The fix for
# the best['pkg'] KeyError briefly carried two prose backticks in its comment,
# and bash dutifully ran them ('from: command not found', 'active: command not
# found'); output was empty so the run limped on, but the shape is arbitrary
# command execution. Caught in review before it was ever committed. For the
# same reason the block contains no dollar sign either, so bash has nothing
# to expand at all. Enforced by test_surfaces §7, which is why this very
# comment spells both characters out in words instead of quoting them.
import json, os, sys
from pathlib import Path

HOME = Path.home()
SETTINGS = HOME / '.claude' / 'settings.json'
LEGACY = HOME / '.claude' / 'hooks' / 'cc-memory'
EVENTS = ('PreCompact', 'SessionStart', 'Stop', 'PostToolUse', 'UserPromptSubmit')
Q = chr(34)  # a literal double quote: this script is embedded in a shell dquote

def _d(x):
    return x if isinstance(x, dict) else {}

def _l(x):
    return x if isinstance(x, list) else []

# ── (1) Activation check — PER LAYOUT ──────────────────────────────────
# Two layouts ship and they are activated by DIFFERENT mechanisms. Gating
# BOTH on enabledPlugins + extraKnownMarketplaces made this skill print
# 'NOT FULLY ACTIVATED' on every standalone / .exe install — the layout the
# README recommends to Windows users — and then skip bootstrap entirely,
# while /cc-mem status on the same machine reported '5/5 registered'. Two
# shipped surfaces, opposite verdicts on one healthy install.
#   marketplace / dev checkout — an enabledPlugins['cc-memory@*'] key true;
#       root from extraKnownMarketplaces.source.path or
#       plugins/installed_plugins.json; hooks declared by
#       <root>/hooks/hooks.json; tree is NESTED (<root>/cc_memory/core/db.py)
#   standalone (ui/installer.py) — never appears in enabledPlugins at all:
#       _merge_into_settings writes ONLY settings.json['hooks'], and
#       _copy_subpackages writes TARGET_DIR/<subdir>/, a FLAT tree with no
#       cc_memory/ segment (<root>/core/db.py)
# Mirrors cc_memory/cli/mem.py:_detect_install_layouts / _inspect_layout —
# see its enabled=True comment, 'legacy install does not gate on
# enabledPlugins'.
settings, settings_err = {}, None
if SETTINGS.exists():
    try:
        # utf-8-sig: a settings.json ever saved from PowerShell carries a BOM.
        settings = _d(json.loads(SETTINGS.read_text(encoding='utf-8-sig')))
    except Exception as e:
        settings_err = f'{SETTINGS} unreadable: {e}'
else:
    settings_err = f'{SETTINGS} not found'

def _pkg_dir(root):
    # The directory to put on sys.path, for EITHER on-disk shape, or None.
    if not root:
        return None
    p = Path(root)
    if (p / 'cc_memory' / 'core' / 'db.py').exists():
        return p / 'cc_memory'
    if (p / 'core' / 'db.py').exists():
        return p
    return None

def _manifest_events(root):
    hj = Path(root) / 'hooks' / 'hooks.json'
    if not hj.exists():
        return set()
    try:
        hooks = _d(json.loads(hj.read_text(encoding='utf-8'))).get('hooks', {})
        return set(_d(hooks)) & set(EVENTS)
    except Exception:
        return set()  # why: malformed hooks.json is reported below as 0/5

def _settings_events():
    got = set()
    blk = _d(settings.get('hooks'))
    for ev in EVENTS:
        for mg in _l(blk.get(ev)):
            for h in _l(_d(mg).get('hooks')):
                if 'cc-memory' in (_d(h).get('command') or ''):
                    got.add(ev)
    return got

layouts = []
# Prefix match, DELIBERATE MIRROR of cli/mem.py:_marketplace_enabled_key:
# cc-memory@<any marketplace> is the same plugin; testing only the literal
# cc-memory@cc-memory key reported a healthy install as disabled.
mp_enabled = any(isinstance(k, str) and k.split('@')[0] == 'cc-memory' and bool(v)
                 for k, v in _d(settings.get('enabledPlugins')).items())
mp_roots = []
mp_src = _d(_d(_d(settings.get('extraKnownMarketplaces')).get('cc-memory')).get('source'))
if mp_src.get('path'):
    mp_roots.append(mp_src['path'])
_inst = HOME / '.claude' / 'plugins' / 'installed_plugins.json'
if _inst.exists():
    try:
        _plugs = _d(_d(json.loads(_inst.read_text(encoding='utf-8'))).get('plugins'))
        for e in _l(_plugs.get('cc-memory@cc-memory')):
            if _d(e).get('installPath'):
                mp_roots.append(e['installPath'])
    except Exception:
        pass  # why: a corrupt plugin cache costs one root candidate, not the run
for r in dict.fromkeys(mp_roots):
    pd, evs, probs = _pkg_dir(r), _manifest_events(r), []
    if pd is None:
        probs.append(f'no cc-memory package tree under {r}')
    if not mp_enabled:
        probs.append('settings.json enabledPlugins has no enabled cc-memory@* key')
    if len(evs) < 5:
        probs.append(f'{Path(r) / \"hooks\" / \"hooks.json\"} declares {len(evs)}/5 events')
    layouts.append({'name': 'marketplace', 'root': Path(r), 'pkg': pd, 'n': len(evs),
                    'via': 'hooks/hooks.json', 'probs': probs})

if (LEGACY / 'cc_memory').exists() or (LEGACY / 'core' / 'db.py').exists():
    pd, evs, probs = _pkg_dir(LEGACY), _settings_events(), []
    if pd is None:
        probs.append(f'incomplete package tree under {LEGACY} (no core/db.py)')
    if len(evs) < 5:
        probs.append('settings.json[\"hooks\"] registers ' + str(len(evs)) + '/5 events '
                     '(missing: ' + ', '.join(e for e in EVENTS if e not in evs) + ')')
    layouts.append({'name': 'standalone', 'root': LEGACY, 'pkg': pd, 'n': len(evs),
                    'via': 'settings.json[hooks]', 'probs': probs})

for L in layouts:
    if L['pkg'] is None:
        shape = 'no package tree'
    elif L['pkg'] == L['root']:
        shape = 'flat'
    else:
        shape = 'nested'
    tag = 'OK  ' if not L['probs'] else 'WARN'
    # ASCII-only from here down: these lines print BEFORE core.encoding_setup
    # is importable, and a cp437 console cannot encode U+2014, which would
    # abort the skill with UnicodeEncodeError instead of bootstrapping.
    print(f'[{tag}] {L[\"name\"]} install at {L[\"root\"]} ({shape}) - '
          f'hooks {L[\"n\"]}/5 via {L[\"via\"]}')
    for p in L['probs']:
        print(f'         - {p}')

active = [L for L in layouts if not L['probs']]
if not active:
    print()
    print('=== cc-memory NOT FULLY ACTIVATED ===')
    if settings_err:
        print(f'  - {settings_err}')
    if not layouts:
        print('  No cc-memory install found. Checked:')
        print(f'    marketplace  {SETTINGS} extraKnownMarketplaces, and')
        print(f'                 {_inst}')
        print(f'    standalone   {LEGACY}')
    print()
    print('Fix - pick exactly ONE (both at once registers every hook twice):')
    if not layouts or any(L['name'] == 'standalone' for L in layouts):
        print('  Standalone / .exe - no repo needed. Re-run the installer:')
        print('    cc-memory-installer.exe          (add --cli for a console run)')
        print('    or: python ' + Q + str(LEGACY / 'ui' / 'installer.py') + Q + ' --cli')
    if not layouts or any(L['name'] == 'marketplace' for L in layouts):
        print('  Plugin / dev checkout - needs the repo. Inside Claude Code:')
        print('    /plugin marketplace add <path-to-cc-memory-repo>')
        print('    /plugin install cc-memory@cc-memory')
    sys.exit(0)

best = active[0]
print(f'[OK] cc-memory ACTIVATED ({best[\"name\"]} layout) - bootstrapping project')

# ── (2) Project init ───────────────────────────────────────────────────
# Anchor on the project ROOT, not the shell's cwd. Run from a subdirectory
# this used to build the scaffold there — the exact stray database the hooks
# refuse to create since v2.6.0 — and because an existing database is
# terminal, all six hooks would then be pinned to it permanently. Falls back
# to cwd if the resolver cannot be imported (flat installs predating it).
project = Path('.').resolve()
# 'pkg', not 'root': _pkg_dir returns the directory that CONTAINS core/,
# which is what the imports below need on sys.path — under the nested
# layout that is <root>/cc_memory, not <root>. And never None here: a None
# pkg always appends a probs entry (marketplace and standalone both), and
# 'active' keeps only the layouts whose probs list came out empty.
sys.path.insert(0, str(Path(best['pkg'])))
try:
    # The opt-out gate lives in its OWN try, SEPARATE from the anchoring try
    # below and BEFORE it. It used to sit after the core.roots import inside
    # one shared try, so a package tree missing core/roots.py (an older or
    # partial flat install) raised ImportError PAST the gate, the except arm
    # said 'root anchoring unavailable', and an EXCLUDED project was then
    # fully initialized — database, PROGRESS.md, MEMORY.md, .gitignore
    # (reproduced both ways: intact tree refuses, roots-less tree created
    # everything). The gate must not share a failure domain with a module it
    # does not need. Checked on the pre-anchor path so a per-subdirectory
    # exclusion is not widened. cli_opt_out_notice itself never raises; this
    # try only covers the import, and failing OPEN on an unimportable
    # core.modes is the documented convention shared with cli/mem.py and
    # ui/installer.py (the hooks and the MCP server enforce the opt-out
    # independently on every write).
    from core.modes import cli_opt_out_notice
    _refusal = cli_opt_out_notice(str(project))
    if _refusal:
        print('[cc-memory] ' + _refusal)
        sys.exit(0)
except ImportError as _gate_err:
    print(f'[init] opt-out check unavailable ({_gate_err}); continuing')
try:
    from core.roots import project_root
    anchored = Path(project_root(str(project))).resolve()
    if anchored != project:
        print(f'[init] {project} is inside a project rooted at {anchored} - using that root')
        project = anchored
except Exception as _anchor_err:
    print(f'[init] root anchoring unavailable ({_anchor_err}); using {project}')
mem_dir = project / '.ccm'
try:
    # The migrating resolver, not a join: a project last touched before
    # v2.13.0 still has its state under 'memory', and this skill is often the
    # first thing run in it. Imported the same way project_root just was, and
    # degraded the same way - the literal above is what a broken import falls
    # back to, and it is held to core.layout.MEMORY_DIRNAME by the parity gate.
    from core.layout import memory_dir as _resolve_state_dir
    mem_dir = _resolve_state_dir(project)
except Exception as _layout_err:
    print(f'[init] state-directory resolver unavailable ({_layout_err}); using {mem_dir}')
db_path = mem_dir / 'memory.db'

if not db_path.exists():
    print(f'[init] Creating {mem_dir.name}/ at {mem_dir}')
    mem_dir.mkdir(exist_ok=True)
    (mem_dir / 'sessions').mkdir(exist_ok=True)
    (mem_dir / 'topics').mkdir(exist_ok=True)
    # Literal copy (inline script, no package import). Must mirror BOTH halves
    # of core.progress.ensure_memory_gitignore: the LINE LIST
    # (core.progress.MEMORY_GITIGNORE_LINES) and the APPEND SEMANTICS. Only the
    # list was ever mirrored: this copy did open(gi, 'a') + join(missing), so
    # against an existing .gitignore whose last line had no trailing newline it
    # emitted 'sessions/# cc-memory: generated state, not content' - fusing the
    # user's last rule with our first comment and destroying that rule. The
    # read / normalise / write shape below matches
    # core.progress.ensure_memory_gitignore line for line;
    # cc_memory/ui/installer.py:_init_project is copy #3.
    gi = mem_dir / '.gitignore'
    _ign = ['# cc-memory: generated state, not content', 'memory.db', 'memory.db-wal',
            'memory.db-shm', 'sessions/', '.last_save.json', '.last_inject.json',
            '.last_recall.json',
            '.last_consolidation.json', '.consolidation.lock', '.consolidation.kick',
            '.observer.lock', '.retro.lock', '.llm_backoff.json',
            '.pre_compact_attempt.json', '.plan_raw.md', '.plan_history/', '*.tmp']
    # errors='replace', mirroring core/progress.py's canonical read: a user
    # line appended from a GBK editor or a PowerShell redirect makes strict
    # UTF-8 raise UnicodeDecodeError, which aborted this whole skill with a
    # traceback (reproduced with a UTF-16 .gitignore: rc=1). The canonical
    # implementation gained errors='replace' for exactly this and the two
    # literal copies missed it — the drift the parity gate now checks.
    _cur = gi.read_text(encoding='utf-8', errors='replace') if gi.exists() else ''
    _have = {l.rstrip() for l in _cur.splitlines()}  # rstrip: leading space is significant to git (D3)
    _miss = [l for l in _ign if l not in _have]
    if _miss:
        _pre = _cur if _cur.endswith('\n') or not _cur else _cur + '\n'
        gi.write_text(_pre + '\n'.join(_miss) + '\n', encoding='utf-8')

# ── (3) Resolve the package tree (env override > activated layout) ─────
# why: hardcoding the maintainer's path breaks the skill on every other
# machine. CLAUDE_PLUGIN_ROOT is set by Claude Code in a plugin context and
# wins when it resolves; otherwise use the layout certified above, whose
# pkg dir is already the correct one for its shape (flat or nested).
pkg_dir = _pkg_dir(os.environ.get('CLAUDE_PLUGIN_ROOT')) or best['pkg']
sys.path.insert(0, str(pkg_dir.resolve()))

from core.db import MemoryDB
from core.progress import write_progress_md, migrate_legacy_handoff
from llm.memory_writer import regenerate_memory_index

# Migrate any v2.0 SESSION_HANDOFF.md aside before generating PROGRESS.md
migrate_legacy_handoff(mem_dir)

db = MemoryDB(db_path)
pid = db.upsert_project(str(project))
if not db.get_progress(pid):
    db.upsert_progress(pid, trigger_type='ccm-load')
write_progress_md(db, pid, mem_dir)
regenerate_memory_index(db, pid, mem_dir)
print(f'[OK] Project initialized: {mem_dir}')
print(f'[OK] PROGRESS.md + MEMORY.md generated; legacy handoff migrated if present')

# ── (4) Quick stats ─────────────────────────────────────────────────────
stats = db.get_stats(pid)
n_obs = db.get_observation_count(pid)
print(f'[stats] {stats[\"n_memories\"]} memories | {stats[\"n_sessions\"]} sessions '
      f'| {stats.get(\"n_topics\", 0)} topics | {n_obs} observations')
print()
print('cc-memory is loaded for this project. Hooks will fire automatically:')
print('  - UserPromptSubmit: track turn count + seed PROGRESS.md current_request')
print('  - PostToolUse:      live plan anchor (ExitPlanMode / TodoWrite, every mode) + observations')
print('  - Stop:             Haiku observer + idle reorg every 5 turns')
print('  - PreCompact:       full extraction + PROGRESS.md rewrite')
print('  - SessionStart:     inject context + FORCED <system-reminder> for read-first')
"
```

### Step 2 — Report

Summarize to the user in 1-2 sentences:
- If not activated: relay the script's own **layout-appropriate** fix verbatim —
  a standalone/`.exe` user re-runs `cc-memory-installer.exe`; a marketplace/dev
  user runs `/plugin install cc-memory@cc-memory`. Do not offer the marketplace
  route to a user who has no repo. Then: "…, then re-run /ccm-load."
- If initialized fresh: "cc-memory loaded for {project_name}. PROGRESS.md and MEMORY.md generated; hooks will fire on subsequent activity."
- If already initialized: "cc-memory active here — {n_memories} memories, {n_sessions} sessions, last update at {timestamp}. PROGRESS.md refreshed."

### When to invoke

- **New project** that should benefit from cross-session memory.
- **After cloning** a repo that has a `.ccm/` directory but you've not yet
  loaded the project under cc-memory globally.
- **After upgrading** cc-memory to a new version, to confirm PROGRESS.md
  and the state directory initialized correctly.
- **Whenever PROGRESS.md or MEMORY.md is missing** but you expected them
  (e.g. you suspect a partial uninstall).

### Relation to other cc-memory entry points

These entry points are deliberately **orthogonal** — each owns something the
others cannot do. Nothing here is a subset of anything else.

| Entry point | Owns |
|-------------|------|
| `/ccm-load` (this) | **Activation + bootstrap**: global plugin-enablement check, package-tree resolution, project `.ccm/` creation, PROGRESS.md seeding. Run once per new project |
| `/cc-mem status` | **Ongoing diagnostics**: install-layout inspection, hook-registration verdicts, API-key resolution, last-save staleness. None of these are run by `/ccm-load` |
| `/cc-mem <other>` | **Querying and management**: search, list, topics, progress, consolidate, plan-* |
| `/cc-mem dashboard` | Tkinter GUI for the current project |
| `/save-memories` | **Manual write path** through the anti-patch writer. The hooks save automatically; this is the on-demand trigger |

`/ccm-load` is the recommended first command in a new project. Day-to-day
inspection uses `/cc-mem <subcommand>`; deliberate saves use `/save-memories`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.