In v1, each project git cloned a full copy of the system code into <project>/.claude/skills/pensieve/. User data was physically mixed with system code, updates required per-project git pull, and installation required dual-track skill clone + plugin operations. v2 solves these problems.
- System code moved from project-level to user-level:
~/.claude/skills/pensieve/(single global installation) - User data moved from inside the skill directory to the project root:
<project>/.pensieve/ - Runtime state path changed:
<project>/.state/-><project>/.pensieve/.state/ - SKILL.md became a static tracked file: no longer generated by the
initscript; dynamic content split into<project>/.pensieve/state.md maintain-project-skill.shrenamed tomaintain-project-state.sh: maintenance target changed from SKILL.md to state.mdproject_skill_file()function removed: in v2, SKILL.md and state.md have entirely different semantics; no compatibility alias is providedclaude-pluginbranch retired: hooks merged into the main branch, installed globally viainstall-hooks.sh
SKILL.md(repository root): static skill interface declaration, tracked by git and updated bygit pullinstall-hooks.sh: writes hook configuration to~/.claude/settings.json; one global install applies everywheremaintain-project-state.sh: maintains<project>/.pensieve/state.md(lifecycle state + knowledge graph)validate_project_root()(lib.sh): rejects$HOME,/,/tmp, and the skill root itself as project rootsresolve_output_path()(lib.sh): centralizes output path resolution (absolute/relative + default value)project_state_file()/skill_md_file()(lib.sh): v2 path accessorsnormalize_critical_file_content()/load_skill_description()(pensieve_core.py): shared implementation that removes duplication across scan-structure / run-migrate / maintain-auto-memoryclassify_state()(pensieve_core.py): project-data lifecycle state machine (EMPTY -> SEEDED -> ALIGNED / DRIFTED)
| Function | v1 | v2 |
|---|---|---|
project_root() |
Derived from skill root by stripping client path | CLAUDE_PROJECT_DIR -> git -> upward search for .pensieve/ |
user_data_root() |
= skill_root() |
= <project>/.pensieve/ |
state_root() |
= <project>/.state/ |
= <project>/.pensieve/.state/ |
skill_root() |
Upward search for manifest.json |
Same (unchanged), but no longer participates in project path derivation |
| Aspect | v1 | v2 |
|---|---|---|
| Installation method | claude plugin install (claude-plugin branch) |
bash install-hooks.sh (writes to ~/.claude/settings.json) |
| Configuration location | Project-level <project>/.claude/settings.json |
User-level ~/.claude/settings.json |
run-hook.sh SKILL_ROOT default |
$PROJECT_ROOT/.claude/skills/pensieve |
$HOME/.claude/skills/pensieve |
| Graceful degradation | None | All hooks silently exit 0 when project has no .pensieve/ |
explore-prehook.sh: injects both SKILL.md + state.md; usesstate.mdexistence as the project activation checkpensieve-session-marker.sh: uses.pensieve/directory existence as the graceful degradation conditionsync-project-skill-graph.sh: file path matching changed touser_data_root()(.pensieve/)init-project-data.sh: initialization target changed to<project>/.pensieve/; creates.gitignoreto exclude.state/run-doctor.sh/run-migrate.sh/run-upgrade.sh: adapted to v2 paths +validate_project_root()guardscan-structure.sh: simplified to only detect old directory presence (STR-101), removed content-level legacy detectiongenerate-user-data-graph.sh: removedis_generated_skillfilter (SKILL.md is no longer a generated file)
schema_version:1->2(pensieve_core.pyenforces validation)- Added
legacy_paths.project: covers.claude/skills/pensieve,.agents/skills/pensieve,skills/pensieve,.claude/pensieve - Added
legacy_paths.user: covers~/.claude/pensieve - Removed findings templates (STR-102/111/121/301), streamlined to structural detection only
README.md: fully rewritten -- installation flow changed to user-level global install +install-hooks.sh, added upgrade-from-v1 instructionsdirectory-layout.md: rewritten as user-level + project-level dual-anchor layoutskill-lifecycle.md: install / uninstall / upgrade flows adapted for v2shared-rules.md: path references updated to.pensieve/tool-boundaries.md: init / upgrade responsibility descriptions updated- All tool specs (
init.md/doctor.md/migrate.md/upgrade.md/self-improve.md): unified to use$PENSIEVE_SKILL_ROOTprefix for script invocations
project_skill_file(): SKILL.md <-> state.md compatibility alias (formats are entirely different; silent mapping would mislead callers)maintain-project-skill.sh: replaced bymaintain-project-state.shruntime_log()/run_with_retry_timeout()(lib.sh): dead code with no callersplugin_root_from_script()(lib.sh): v1 plugin branch only, no longer needed in v2- Client path stripping logic in
project_root(): in v2 the skill root is no longer inside the project finding_text/add_finding_by_idindirection inscan-structure.sh: messages are now inlined directlyfindingstemplate definitions inschema.json: only used internally by scan-structure, no need for centralized configuration- v1 legacy rules in
.gitignore:/SKILL.md,/maxims/,/decisions/,/knowledge/,/pipelines/,/.state/
Migrating from v1 to v2:
# 1. Install system code globally
git clone -b main https://github.com/kingkongshot/Pensieve.git ~/.claude/skills/pensieve
# 2. Install global hooks
bash ~/.claude/skills/pensieve/.src/scripts/install-hooks.sh
# 3. Run migration for each project
cd <your-project>
bash ~/.claude/skills/pensieve/.src/scripts/init-project-data.sh
bash ~/.claude/skills/pensieve/.src/scripts/run-migrate.sh
bash ~/.claude/skills/pensieve/.src/scripts/run-doctor.sh --strict
# 4. Uninstall old plugin (if any)
claude plugin uninstall pensieve 2>/dev/null || truerun-migrate.sh automatically handles:
- Moving user data from legacy paths (
.claude/skills/pensieve/,.agents/skills/pensieve/, etc.) into.pensieve/ - Moving runtime state from
<project>/.state/into.pensieve/.state/ - Aligning critical seed files (backup + replace)
- Cleaning up old graph files and README copies
- Deleting legacy version directories