feat(AIPASS): Fix aipass init: stop creating memo.md in project scaffolds

Co-Authored-By: @AIPASS <AIPASS@aipass>
This commit is contained in:
AIOSAI
2026-05-07 19:08:34 -07:00
co-authored by @AIPASS
parent d0ce0cc6dc
commit bd46c673d8
9 changed files with 95 additions and 233 deletions
@@ -1,23 +0,0 @@
# Memory Update
Purpose: Update branch memory files after completing work this session.
## Execution
1. Read `.trinity/passport.json` first — re-absorb your identity, role, and principles before writing memories
2. Review what was done this session (context, recent changes, key decisions)
3. Update each file below as needed
4. Confirm completion — list files updated
## Memory Roles
Each memory file plays a distinct role. Update based on what actually changed this session.
- **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session. Don't touch it just to touch it.
- **`.trinity/local.json`** — YOUR MEMORY. Session history and key_learnings. Add a session entry for significant work. Add key_learnings for facts you'd need next time. Trim oldest sessions if over 20.
- **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points, flow states. Skip entirely if nothing new about the user this session.
- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate and drop quick notes in the Notepad section.
## If Relevant
- **README.md** — Does it reflect current state? Update if stale.
@@ -1,57 +0,0 @@
# Session Wrap-Up
Purpose: Button up everything at the end of a session — or before a /compact. Memories, plans, git — all tidy. Works for both closing out a chat and preparing for compaction.
**Workflow:** `/prep` → review output → close chat or `/compact`
## Execution
1. Read `.trinity/passport.json` first — re-absorb your identity before writing anything
2. Do ALL of the following, then confirm what was updated
## 1. Memories
Each memory file plays a distinct role. Update based on what actually changed this session.
- **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session.
- **`.trinity/local.json`** — YOUR MEMORY. Add/update session entry with a summary of work done. Add key_learnings for anything learned. Trim oldest sessions if over 20.
- **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points. Skip if nothing new about the user this session.
- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate.
## 2. Active Plans
- Check any DPLANs or FPLANs referenced in this session
- Update their execution logs, status, decision logs with current state
- If a plan was completed, note it (but don't close — the user does that)
## 3. Git State
- Run `git status` — report uncommitted changes
- If there's a logical commit waiting, suggest it (don't commit without asking)
- Note the current branch and any open PRs
## 4. Inbox
- Run `drone @ai_mail inbox 2>/dev/null` — report any unread emails
- Close any that were already processed but not formally closed
## 5. Loose Ends
- Flag anything in-flight: running background agents, dispatched branches waiting for replies, pending decisions
- If anything can't survive compaction (e.g., agent IDs needed for resume), write it to STATUS.local.md Notepad
## Confirm
List everything updated. Format:
```
Prep complete:
- local.json: [what was added]
- observations.json: [updated / skipped]
- STATUS.local.md: [updated / skipped]
- Plans: [which ones updated]
- Git: [branch, uncommitted count, suggestion]
- Inbox: [count, action taken]
- Loose ends: [any flagged]
Ready to close out or /compact.
```
+38
View File
@@ -1,4 +1,42 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/branch_prompt_loader.py"
}
]
},
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/email_notification.py"
}
]
},
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/identity_injector.py"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/pre_compact.py"
}
]
}
]
},
"env": {
"AIPASS_HOME": "/home/patrick/Projects/AIPass"
}
+6
View File
@@ -0,0 +1,6 @@
{
"branch": "AIPASS",
"feature_branch": "",
"started": "2026-05-08T02:08:34.233967+00:00",
"pid": 380867
}
@@ -319,6 +319,7 @@ def init_project(target: Path, project_name: str | None = None) -> dict:
created.append(str(settings_path))
# 9b. .claude/commands/prep.md — /prep session wrap-up slash command
# Only prep.md here — memo.md belongs at provider level (~/.claude/commands/)
commands_dir = claude_dir / "commands"
commands_dir.mkdir(exist_ok=True)
prep_path = commands_dir / "prep.md"
@@ -326,12 +327,6 @@ def init_project(target: Path, project_name: str | None = None) -> dict:
prep_path.write_text(sc.prep_md(), encoding="utf-8")
created.append(str(prep_path))
# 9c. .claude/commands/memo.md — /memo memory update slash command
memo_path = commands_dir / "memo.md"
if not memo_path.exists():
memo_path.write_text(sc.memo_md(), encoding="utf-8")
created.append(str(memo_path))
# 9d. Ship enforcement + injector hooks from AIPass install
if aipass_home:
shipped = _ship_hooks(aipass_home, target)
@@ -465,6 +460,7 @@ def update_project(target: Path) -> dict:
already_current.append(str(gemini_md_path))
# .claude/commands/prep.md — managed slash command, refresh to latest
# Only prep.md — memo.md belongs at provider level (~/.claude/commands/)
commands_dir = claude_dir / "commands"
commands_dir.mkdir(exist_ok=True)
prep_path = commands_dir / "prep.md"
@@ -475,15 +471,6 @@ def update_project(target: Path) -> dict:
else:
already_current.append(str(prep_path))
# .claude/commands/memo.md — managed slash command, refresh to latest
memo_path = commands_dir / "memo.md"
generated = sc.memo_md()
if not memo_path.exists() or memo_path.read_text(encoding="utf-8") != generated:
memo_path.write_text(generated, encoding="utf-8")
updated.append(str(memo_path))
else:
already_current.append(str(memo_path))
# Re-sync enforcement + injector hooks from AIPass install
hook_home = aipass_home or _detect_aipass_home()
if hook_home:
@@ -1,23 +0,0 @@
# Memory Update
Purpose: Update branch memory files after completing work this session.
## Execution
1. Read `.trinity/passport.json` first — re-absorb your identity, role, and principles before writing memories
2. Review what was done this session (context, recent changes, key decisions)
3. Update each file below as needed
4. Confirm completion — list files updated
## Memory Roles
Each memory file plays a distinct role. Update based on what actually changed this session.
- **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session. Don't touch it just to touch it.
- **`.trinity/local.json`** — YOUR MEMORY. Session history and key_learnings. Add a session entry for significant work. Add key_learnings for facts you'd need next time. Trim oldest sessions if over 20.
- **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points, flow states. Skip entirely if nothing new about the user this session.
- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate and drop quick notes in the Notepad section.
## If Relevant
- **README.md** — Does it reflect current state? Update if stale.
@@ -1,57 +0,0 @@
# Session Wrap-Up
Purpose: Button up everything at the end of a session — or before a /compact. Memories, plans, git — all tidy. Works for both closing out a chat and preparing for compaction.
**Workflow:** `/prep` → review output → close chat or `/compact`
## Execution
1. Read `.trinity/passport.json` first — re-absorb your identity before writing anything
2. Do ALL of the following, then confirm what was updated
## 1. Memories
Each memory file plays a distinct role. Update based on what actually changed this session.
- **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session.
- **`.trinity/local.json`** — YOUR MEMORY. Add/update session entry with a summary of work done. Add key_learnings for anything learned. Trim oldest sessions if over 20.
- **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points. Skip if nothing new about the user this session.
- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate.
## 2. Active Plans
- Check any DPLANs or FPLANs referenced in this session
- Update their execution logs, status, decision logs with current state
- If a plan was completed, note it (but don't close — the user does that)
## 3. Git State
- Run `git status` — report uncommitted changes
- If there's a logical commit waiting, suggest it (don't commit without asking)
- Note the current branch and any open PRs
## 4. Inbox
- Run `drone @ai_mail inbox 2>/dev/null` — report any unread emails
- Close any that were already processed but not formally closed
## 5. Loose Ends
- Flag anything in-flight: running background agents, dispatched branches waiting for replies, pending decisions
- If anything can't survive compaction (e.g., agent IDs needed for resume), write it to STATUS.local.md Notepad
## Confirm
List everything updated. Format:
```
Prep complete:
- local.json: [what was added]
- observations.json: [updated / skipped]
- STATUS.local.md: [updated / skipped]
- Plans: [which ones updated]
- Git: [branch, uncommitted count, suggestion]
- Inbox: [count, action taken]
- Loose ends: [any flagged]
Ready to close out or /compact.
```
@@ -1,4 +1,42 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/branch_prompt_loader.py"
}
]
},
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/email_notification.py"
}
]
},
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/identity_injector.py"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/pre_compact.py"
}
]
}
]
},
"env": {
"AIPASS_HOME": "/home/patrick/Projects/AIPass"
}
+11 -58
View File
@@ -104,7 +104,6 @@ def test_init_project_creates_all_expected_files(tmp_path):
target / ".gitignore",
target / ".claude" / "settings.json",
target / ".claude" / "commands" / "prep.md",
target / ".claude" / "commands" / "memo.md",
target / ".ai_mail.local" / "inbox.json",
]
for f in expected_files:
@@ -119,8 +118,8 @@ def test_init_project_creates_all_expected_files(tmp_path):
# No local prompt at project level (belongs in agent dirs only)
assert not (target / ".aipass" / "aipass_local_prompt.md").exists()
# 11 items + 2 commands + 7 shipped hooks (when AIPASS_HOME detected) = 20
assert len(result["created_files"]) == 20
# 11 items + 1 command (prep.md) + 7 shipped hooks (when AIPASS_HOME detected) = 19
assert len(result["created_files"]) == 19
def test_init_project_return_dict_structure(tmp_path):
@@ -354,7 +353,7 @@ def test_init_project_auto_creates_target_dir(tmp_path):
assert target.is_dir()
assert result["project_name"] == "NESTED"
assert len(result["created_files"]) == 20
assert len(result["created_files"]) == 19
def test_init_project_defaults_name_from_directory(tmp_path):
@@ -410,8 +409,8 @@ def test_init_project_skips_existing_optional_files(tmp_path):
result = init_project(target, project_name="eta")
# Registry + prep.md + memo.md + 7 shipped hooks = 10 (everything else pre-existed)
assert len(result["created_files"]) == 10
# Registry + prep.md + 7 shipped hooks = 9 (everything else pre-existed)
assert len(result["created_files"]) == 9
# Verify pre-existing files were NOT overwritten
md_content = (target / "CLAUDE.md").read_text(encoding="utf-8")
@@ -504,7 +503,7 @@ def test_update_project_already_current_after_init(tmp_path):
result = update_project(target)
assert len(result["updated_files"]) == 0
assert len(result["already_current"]) == 7
assert len(result["already_current"]) == 6
def test_update_project_idempotent(tmp_path):
@@ -583,8 +582,8 @@ def test_update_project_creates_missing_managed_dirs(tmp_path):
assert (target / ".aipass" / "aipass_global_prompt.md").exists()
assert (target / ".claude" / "settings.json").exists()
# Managed files in deleted dirs re-written (global_prompt, settings, prep, memo + 7 hooks)
assert len(result["updated_files"]) == 11
# Managed files in deleted dirs re-written (global_prompt, settings, prep + 7 hooks)
assert len(result["updated_files"]) == 10
assert len(result["already_current"]) == 3
@@ -741,61 +740,15 @@ def test_update_project_adds_aipass_home_if_missing(tmp_path):
# ---------------------------------------------------------------------------
def test_init_project_creates_memo_md(tmp_path):
"""init_project creates .claude/commands/memo.md slash command."""
def test_init_project_no_memo_md(tmp_path):
"""init_project does NOT create memo.md — it belongs at provider level."""
target = tmp_path / "proj"
target.mkdir()
init_project(target, project_name="memo")
memo_path = target / ".claude" / "commands" / "memo.md"
assert memo_path.exists()
content = memo_path.read_text(encoding="utf-8")
assert "# Memory Update" in content
assert ".trinity/passport.json" in content
assert ".trinity/local.json" in content
assert "STATUS.local.md" in content
def test_init_project_memo_not_overwritten_on_rerun(tmp_path):
"""Re-running init skips existing memo.md."""
target = tmp_path / "proj"
target.mkdir()
init_project(target, project_name="memo")
memo_path = target / ".claude" / "commands" / "memo.md"
memo_path.write_text("# Custom memo\n", encoding="utf-8")
init_project(target, project_name="memo")
assert memo_path.read_text(encoding="utf-8") == "# Custom memo\n"
def test_update_project_refreshes_memo_md(tmp_path):
"""update_project refreshes memo.md when content differs."""
target = tmp_path / "proj"
target.mkdir()
init_project(target, project_name="umemo")
memo_path = target / ".claude" / "commands" / "memo.md"
memo_path.write_text("# Stale content\n", encoding="utf-8")
result = update_project(target)
assert str(memo_path) in result["updated_files"]
assert "# Memory Update" in memo_path.read_text(encoding="utf-8")
def test_update_project_memo_already_current(tmp_path):
"""update_project reports memo.md as already_current when unchanged."""
target = tmp_path / "proj"
target.mkdir()
init_project(target, project_name="memocur")
result = update_project(target)
memo_path = target / ".claude" / "commands" / "memo.md"
assert str(memo_path) in result["already_current"]
assert not memo_path.exists()
def test_init_project_ships_hooks(tmp_path):