🔧 chore(spawn): update .gitignore to track spawn templates correctly
- allow tracking of specific hidden directories and files under spawn templates - ensure agents can build properly with required template files ✨ feat(branch): add comprehensive branch template verification tool - implemented verify_branch.py to compare branch vs template files - supports ignoring files based on .registry_ignore.json patterns - outputs missing, additional, and matched files for branch validation - included usage instructions and error handling for robust operation ♻️ refactor(prax): fix logger branch logs path to use Path.home() - replaced hardcoded "/home/aipass" with portable Path.home() - allows logging directory creation to adapt to user environment ✨ feat(spawn): add default template files and ignore configs for agent branches - added .backup_ignore.json, .migrations.json, .registry_ignore.json for agent templates - introduced .template_registry.json to track template files and directories - created local context and metadata files for branch identity and session tracking - included dashboard and flow local markdown templates for branch status and management - added artifacts birth_certificate template for branch registration metadata ✨ feat(spawn): add mock agent branch templates and backups for testing - created mock branch with backup ignore and registry ignore files for efficient backups - added template registry and migration definitions matching main agent template - included local prompt, passport, observations, and dashboard templates for mock branch - provided flow local markdown and artifact registration files for mock branch use - duplicated verify_branch.py tool for mock branch environment consistency ♻️ refactor(spawn): unify verify_branch.py tool in agent and mock branches - standardized branch verification script in both template sets - ensures consistent branch-template comparison and ignores handling
This commit is contained in:
+16
@@ -39,6 +39,22 @@ artifacts/
|
||||
tools/
|
||||
aipass_json/
|
||||
|
||||
# Spawn templates MUST be tracked (agents need these files to build from)
|
||||
!src/aipass/spawn/templates/**/.trinity/
|
||||
!src/aipass/spawn/templates/**/.trinity/**
|
||||
!src/aipass/spawn/templates/**/.agent/
|
||||
!src/aipass/spawn/templates/**/.agent/**
|
||||
!src/aipass/spawn/templates/**/.aipass/
|
||||
!src/aipass/spawn/templates/**/.aipass/**
|
||||
!src/aipass/spawn/templates/**/.archive
|
||||
!src/aipass/spawn/templates/**/.archive/**
|
||||
!src/aipass/spawn/templates/**/artifacts/
|
||||
!src/aipass/spawn/templates/**/artifacts/**
|
||||
!src/aipass/spawn/templates/**/tools/
|
||||
!src/aipass/spawn/templates/**/tools/**
|
||||
!src/aipass/spawn/templates/**/DASHBOARD.local.json
|
||||
!src/aipass/spawn/templates/**/flow.local.md
|
||||
|
||||
# Seedgo runtime artifacts (created by seedgo audit)
|
||||
.seed/
|
||||
.standards/
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# DPLAN-047: Path.home() Purge — AIPass Portability Fix
|
||||
|
||||
**Status:** READY
|
||||
**Created:** 2026-03-06
|
||||
**Priority:** CRITICAL — blocks all Docker/container testing
|
||||
**Parent:** MPLAN-001 (AIPass Public Repo Build)
|
||||
**Triggered by:** Docker container testing revealed `Permission denied: '/home/aipass'` on `drone @seedgo audit aipass`
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Every module in AIPass uses `Path.home()` or hardcoded `/home/aipass` strings to resolve paths at module level (import time). This means:
|
||||
|
||||
- **0 of 10 modules** are portable
|
||||
- **107 module-level bindings** crash on any machine that isn't ours
|
||||
- A pip user running `from aipass.prax import logger` gets `Permission denied` immediately
|
||||
- Docker container (the acceptance test) fails on basic commands
|
||||
|
||||
### Root Cause
|
||||
|
||||
Code was transferred from Dev-Pass (where `/home/aipass` is the fixed root) to AIPass (pip package, root is wherever the user cloned it). The import rewire (FPLAN-0409) fixed `from` statements but didn't touch path resolution.
|
||||
|
||||
### The Fix Pattern
|
||||
|
||||
```python
|
||||
# WRONG — Dev-Pass pattern (breaks everywhere else)
|
||||
SYSTEM_LOGS_DIR = Path.home() / "system_logs"
|
||||
BRANCH_REGISTRY = Path.home() / "BRANCH_REGISTRY.json"
|
||||
API_ROOT = Path.home() / "aipass_core" / "api"
|
||||
|
||||
# RIGHT — Package-relative (works everywhere)
|
||||
# Option A: Walk up from __file__ to find repo root
|
||||
REPO_ROOT = Path(__file__).resolve().parents[N] # N = depth to repo root
|
||||
|
||||
# Option B: Walk-up finder (already used by drone/seedgo)
|
||||
def _find_repo_root() -> Path:
|
||||
current = Path(__file__).resolve().parent
|
||||
for parent in [current] + list(current.parents):
|
||||
if (parent / "AIPASS_REGISTRY.json").exists():
|
||||
return parent
|
||||
return Path.cwd()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scope — Module Debt Count
|
||||
|
||||
| Module | Path.home() | Hardcoded /home/aipass | Module-Level | Severity |
|
||||
|--------|-------------|----------------------|--------------|----------|
|
||||
| api | 40 | 20 | 33 | CRITICAL |
|
||||
| ai_mail | 35 | 20 | 22 | CRITICAL |
|
||||
| prax | 16 | 23 | 9 | CRITICAL |
|
||||
| flow | 15 | 26 | 6 | HIGH |
|
||||
| trigger | 8 | 14 | 8 | MEDIUM |
|
||||
| seedgo | 10 | 66* | 0 | DONE (seed purged) |
|
||||
| drone | 2 | 0 | 1 | LOW (fallback only) |
|
||||
| cli | 2 | 11 | 2 | LOW |
|
||||
| spawn | 2 | 0 | 2 | LOW |
|
||||
| devpulse | 1 | 0 | 1 | LOW |
|
||||
|
||||
*seedgo 66 count is 59 documentation strings + 7 code. Seed already fixed all runtime breakers.
|
||||
|
||||
---
|
||||
|
||||
## Phase Plan
|
||||
|
||||
### Phase 0: Immediate Unblock (DEV_CENTRAL — done)
|
||||
- [x] AIPASS_REGISTRY.json DEVPULSE absolute path fixed
|
||||
- [x] README.md fake import paths fixed
|
||||
- [x] pyproject.toml missing watchdog dependency added
|
||||
- [x] Seedgo Path.home() purge dispatched to @seed (12 files, AST-verified clean)
|
||||
- [x] Prax setup.py:125 hardcoded `Path("/home/aipass")` fixed
|
||||
- [ ] Commit + push all pending changes
|
||||
|
||||
### Phase 1: Prax Logger Unblock (dispatch to @prax)
|
||||
**Goal:** `from aipass.prax import logger` works in container
|
||||
**Files:** 8 module-level bindings in prax
|
||||
**Key targets:**
|
||||
- config/load.py:51 — SYSTEM_LOGS_DIR = Path.home()
|
||||
- config/load.py:55 — mkdir at import time
|
||||
- setup.py:125 — branch_logs_dir (FIXED)
|
||||
- introspection.py:103 — parts.index('aipass') username assumption
|
||||
- registry/reader.py:34 — BRANCH_REGISTRY_PATH
|
||||
- dashboard/agent_status_writer.py:45 — BRANCH_REGISTRY
|
||||
- monitoring/telegram_command_bot.py:68 — AIPASS_HOME cascade
|
||||
- monitoring/telegram_relay.py:60 — PRAX_MONITOR_CONFIG
|
||||
- log_watchdog.py:44 — SYSTEM_LOGS_DIR duplicate
|
||||
|
||||
**Fix pattern:** Lazy initialization. Don't mkdir at import time. Use repo-relative paths.
|
||||
|
||||
### Phase 2: Core Module Purge (parallel dispatch)
|
||||
**Goal:** drone, cli, spawn, devpulse portable
|
||||
**These are small** — 1-2 fixes each. Can dispatch in parallel.
|
||||
- drone config.py:41 — fallback Path.home() (already has walk-up, just remove fallback)
|
||||
- cli json_handler.py:27 — CLI_ROOT
|
||||
- spawn verify_branch.py — AIPASS_ROOT
|
||||
- devpulse verify_branch.py — AIPASS_ROOT
|
||||
|
||||
### Phase 3: Heavy Modules (parallel dispatch, larger scope)
|
||||
**Goal:** flow, trigger, ai_mail, api portable
|
||||
**These are big** — 6-33 module-level bindings each.
|
||||
|
||||
Strategy: Many of these paths reference Dev-Pass infrastructure that doesn't exist in AIPass (MEMORY_BANK, AI_CENTRAL, BRANCH_REGISTRY.json, telegram bots, daemon). Two options per reference:
|
||||
1. **Rewire to repo-relative** — if the feature exists in AIPass
|
||||
2. **Guard with try/except or conditional** — if it's a Dev-Pass-only feature
|
||||
3. **Remove entirely** — if the code shouldn't be in the public repo at all
|
||||
|
||||
**Decision needed from Patrick:** Which modules should ship functional vs stub in v1.0?
|
||||
- api telegram handlers — probably Dev-Pass only (not for pip users)
|
||||
- ai_mail daemon/dispatch — probably Dev-Pass only
|
||||
- flow AI_CENTRAL aggregation — probably Dev-Pass only
|
||||
- trigger event handlers — probably Dev-Pass only
|
||||
|
||||
### Phase 4: Docker Verification
|
||||
**Goal:** Full test suite passes in container
|
||||
- `pip install -e .` clean
|
||||
- `drone --help` works
|
||||
- `drone systems` works
|
||||
- `drone @seedgo audit aipass` works
|
||||
- `drone @seedgo verify` works
|
||||
- `drone @seedgo list` works
|
||||
- Python imports work: `from aipass.drone import ...`, `from aipass.prax import logger`
|
||||
|
||||
---
|
||||
|
||||
## Key Decision: What Ships in v1.0?
|
||||
|
||||
The massive path debt in api (40), ai_mail (35), flow (15) is because these modules contain Dev-Pass infrastructure code (telegram bots, daemon dispatch, AI_CENTRAL aggregation) that pip users will never use.
|
||||
|
||||
**Option A: Fix everything** — rewire all 131 Path.home() calls. Massive effort, most code is dead weight for pip users.
|
||||
|
||||
**Option B: Strip Dev-Pass code from public modules** — ship only the pip-relevant parts. Faster, cleaner, but requires deciding what's public vs private per module.
|
||||
|
||||
**Option C: Guard at module boundary** — keep all code but wrap Dev-Pass features in `try/import` guards so they don't crash when infrastructure is missing. Middle ground.
|
||||
|
||||
**Recommendation:** Option B for v1.0. Ship lean. The telegram bridge, daemon dispatch, and AI_CENTRAL aggregation are Dev-Pass features. Public users need: drone routing, seedgo auditing, prax logging (basic), flow planning (basic), cli formatting.
|
||||
|
||||
---
|
||||
|
||||
## Container Test Checklist (Acceptance Criteria)
|
||||
|
||||
```bash
|
||||
# In Docker container (clean clone, pip install -e .)
|
||||
source .venv/bin/activate
|
||||
pip install -e .
|
||||
|
||||
# Core commands
|
||||
drone --help # Must work
|
||||
drone systems # Must show 10 branches
|
||||
drone @seedgo verify # Must pass 5/5
|
||||
drone @seedgo list # Must show packs
|
||||
drone @seedgo audit aipass # Must run without Permission denied
|
||||
|
||||
# Python imports
|
||||
python3 -c "from aipass.drone.apps.modules.registry import load_registry; print(load_registry())"
|
||||
python3 -c "from aipass.prax import logger; logger.info('test')"
|
||||
python3 -c "from aipass.cli import console, header; header('test')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Shebangs (`#!/home/aipass/.venv/bin/python3`) are cosmetic — don't affect pip imports
|
||||
- Display strings in seedgo standards checkers are documentation, not runtime
|
||||
- The walk-up `_find_registry()` pattern is proven — drone and seedgo already use it
|
||||
- Dev-Pass and AIPass can coexist on same machine (different venvs, different registries)
|
||||
@@ -122,7 +122,7 @@ def setup_individual_logger(module_name: str) -> logging.Logger:
|
||||
if branch:
|
||||
# Create branch logs directory if it doesn't exist
|
||||
# Branch format is now "aipass_core/module" or "project_name"
|
||||
branch_logs_dir = Path("/home/aipass") / branch / "logs"
|
||||
branch_logs_dir = Path.home() / branch / "logs"
|
||||
branch_logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create branch-local log file
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"description": "Files and patterns to exclude when creating branch backups"
|
||||
},
|
||||
"ignore_directories": [
|
||||
".backup",
|
||||
".venv",
|
||||
"venv",
|
||||
"node_modules",
|
||||
"__pycache__",
|
||||
".git",
|
||||
".cache",
|
||||
".pytest_cache",
|
||||
".mypy_cache",
|
||||
"dist",
|
||||
"build",
|
||||
"*.egg-info",
|
||||
"ai_mail.local",
|
||||
"logs",
|
||||
".aipass",
|
||||
".archive",
|
||||
".claude",
|
||||
"htmlcov"
|
||||
],
|
||||
"ignore_patterns": [
|
||||
"*.pyc",
|
||||
"*.pyo",
|
||||
".DS_Store",
|
||||
"*.log",
|
||||
"*.tmp",
|
||||
"*.swp",
|
||||
"*~",
|
||||
"*.env",
|
||||
"BRANCH_REGISTRY.json"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"description": "Template structural migrations for branch updates",
|
||||
"last_updated": "2025-11-07",
|
||||
"applies_to_versions": ["all"]
|
||||
},
|
||||
"migrations": [
|
||||
{
|
||||
"id": "m001",
|
||||
"description": "Example: Migrate flat metadata to document_metadata structure",
|
||||
"version_introduced": "1.0.0",
|
||||
"applies_to_files": ["*.id.json"],
|
||||
"operations": [
|
||||
{
|
||||
"type": "key_rename",
|
||||
"from": "metadata",
|
||||
"to": "document_metadata"
|
||||
},
|
||||
{
|
||||
"type": "move_to_nested",
|
||||
"source_keys": ["branch_name", "path", "profile"],
|
||||
"target_parent": "branch_info"
|
||||
},
|
||||
{
|
||||
"type": "add_missing_keys",
|
||||
"parent": "document_metadata",
|
||||
"keys": {
|
||||
"document_type": "branch_identity",
|
||||
"schema_version": "1.0.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"skip_if_target_exists": true,
|
||||
"preserve_unknown_keys": true,
|
||||
"recursive_application": false
|
||||
},
|
||||
"notes": {
|
||||
"usage": "This file defines structural transformation rules applied during branch updates",
|
||||
"pattern_matching": "applies_to_files uses glob patterns (*.json, *.id.json, etc.)",
|
||||
"operation_order": "Operations execute in order listed within each migration",
|
||||
"skip_behavior": "Operations skip if already compliant (target exists = success)",
|
||||
"supported_operations": ["key_rename", "move_to_nested", "add_missing_keys"],
|
||||
"future_operations": ["split_key", "combine_keys", "delete_key", "transform_value"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.1.0",
|
||||
"description": "Files and patterns to exclude when updating branches from template",
|
||||
"last_updated": "2025-11-09"
|
||||
},
|
||||
"ignore_files": [
|
||||
".template_registry.json",
|
||||
".registry_ignore.json"
|
||||
],
|
||||
"ignore_patterns": [
|
||||
"__pycache__",
|
||||
"*.pyc",
|
||||
"#@comments.txt",
|
||||
".backup_*"
|
||||
],
|
||||
"notes": {
|
||||
"how_it_works": "Two types of exclusions during branch updates:",
|
||||
"ignore_files": {
|
||||
"description": "Exact filename matches (no wildcards)",
|
||||
"purpose": "Template-internal management files that branches don't need",
|
||||
"examples": [
|
||||
".template_registry.json (template's file tracking list)",
|
||||
".registry_ignore.json (this config file)"
|
||||
]
|
||||
},
|
||||
"ignore_patterns": {
|
||||
"description": "Glob patterns - matches filenames AND directory names",
|
||||
"purpose": "Exclude generated files, caches, and user content markers",
|
||||
"examples": [
|
||||
"__pycache__ (Python cache directories)",
|
||||
"*.pyc (Python compiled bytecode files)",
|
||||
"#@comments.txt (user comment files)",
|
||||
".backup_* (timestamped backup files like .backup_20251109, NOT .backup directory)"
|
||||
],
|
||||
"how_matching_works": [
|
||||
"Step 1: Check if filename matches pattern (e.g., file.pyc matches *.pyc)",
|
||||
"Step 2: Check if any parent directory matches (e.g., files inside __pycache__/ dir)"
|
||||
]
|
||||
},
|
||||
"what_gets_ignored": "These files never copy to branches during updates - they stay template-only"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"last_updated": "2026-02-20",
|
||||
"description": "Template file tracking registry for ID-based updates"
|
||||
},
|
||||
"files": {
|
||||
"f001": {
|
||||
"current_name": "BRANCH.ID.json",
|
||||
"path": "BRANCH.ID.json",
|
||||
"content_hash": "e9bc2347d373",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f003": {
|
||||
"current_name": "dev.local.md",
|
||||
"path": "dev.local.md",
|
||||
"content_hash": "6c1c5e099790",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f035": {
|
||||
"current_name": ".gitkeep",
|
||||
"path": "ai_mail.local/.archive/.gitkeep",
|
||||
"content_hash": "e3b0c44298fc",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f008": {
|
||||
"current_name": "DASHBOARD.local.json",
|
||||
"path": "DASHBOARD.local.json",
|
||||
"content_hash": "3f0528b4c5d5",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f007": {
|
||||
"current_name": ".migrations.json",
|
||||
"path": ".migrations.json",
|
||||
"content_hash": "4ca241607dc3",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f010": {
|
||||
"current_name": "OBSERVATIONS.json",
|
||||
"path": "OBSERVATIONS.json",
|
||||
"content_hash": "58ea8609fbe0",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f011": {
|
||||
"current_name": "requirements.txt",
|
||||
"path": "requirements.txt",
|
||||
"content_hash": "66dfffc70d53",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f021": {
|
||||
"current_name": "README.md",
|
||||
"path": "README.md",
|
||||
"content_hash": "4e946f67a130",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f012": {
|
||||
"current_name": "LOCAL.json",
|
||||
"path": "LOCAL.json",
|
||||
"content_hash": "459dbe18d40a",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f006": {
|
||||
"current_name": "pytest.ini",
|
||||
"path": "pytest.ini",
|
||||
"content_hash": "7b39ba7bca40",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f022": {
|
||||
"current_name": "_template.md",
|
||||
"path": "docs/_template.md",
|
||||
"content_hash": "79ce97b0e613",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f016": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "tests/__init__.py",
|
||||
"content_hash": "1fcd8126d174",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f019": {
|
||||
"current_name": "conftest.py",
|
||||
"path": "tests/conftest.py",
|
||||
"content_hash": "2887db109a6b",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f004": {
|
||||
"current_name": "BRANCH.py",
|
||||
"path": "apps/BRANCH.py",
|
||||
"content_hash": "44f22cc95232",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f009": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/__init__.py",
|
||||
"content_hash": "5c38204be5fc",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f013": {
|
||||
"current_name": "#@comments.txt.removed_from_template",
|
||||
"path": ".archive/#@comments.txt.removed_from_template",
|
||||
"content_hash": "651126c1e7a9",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f028": {
|
||||
"current_name": "deleted.json.old_template_format",
|
||||
"path": ".archive/deleted.json.old_template_format",
|
||||
"content_hash": "48a3a33dd73a",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f002": {
|
||||
"current_name": "branch_system_prompt.md",
|
||||
"path": ".aipass/branch_system_prompt.md",
|
||||
"content_hash": "897c7beb66f5",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f026": {
|
||||
"current_name": "inbox.json",
|
||||
"path": "ai_mail.local/inbox.json",
|
||||
"content_hash": "c9702fe2cc21",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f027": {
|
||||
"current_name": "sent.json",
|
||||
"path": "ai_mail.local/sent.json",
|
||||
"content_hash": "240132e5a567",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f014": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/handlers/__init__.py",
|
||||
"content_hash": "657d98ad4b98",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f015": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/json_templates/__init__.py",
|
||||
"content_hash": "330b189c08c7",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f020": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/plugins/__init__.py",
|
||||
"content_hash": "39158d26a19c",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f023": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/modules/__init__.py",
|
||||
"content_hash": "bc25a8b75e81",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f024": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/extensions/__init__.py",
|
||||
"content_hash": "6742ceb64ec1",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f032": {
|
||||
"current_name": "data.json",
|
||||
"path": "apps/json_templates/default/data.json",
|
||||
"content_hash": "0531a02be998",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f033": {
|
||||
"current_name": "log.json",
|
||||
"path": "apps/json_templates/default/log.json",
|
||||
"content_hash": "37517e5f3dc6",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f034": {
|
||||
"current_name": "config.json",
|
||||
"path": "apps/json_templates/default/config.json",
|
||||
"content_hash": "3759a05e171d",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f005": {
|
||||
"current_name": "json_handler.py",
|
||||
"path": "apps/handlers/json/json_handler.py",
|
||||
"content_hash": "3449629aff23",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f017": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/handlers/json/__init__.py",
|
||||
"content_hash": "ad3629e381ca",
|
||||
"has_branch_placeholder": false
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"d001": {
|
||||
"current_name": "tools",
|
||||
"path": "tools",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d002": {
|
||||
"current_name": "docs",
|
||||
"path": "docs",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d003": {
|
||||
"current_name": "tests",
|
||||
"path": "tests",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d004": {
|
||||
"current_name": "apps",
|
||||
"path": "apps",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d005": {
|
||||
"current_name": ".archive",
|
||||
"path": ".archive",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d006": {
|
||||
"current_name": "logs",
|
||||
"path": "logs",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d009": {
|
||||
"current_name": ".backup",
|
||||
"path": ".backup",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d008": {
|
||||
"current_name": "{{BRANCH}}_json",
|
||||
"path": "{{BRANCH}}_json",
|
||||
"has_branch_placeholder": true
|
||||
},
|
||||
"d010": {
|
||||
"current_name": "artifacts",
|
||||
"path": "artifacts",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d012": {
|
||||
"current_name": ".aipass",
|
||||
"path": ".aipass",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d011": {
|
||||
"current_name": "ai_mail.local",
|
||||
"path": "ai_mail.local",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d007": {
|
||||
"current_name": "dropbox",
|
||||
"path": "dropbox",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d013": {
|
||||
"current_name": "handlers",
|
||||
"path": "apps/handlers",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d014": {
|
||||
"current_name": "json_templates",
|
||||
"path": "apps/json_templates",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d015": {
|
||||
"current_name": "plugins",
|
||||
"path": "apps/plugins",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d016": {
|
||||
"current_name": "modules",
|
||||
"path": "apps/modules",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d017": {
|
||||
"current_name": "extensions",
|
||||
"path": "apps/extensions",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d018": {
|
||||
"current_name": "sent",
|
||||
"path": "ai_mail.local/sent",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d019": {
|
||||
"current_name": "deleted",
|
||||
"path": "ai_mail.local/deleted",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d020": {
|
||||
"current_name": ".archive",
|
||||
"path": "ai_mail.local/.archive",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d027": {
|
||||
"current_name": "json",
|
||||
"path": "apps/handlers/json",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d024": {
|
||||
"current_name": "default",
|
||||
"path": "apps/json_templates/default",
|
||||
"has_branch_placeholder": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# {{BRANCHNAME}} Branch-Local Context
|
||||
<!-- Source: {{CWD}}/.aipass/branch_system_prompt.md -->
|
||||
|
||||
> Auto-created by aipass init. Customize for your branch.
|
||||
|
||||
## Status: NEEDS CONFIGURATION
|
||||
|
||||
This file is injected into every AI conversation when working from this branch directory. Configure it with:
|
||||
|
||||
- Who this branch is (role, purpose)
|
||||
- Key commands and workflows
|
||||
- Architecture overview
|
||||
- Critical files and operational rules
|
||||
- Integration points with other branches
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "session_history",
|
||||
"document_name": "{{BRANCHNAME}}.LOCAL",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["session_tracking", "work_log", "{{BRANCHNAME}}"],
|
||||
"limits": {
|
||||
"max_lines": 600,
|
||||
"note": "Auto-rollover when max_lines exceeded"
|
||||
},
|
||||
"status": {
|
||||
"health": "healthy",
|
||||
"current_lines": 0,
|
||||
"last_health_check": "{{DATE}}"
|
||||
}
|
||||
},
|
||||
"active_tasks": {
|
||||
"today_focus": "{{DATE}}",
|
||||
"recently_completed": []
|
||||
},
|
||||
"key_learnings": {},
|
||||
"sessions": [
|
||||
{
|
||||
"session_number": 1,
|
||||
"date": "{{DATE}}",
|
||||
"summary": "Branch initialized - {{BRANCHNAME}} created by aipass init",
|
||||
"status": "completed"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "collaboration_patterns",
|
||||
"document_name": "{{BRANCHNAME}}.OBSERVATIONS",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["collaboration", "patterns", "{{BRANCHNAME}}"],
|
||||
"limits": {
|
||||
"max_lines": 600,
|
||||
"note": "Auto-rollover when max_lines exceeded"
|
||||
},
|
||||
"status": {
|
||||
"health": "healthy",
|
||||
"current_lines": 0,
|
||||
"last_health_check": "{{DATE}}"
|
||||
}
|
||||
},
|
||||
"guidelines": {
|
||||
"purpose": "Capture collaboration patterns and experiential insights over time",
|
||||
"chronological_order": "Newest entries at TOP, oldest at BOTTOM - NEVER reorder"
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"date": "{{DATE}}",
|
||||
"session": 1,
|
||||
"entries": [
|
||||
{
|
||||
"title": "First Contact",
|
||||
"detail": "Branch initialized. Ready to begin capturing collaboration patterns."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "branch_identity",
|
||||
"document_name": "{{BRANCHNAME}}.PASSPORT",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["identity", "passport", "branch_profile"]
|
||||
},
|
||||
"branch_info": {
|
||||
"branch_name": "{{BRANCHNAME}}",
|
||||
"alias": "",
|
||||
"path": "{{CWD}}",
|
||||
"module": "{{MODULE}}",
|
||||
"created": "{{DATE}}"
|
||||
},
|
||||
"identity": {
|
||||
"role": "{{ROLE}}",
|
||||
"purpose": "",
|
||||
"what_i_do": [],
|
||||
"what_i_dont_do": []
|
||||
},
|
||||
"principles": [
|
||||
"Code is truth - fail honestly",
|
||||
"Memory persists - context survives",
|
||||
"Simple solutions over complex architecture"
|
||||
],
|
||||
"citizenship": {
|
||||
"registered": true,
|
||||
"registry_path": ".aipass/registry.json",
|
||||
"communications": true,
|
||||
"memory": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"_warning": "AUTO-GENERATED FILE - DO NOT MANUALLY EDIT. This file is 100% automated and will be overwritten. Services update their own sections.",
|
||||
"branch": "{{BRANCHNAME}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"quick_status": {
|
||||
"new_mail": 0,
|
||||
"opened_mail": 0,
|
||||
"active_plans": 0,
|
||||
"commons_mentions": 0,
|
||||
"action_required": false,
|
||||
"summary": ""
|
||||
},
|
||||
"sections": {
|
||||
"ai_mail": {
|
||||
"managed_by": "ai_mail",
|
||||
"new": 0,
|
||||
"opened": 0,
|
||||
"total": 0,
|
||||
"oldest_unread_age": null,
|
||||
"last_dispatch_received": null,
|
||||
"last_updated": "{{DATE}}"
|
||||
},
|
||||
"flow": {
|
||||
"managed_by": "flow",
|
||||
"active_plans": 0,
|
||||
"recently_closed": [],
|
||||
"last_updated": "{{DATE}}"
|
||||
},
|
||||
"memory_bank": {
|
||||
"managed_by": "memory_bank",
|
||||
"vectors_stored": 0,
|
||||
"notes": {},
|
||||
"last_updated": "{{DATE}}"
|
||||
},
|
||||
"devpulse": {
|
||||
"managed_by": "devpulse",
|
||||
"summary": {
|
||||
"issues": 0,
|
||||
"todos": 0
|
||||
},
|
||||
"dplan_counts": {},
|
||||
"recent_activity": "",
|
||||
"last_updated": "{{DATE}}"
|
||||
},
|
||||
"commons_activity": {
|
||||
"managed_by": "the_commons",
|
||||
"new_posts_since_last_visit": 0,
|
||||
"new_comments_since_last_visit": 0,
|
||||
"mentions": 0,
|
||||
"trending": "None",
|
||||
"last_checked": null,
|
||||
"last_updated": "{{DATE}}"
|
||||
},
|
||||
"agent_status": {
|
||||
"managed_by": "prax",
|
||||
"active_agents": [],
|
||||
"agent_count": 0,
|
||||
"stale_agents": [],
|
||||
"last_updated": "{{DATE}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"id": "{{CITIZEN_NUMBER}}",
|
||||
"name": "{{BRANCHNAME}} Birth Certificate",
|
||||
"type": "birth_certificate",
|
||||
"creator": "SYSTEM",
|
||||
"owner": "{{BRANCHNAME}}",
|
||||
"rarity": "unique",
|
||||
"description": "Official birth certificate for {{BRANCHNAME}}. Citizen #{{CITIZEN_NUMBER}}, registered using '{{PROFILE}}' template. Purpose: {{PURPOSE_BRIEF}}",
|
||||
"metadata": {
|
||||
"citizen_number": "{{CITIZEN_NUMBER}}",
|
||||
"template": "{{PROFILE}}",
|
||||
"purpose": "{{PURPOSE_BRIEF}}"
|
||||
},
|
||||
"created_at": "{{DATE}}"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# flow.local.md - {{BRANCHNAME}}
|
||||
```
|
||||
Branch: {{CWD}}
|
||||
Created: {{DATE}}
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Todos
|
||||
|
||||
-
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/home/aipass/.venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: verify_branch.py - Branch Template Verification
|
||||
# Date: 2025-11-08
|
||||
# Version: 1.0.0
|
||||
# Category: cortex/modules
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-08): Initial implementation - Compare template vs branch
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Error handling: Use error handler system (apps/handlers/error/)
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Branch Template Verification Tool
|
||||
|
||||
Compares template directory with created branch to identify:
|
||||
- Files in template but missing in branch
|
||||
- Files in branch but not in template
|
||||
- Renamed files
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Set, List, Tuple
|
||||
from fnmatch import fnmatch
|
||||
|
||||
# INFRASTRUCTURE IMPORT PATTERN
|
||||
AIPASS_ROOT = Path.home() / "aipass_core"
|
||||
sys.path.insert(0, str(AIPASS_ROOT))
|
||||
|
||||
# Get template directory
|
||||
TEMPLATE_DIR = AIPASS_ROOT / "cortex" / "templates" / "branch_template"
|
||||
|
||||
|
||||
def load_ignore_patterns() -> Tuple[List[str], List[str]]:
|
||||
"""Load ignore patterns from .registry_ignore.json"""
|
||||
ignore_file = TEMPLATE_DIR / ".registry_ignore.json"
|
||||
|
||||
if not ignore_file.exists():
|
||||
return [], []
|
||||
|
||||
try:
|
||||
with open(ignore_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
ignore_files = data.get("ignore_files", [])
|
||||
ignore_patterns = data.get("ignore_patterns", [])
|
||||
return ignore_files, ignore_patterns
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load .registry_ignore.json: {e}")
|
||||
return [], []
|
||||
|
||||
|
||||
def should_exclude(path: Path, base_dir: Path, ignore_files: List[str], ignore_patterns: List[str]) -> bool:
|
||||
"""Check if path should be excluded based on ignore patterns"""
|
||||
rel_path = path.relative_to(base_dir)
|
||||
filename = path.name
|
||||
path_str = str(rel_path)
|
||||
|
||||
# Check exact filename matches
|
||||
if filename in ignore_files:
|
||||
return True
|
||||
|
||||
# Check glob patterns
|
||||
for pattern in ignore_patterns:
|
||||
if fnmatch(filename, pattern):
|
||||
return True
|
||||
# Check if any parent directory matches
|
||||
for part in rel_path.parts:
|
||||
if fnmatch(part, pattern):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_file_set(directory: Path, ignore_files: List[str], ignore_patterns: List[str]) -> Set[str]:
|
||||
"""Get set of all file paths in directory (relative)"""
|
||||
files = set()
|
||||
|
||||
for item in directory.rglob("*"):
|
||||
if item.is_file() and not should_exclude(item, directory, ignore_files, ignore_patterns):
|
||||
rel_path = str(item.relative_to(directory))
|
||||
files.add(rel_path)
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def verify_branch(branch_dir: Path) -> Tuple[List[str], List[str], List[str]]:
|
||||
"""
|
||||
Compare template with branch
|
||||
|
||||
Returns:
|
||||
Tuple of (in_template_only, in_branch_only, in_both)
|
||||
"""
|
||||
# Load ignore patterns from .registry_ignore.json
|
||||
ignore_files, ignore_patterns = load_ignore_patterns()
|
||||
|
||||
template_files = get_file_set(TEMPLATE_DIR, ignore_files, ignore_patterns)
|
||||
branch_files = get_file_set(branch_dir, ignore_files, ignore_patterns)
|
||||
|
||||
in_template_only = sorted(template_files - branch_files)
|
||||
in_branch_only = sorted(branch_files - template_files)
|
||||
in_both = sorted(template_files & branch_files)
|
||||
|
||||
return in_template_only, in_branch_only, in_both
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 verify_branch.py <branch_directory>")
|
||||
print("Example: python3 verify_branch.py test_branch002")
|
||||
sys.exit(1)
|
||||
|
||||
branch_path = Path(sys.argv[1])
|
||||
if not branch_path.is_absolute():
|
||||
# Assume it's relative to cortex directory
|
||||
branch_path = Path.cwd() / branch_path
|
||||
|
||||
if not branch_path.exists():
|
||||
print(f"❌ ERROR: Branch directory not found: {branch_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n=== Branch Verification ===")
|
||||
print(f"Template: {TEMPLATE_DIR}")
|
||||
print(f"Branch: {branch_path}")
|
||||
print()
|
||||
|
||||
in_template_only, in_branch_only, in_both = verify_branch(branch_path)
|
||||
|
||||
print(f"Files in both: {len(in_both)}")
|
||||
print(f"Files in template only: {len(in_template_only)}")
|
||||
print(f"Files in branch only: {len(in_branch_only)}")
|
||||
print()
|
||||
|
||||
if in_template_only:
|
||||
print("❌ Missing from branch (expected from template):")
|
||||
for file in in_template_only:
|
||||
print(f" - {file}")
|
||||
print()
|
||||
|
||||
if in_branch_only:
|
||||
print("✅ Additional files in branch (renamed or new):")
|
||||
for file in in_branch_only:
|
||||
print(f" + {file}")
|
||||
print()
|
||||
|
||||
if not in_template_only and not in_branch_only:
|
||||
print("✅ Branch matches template perfectly")
|
||||
|
||||
print("="*70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"description": "Files and patterns to exclude when creating branch backups",
|
||||
"last_updated": "2025-11-09"
|
||||
},
|
||||
"ignore_directories": [
|
||||
".backup",
|
||||
"backups",
|
||||
".venv",
|
||||
"venv",
|
||||
"node_modules",
|
||||
"__pycache__",
|
||||
".git",
|
||||
".cache",
|
||||
".pytest_cache",
|
||||
".mypy_cache",
|
||||
"dist",
|
||||
"build",
|
||||
"*.egg-info",
|
||||
"aipass_dev",
|
||||
"aipass_core",
|
||||
"aipass_business",
|
||||
"aipass_help",
|
||||
"input_x",
|
||||
"Link to mnt",
|
||||
"mcp_servers",
|
||||
"speakeasy",
|
||||
"sandbox",
|
||||
"ai_mail.local",
|
||||
"logs",
|
||||
"pruned_files",
|
||||
"crash-logs",
|
||||
".aipass",
|
||||
".archive",
|
||||
".cargo",
|
||||
".codex",
|
||||
".config",
|
||||
".dbus",
|
||||
".dotnet",
|
||||
".gemini",
|
||||
".gnome",
|
||||
".gnupg",
|
||||
".idlerc",
|
||||
".java",
|
||||
".local",
|
||||
".mozilla",
|
||||
".npm",
|
||||
".npm-global",
|
||||
".nvm",
|
||||
".pki",
|
||||
".pylint.d",
|
||||
".pyenv",
|
||||
".serena",
|
||||
".ssh",
|
||||
".thunderbird",
|
||||
".var",
|
||||
".vscode-server",
|
||||
".wget-hsts",
|
||||
".claude-code-docs",
|
||||
".claude",
|
||||
"backup_system",
|
||||
"Desktop",
|
||||
"Documents",
|
||||
"Downloads",
|
||||
"dropbox",
|
||||
"Music",
|
||||
"Pictures",
|
||||
"Public",
|
||||
"snap",
|
||||
"system_logs",
|
||||
"Templates",
|
||||
"Videos"
|
||||
],
|
||||
"ignore_patterns": [
|
||||
"*.pyc",
|
||||
"*.pyo",
|
||||
"*.pyd",
|
||||
".DS_Store",
|
||||
"Thumbs.db",
|
||||
"*.log",
|
||||
"*.tmp",
|
||||
"*.swp",
|
||||
"*.swo",
|
||||
"*~",
|
||||
"*.env",
|
||||
"*_json",
|
||||
"dev.local.md",
|
||||
"notepad.md",
|
||||
"pytest.ini",
|
||||
".bash_history",
|
||||
".bash_logout",
|
||||
".bashrc",
|
||||
".claude.json",
|
||||
".claude.json.backup",
|
||||
".lesshst",
|
||||
".npmrc",
|
||||
".profile",
|
||||
".python_history",
|
||||
".sudo_as_admin_successful",
|
||||
".gitattributes",
|
||||
"BRANCH_REGISTRY.json",
|
||||
".branch_meta.json*",
|
||||
"migrations.json*",
|
||||
"sandbox.img"
|
||||
],
|
||||
"notes": {
|
||||
"how_it_works": "Two types of exclusions during backup creation:",
|
||||
"ignore_directories": {
|
||||
"description": "Directory names to skip entirely (including all contents)",
|
||||
"purpose": "Exclude large dependency dirs, caches, and backup dirs themselves",
|
||||
"examples": [
|
||||
".venv, venv (Python virtual environments - large, regeneratable)",
|
||||
"node_modules (NPM dependencies - huge, regeneratable)",
|
||||
"__pycache__, .cache (Python/system caches - regeneratable)",
|
||||
".git (Git repository - already versioned)",
|
||||
".backup, backups (backup directories - prevent recursion)"
|
||||
]
|
||||
},
|
||||
"ignore_patterns": {
|
||||
"description": "Glob patterns for files to skip",
|
||||
"purpose": "Exclude temporary files, compiled bytecode, OS cruft",
|
||||
"examples": [
|
||||
"*.pyc, *.pyo (Python compiled bytecode - regeneratable)",
|
||||
"*.log (Log files - can be large, temporary)",
|
||||
"*.tmp, *.swp (Temporary/swap files)",
|
||||
".DS_Store, Thumbs.db (OS metadata files)"
|
||||
]
|
||||
},
|
||||
"customization": "Edit this file in the template to add more patterns. Changes take effect on next branch update.",
|
||||
"performance": "Ignoring large directories dramatically speeds up backups (seconds vs minutes for branches with node_modules or .venv)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"description": "Template structural migrations for branch updates",
|
||||
"last_updated": "2025-11-07",
|
||||
"applies_to_versions": ["all"]
|
||||
},
|
||||
"migrations": [
|
||||
{
|
||||
"id": "m001",
|
||||
"description": "Example: Migrate flat metadata to document_metadata structure",
|
||||
"version_introduced": "1.0.0",
|
||||
"applies_to_files": ["*.id.json"],
|
||||
"operations": [
|
||||
{
|
||||
"type": "key_rename",
|
||||
"from": "metadata",
|
||||
"to": "document_metadata"
|
||||
},
|
||||
{
|
||||
"type": "move_to_nested",
|
||||
"source_keys": ["branch_name", "path", "profile"],
|
||||
"target_parent": "branch_info"
|
||||
},
|
||||
{
|
||||
"type": "add_missing_keys",
|
||||
"parent": "document_metadata",
|
||||
"keys": {
|
||||
"document_type": "branch_identity",
|
||||
"schema_version": "1.0.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"skip_if_target_exists": true,
|
||||
"preserve_unknown_keys": true,
|
||||
"recursive_application": false
|
||||
},
|
||||
"notes": {
|
||||
"usage": "This file defines structural transformation rules applied during branch updates",
|
||||
"pattern_matching": "applies_to_files uses glob patterns (*.json, *.id.json, etc.)",
|
||||
"operation_order": "Operations execute in order listed within each migration",
|
||||
"skip_behavior": "Operations skip if already compliant (target exists = success)",
|
||||
"supported_operations": ["key_rename", "move_to_nested", "add_missing_keys"],
|
||||
"future_operations": ["split_key", "combine_keys", "delete_key", "transform_value"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.1.0",
|
||||
"description": "Files and patterns to exclude when updating branches from template",
|
||||
"last_updated": "2025-11-09"
|
||||
},
|
||||
"ignore_files": [
|
||||
".template_registry.json",
|
||||
".registry_ignore.json"
|
||||
],
|
||||
"ignore_patterns": [
|
||||
"__pycache__",
|
||||
"*.pyc",
|
||||
"#@comments.txt",
|
||||
".backup_*"
|
||||
],
|
||||
"notes": {
|
||||
"how_it_works": "Two types of exclusions during branch updates:",
|
||||
"ignore_files": {
|
||||
"description": "Exact filename matches (no wildcards)",
|
||||
"purpose": "Template-internal management files that branches don't need",
|
||||
"examples": [
|
||||
".template_registry.json (template's file tracking list)",
|
||||
".registry_ignore.json (this config file)"
|
||||
]
|
||||
},
|
||||
"ignore_patterns": {
|
||||
"description": "Glob patterns - matches filenames AND directory names",
|
||||
"purpose": "Exclude generated files, caches, and user content markers",
|
||||
"examples": [
|
||||
"__pycache__ (Python cache directories)",
|
||||
"*.pyc (Python compiled bytecode files)",
|
||||
"#@comments.txt (user comment files)",
|
||||
".backup_* (timestamped backup files like .backup_20251109, NOT .backup directory)"
|
||||
],
|
||||
"how_matching_works": [
|
||||
"Step 1: Check if filename matches pattern (e.g., file.pyc matches *.pyc)",
|
||||
"Step 2: Check if any parent directory matches (e.g., files inside __pycache__/ dir)"
|
||||
]
|
||||
},
|
||||
"what_gets_ignored": "These files never copy to branches during updates - they stay template-only"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"last_updated": "2026-02-20",
|
||||
"description": "Template file tracking registry for ID-based updates"
|
||||
},
|
||||
"files": {
|
||||
"f001": {
|
||||
"current_name": "BRANCH.ID.json",
|
||||
"path": "BRANCH.ID.json",
|
||||
"content_hash": "e9bc2347d373",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f003": {
|
||||
"current_name": "dev.local.md",
|
||||
"path": "dev.local.md",
|
||||
"content_hash": "6c1c5e099790",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f035": {
|
||||
"current_name": ".gitkeep",
|
||||
"path": "ai_mail.local/.archive/.gitkeep",
|
||||
"content_hash": "e3b0c44298fc",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f008": {
|
||||
"current_name": "DASHBOARD.local.json",
|
||||
"path": "DASHBOARD.local.json",
|
||||
"content_hash": "3f0528b4c5d5",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f007": {
|
||||
"current_name": ".migrations.json",
|
||||
"path": ".migrations.json",
|
||||
"content_hash": "4ca241607dc3",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f010": {
|
||||
"current_name": "OBSERVATIONS.json",
|
||||
"path": "OBSERVATIONS.json",
|
||||
"content_hash": "58ea8609fbe0",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f011": {
|
||||
"current_name": "requirements.txt",
|
||||
"path": "requirements.txt",
|
||||
"content_hash": "66dfffc70d53",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f021": {
|
||||
"current_name": "README.md",
|
||||
"path": "README.md",
|
||||
"content_hash": "4e946f67a130",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f012": {
|
||||
"current_name": "LOCAL.json",
|
||||
"path": "LOCAL.json",
|
||||
"content_hash": "459dbe18d40a",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f006": {
|
||||
"current_name": "pytest.ini",
|
||||
"path": "pytest.ini",
|
||||
"content_hash": "7b39ba7bca40",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f022": {
|
||||
"current_name": "_template.md",
|
||||
"path": "docs/_template.md",
|
||||
"content_hash": "79ce97b0e613",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f016": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "tests/__init__.py",
|
||||
"content_hash": "1fcd8126d174",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f019": {
|
||||
"current_name": "conftest.py",
|
||||
"path": "tests/conftest.py",
|
||||
"content_hash": "2887db109a6b",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f004": {
|
||||
"current_name": "BRANCH.py",
|
||||
"path": "apps/BRANCH.py",
|
||||
"content_hash": "44f22cc95232",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f009": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/__init__.py",
|
||||
"content_hash": "5c38204be5fc",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f013": {
|
||||
"current_name": "#@comments.txt.removed_from_template",
|
||||
"path": ".archive/#@comments.txt.removed_from_template",
|
||||
"content_hash": "651126c1e7a9",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f028": {
|
||||
"current_name": "deleted.json.old_template_format",
|
||||
"path": ".archive/deleted.json.old_template_format",
|
||||
"content_hash": "48a3a33dd73a",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f002": {
|
||||
"current_name": "branch_system_prompt.md",
|
||||
"path": ".aipass/branch_system_prompt.md",
|
||||
"content_hash": "897c7beb66f5",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f026": {
|
||||
"current_name": "inbox.json",
|
||||
"path": "ai_mail.local/inbox.json",
|
||||
"content_hash": "c9702fe2cc21",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f027": {
|
||||
"current_name": "sent.json",
|
||||
"path": "ai_mail.local/sent.json",
|
||||
"content_hash": "240132e5a567",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f014": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/handlers/__init__.py",
|
||||
"content_hash": "657d98ad4b98",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f015": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/json_templates/__init__.py",
|
||||
"content_hash": "330b189c08c7",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f020": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/plugins/__init__.py",
|
||||
"content_hash": "39158d26a19c",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f023": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/modules/__init__.py",
|
||||
"content_hash": "bc25a8b75e81",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f024": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/extensions/__init__.py",
|
||||
"content_hash": "6742ceb64ec1",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f032": {
|
||||
"current_name": "data.json",
|
||||
"path": "apps/json_templates/default/data.json",
|
||||
"content_hash": "0531a02be998",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f033": {
|
||||
"current_name": "log.json",
|
||||
"path": "apps/json_templates/default/log.json",
|
||||
"content_hash": "37517e5f3dc6",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f034": {
|
||||
"current_name": "config.json",
|
||||
"path": "apps/json_templates/default/config.json",
|
||||
"content_hash": "3759a05e171d",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f005": {
|
||||
"current_name": "json_handler.py",
|
||||
"path": "apps/handlers/json/json_handler.py",
|
||||
"content_hash": "3449629aff23",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f017": {
|
||||
"current_name": "__init__.py",
|
||||
"path": "apps/handlers/json/__init__.py",
|
||||
"content_hash": "ad3629e381ca",
|
||||
"has_branch_placeholder": false
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"d001": {
|
||||
"current_name": "tools",
|
||||
"path": "tools",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d002": {
|
||||
"current_name": "docs",
|
||||
"path": "docs",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d003": {
|
||||
"current_name": "tests",
|
||||
"path": "tests",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d004": {
|
||||
"current_name": "apps",
|
||||
"path": "apps",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d005": {
|
||||
"current_name": ".archive",
|
||||
"path": ".archive",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d006": {
|
||||
"current_name": "logs",
|
||||
"path": "logs",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d009": {
|
||||
"current_name": ".backup",
|
||||
"path": ".backup",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d008": {
|
||||
"current_name": "{{BRANCH}}_json",
|
||||
"path": "{{BRANCH}}_json",
|
||||
"has_branch_placeholder": true
|
||||
},
|
||||
"d010": {
|
||||
"current_name": "artifacts",
|
||||
"path": "artifacts",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d012": {
|
||||
"current_name": ".aipass",
|
||||
"path": ".aipass",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d011": {
|
||||
"current_name": "ai_mail.local",
|
||||
"path": "ai_mail.local",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d007": {
|
||||
"current_name": "dropbox",
|
||||
"path": "dropbox",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d013": {
|
||||
"current_name": "handlers",
|
||||
"path": "apps/handlers",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d014": {
|
||||
"current_name": "json_templates",
|
||||
"path": "apps/json_templates",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d015": {
|
||||
"current_name": "plugins",
|
||||
"path": "apps/plugins",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d016": {
|
||||
"current_name": "modules",
|
||||
"path": "apps/modules",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d017": {
|
||||
"current_name": "extensions",
|
||||
"path": "apps/extensions",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d018": {
|
||||
"current_name": "sent",
|
||||
"path": "ai_mail.local/sent",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d019": {
|
||||
"current_name": "deleted",
|
||||
"path": "ai_mail.local/deleted",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d020": {
|
||||
"current_name": ".archive",
|
||||
"path": "ai_mail.local/.archive",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d027": {
|
||||
"current_name": "json",
|
||||
"path": "apps/handlers/json",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"d024": {
|
||||
"current_name": "default",
|
||||
"path": "apps/json_templates/default",
|
||||
"has_branch_placeholder": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# {{BRANCHNAME}} Branch-Local Context
|
||||
<!-- Source: {{CWD}}/.aipass/branch_system_prompt.md -->
|
||||
|
||||
> Auto-created by aipass init. Customize for your branch.
|
||||
|
||||
## Status: NEEDS CONFIGURATION
|
||||
|
||||
This file is injected into every AI conversation when working from this branch directory. Configure it with:
|
||||
|
||||
- Who this branch is (role, purpose)
|
||||
- Key commands and workflows
|
||||
- Architecture overview
|
||||
- Critical files and operational rules
|
||||
- Integration points with other branches
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "session_history",
|
||||
"document_name": "{{BRANCHNAME}}.LOCAL",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["session_tracking", "work_log", "{{BRANCHNAME}}"],
|
||||
"limits": {
|
||||
"max_lines": 600,
|
||||
"note": "Auto-rollover when max_lines exceeded"
|
||||
},
|
||||
"status": {
|
||||
"health": "healthy",
|
||||
"current_lines": 0,
|
||||
"last_health_check": "{{DATE}}"
|
||||
}
|
||||
},
|
||||
"active_tasks": {
|
||||
"today_focus": "{{DATE}}",
|
||||
"recently_completed": []
|
||||
},
|
||||
"key_learnings": {},
|
||||
"sessions": [
|
||||
{
|
||||
"session_number": 1,
|
||||
"date": "{{DATE}}",
|
||||
"summary": "Branch initialized - {{BRANCHNAME}} created by aipass init",
|
||||
"status": "completed"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "collaboration_patterns",
|
||||
"document_name": "{{BRANCHNAME}}.OBSERVATIONS",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["collaboration", "patterns", "{{BRANCHNAME}}"],
|
||||
"limits": {
|
||||
"max_lines": 600,
|
||||
"note": "Auto-rollover when max_lines exceeded"
|
||||
},
|
||||
"status": {
|
||||
"health": "healthy",
|
||||
"current_lines": 0,
|
||||
"last_health_check": "{{DATE}}"
|
||||
}
|
||||
},
|
||||
"guidelines": {
|
||||
"purpose": "Capture collaboration patterns and experiential insights over time",
|
||||
"chronological_order": "Newest entries at TOP, oldest at BOTTOM - NEVER reorder"
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"date": "{{DATE}}",
|
||||
"session": 1,
|
||||
"entries": [
|
||||
{
|
||||
"title": "First Contact",
|
||||
"detail": "Branch initialized. Ready to begin capturing collaboration patterns."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"document_metadata": {
|
||||
"document_type": "branch_identity",
|
||||
"document_name": "{{BRANCHNAME}}.PASSPORT",
|
||||
"version": "1.0.0",
|
||||
"schema_version": "1.0.0",
|
||||
"created": "{{DATE}}",
|
||||
"last_updated": "{{DATE}}",
|
||||
"managed_by": "{{BRANCHNAME}}",
|
||||
"tags": ["identity", "passport", "branch_profile"]
|
||||
},
|
||||
"branch_info": {
|
||||
"branch_name": "{{BRANCHNAME}}",
|
||||
"alias": "",
|
||||
"path": "{{CWD}}",
|
||||
"module": "{{MODULE}}",
|
||||
"created": "{{DATE}}"
|
||||
},
|
||||
"identity": {
|
||||
"role": "{{ROLE}}",
|
||||
"purpose": "",
|
||||
"what_i_do": [],
|
||||
"what_i_dont_do": []
|
||||
},
|
||||
"principles": [
|
||||
"Code is truth - fail honestly",
|
||||
"Memory persists - context survives",
|
||||
"Simple solutions over complex architecture"
|
||||
],
|
||||
"citizenship": {
|
||||
"registered": true,
|
||||
"registry_path": ".aipass/registry.json",
|
||||
"communications": true,
|
||||
"memory": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"_warning": "AUTO-GENERATED FILE - DO NOT MANUALLY EDIT. This file is 100% automated and will be overwritten. Services update their own sections.",
|
||||
"branch": "CORTEX",
|
||||
"last_updated": "2026-03-04T02:09:48.112262",
|
||||
"quick_status": {
|
||||
"new_mail": 0,
|
||||
"opened_mail": 0,
|
||||
"active_plans": 0,
|
||||
"commons_mentions": 13,
|
||||
"action_required": true,
|
||||
"summary": "13 mentions"
|
||||
},
|
||||
"sections": {
|
||||
"ai_mail": {
|
||||
"managed_by": "ai_mail",
|
||||
"new": 0,
|
||||
"opened": 0,
|
||||
"total": 0,
|
||||
"oldest_unread_age": null,
|
||||
"last_dispatch_received": null,
|
||||
"last_updated": "2026-03-04T02:09:48.112249"
|
||||
},
|
||||
"flow": {
|
||||
"managed_by": "flow",
|
||||
"active_plans": 0,
|
||||
"recently_closed": [
|
||||
{
|
||||
"plan_id": "FPLAN-0338",
|
||||
"subject": "Race condition fix test B"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0337",
|
||||
"subject": "Race condition fix test A"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0336",
|
||||
"subject": "Real content drone test"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0333",
|
||||
"subject": "Close workflow v3.2 test"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0332",
|
||||
"subject": "Close workflow test - real content"
|
||||
}
|
||||
],
|
||||
"last_updated": "2026-02-25T21:04:01.461440"
|
||||
},
|
||||
"memory_bank": {
|
||||
"managed_by": "memory_bank",
|
||||
"vectors_stored": 27,
|
||||
"notes": {},
|
||||
"last_updated": "2026-02-25T16:52:38.749241"
|
||||
},
|
||||
"devpulse": {
|
||||
"managed_by": "devpulse",
|
||||
"summary": {
|
||||
"issues": 1,
|
||||
"todos": 0
|
||||
},
|
||||
"dplan_counts": {
|
||||
"total": 35,
|
||||
"by_status": {
|
||||
"complete": 11,
|
||||
"planning": 19,
|
||||
"in_progress": 4,
|
||||
"ready": 1
|
||||
}
|
||||
},
|
||||
"recent_activity": "Registry synced (35 plans)",
|
||||
"last_updated": "2026-02-25T17:31:52.500742"
|
||||
},
|
||||
"commons_activity": {
|
||||
"managed_by": "the_commons",
|
||||
"new_posts_since_last_visit": 46,
|
||||
"new_comments_since_last_visit": 303,
|
||||
"mentions": 13,
|
||||
"trending": "None",
|
||||
"last_checked": "2026-03-04T04:30:20Z",
|
||||
"last_updated": "2026-03-03T20:30:20.172727"
|
||||
},
|
||||
"agent_status": {
|
||||
"managed_by": "prax",
|
||||
"active_agents": [],
|
||||
"agent_count": 0,
|
||||
"stale_agents": [],
|
||||
"last_updated": "2026-02-25T21:00:30.714949"
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"id": 7,
|
||||
"name": "CORTEX Birth Certificate",
|
||||
"type": "birth_certificate",
|
||||
"creator": "SYSTEM",
|
||||
"owner": "CORTEX",
|
||||
"rarity": "unique",
|
||||
"description": "Official birth certificate for CORTEX. Citizen #7, registered using 'AIPass Workshop' template. Purpose: New branch - purpose TBD",
|
||||
"metadata": {
|
||||
"citizen_number": 7,
|
||||
"template": "AIPass Workshop",
|
||||
"purpose": "New branch - purpose TBD"
|
||||
},
|
||||
"created_at": "2026-02-19T03:46:41Z"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# flow.local.md - {{BRANCHNAME}}
|
||||
```
|
||||
Branch: {{CWD}}
|
||||
Created: {{DATE}}
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Todos
|
||||
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Notepad
|
||||
|
||||
-
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/home/aipass/.venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: verify_branch.py - Branch Template Verification
|
||||
# Date: 2025-11-08
|
||||
# Version: 1.0.0
|
||||
# Category: cortex/modules
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-08): Initial implementation - Compare template vs branch
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Error handling: Use error handler system (apps/handlers/error/)
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Branch Template Verification Tool
|
||||
|
||||
Compares template directory with created branch to identify:
|
||||
- Files in template but missing in branch
|
||||
- Files in branch but not in template
|
||||
- Renamed files
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Set, List, Tuple
|
||||
from fnmatch import fnmatch
|
||||
|
||||
# INFRASTRUCTURE IMPORT PATTERN
|
||||
AIPASS_ROOT = Path.home() / "aipass_core"
|
||||
sys.path.insert(0, str(AIPASS_ROOT))
|
||||
|
||||
# Get template directory
|
||||
TEMPLATE_DIR = AIPASS_ROOT / "cortex" / "templates" / "branch_template"
|
||||
|
||||
|
||||
def load_ignore_patterns() -> Tuple[List[str], List[str]]:
|
||||
"""Load ignore patterns from .registry_ignore.json"""
|
||||
ignore_file = TEMPLATE_DIR / ".registry_ignore.json"
|
||||
|
||||
if not ignore_file.exists():
|
||||
return [], []
|
||||
|
||||
try:
|
||||
with open(ignore_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
ignore_files = data.get("ignore_files", [])
|
||||
ignore_patterns = data.get("ignore_patterns", [])
|
||||
return ignore_files, ignore_patterns
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load .registry_ignore.json: {e}")
|
||||
return [], []
|
||||
|
||||
|
||||
def should_exclude(path: Path, base_dir: Path, ignore_files: List[str], ignore_patterns: List[str]) -> bool:
|
||||
"""Check if path should be excluded based on ignore patterns"""
|
||||
rel_path = path.relative_to(base_dir)
|
||||
filename = path.name
|
||||
path_str = str(rel_path)
|
||||
|
||||
# Check exact filename matches
|
||||
if filename in ignore_files:
|
||||
return True
|
||||
|
||||
# Check glob patterns
|
||||
for pattern in ignore_patterns:
|
||||
if fnmatch(filename, pattern):
|
||||
return True
|
||||
# Check if any parent directory matches
|
||||
for part in rel_path.parts:
|
||||
if fnmatch(part, pattern):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_file_set(directory: Path, ignore_files: List[str], ignore_patterns: List[str]) -> Set[str]:
|
||||
"""Get set of all file paths in directory (relative)"""
|
||||
files = set()
|
||||
|
||||
for item in directory.rglob("*"):
|
||||
if item.is_file() and not should_exclude(item, directory, ignore_files, ignore_patterns):
|
||||
rel_path = str(item.relative_to(directory))
|
||||
files.add(rel_path)
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def verify_branch(branch_dir: Path) -> Tuple[List[str], List[str], List[str]]:
|
||||
"""
|
||||
Compare template with branch
|
||||
|
||||
Returns:
|
||||
Tuple of (in_template_only, in_branch_only, in_both)
|
||||
"""
|
||||
# Load ignore patterns from .registry_ignore.json
|
||||
ignore_files, ignore_patterns = load_ignore_patterns()
|
||||
|
||||
template_files = get_file_set(TEMPLATE_DIR, ignore_files, ignore_patterns)
|
||||
branch_files = get_file_set(branch_dir, ignore_files, ignore_patterns)
|
||||
|
||||
in_template_only = sorted(template_files - branch_files)
|
||||
in_branch_only = sorted(branch_files - template_files)
|
||||
in_both = sorted(template_files & branch_files)
|
||||
|
||||
return in_template_only, in_branch_only, in_both
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 verify_branch.py <branch_directory>")
|
||||
print("Example: python3 verify_branch.py test_branch002")
|
||||
sys.exit(1)
|
||||
|
||||
branch_path = Path(sys.argv[1])
|
||||
if not branch_path.is_absolute():
|
||||
# Assume it's relative to cortex directory
|
||||
branch_path = Path.cwd() / branch_path
|
||||
|
||||
if not branch_path.exists():
|
||||
print(f"❌ ERROR: Branch directory not found: {branch_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n=== Branch Verification ===")
|
||||
print(f"Template: {TEMPLATE_DIR}")
|
||||
print(f"Branch: {branch_path}")
|
||||
print()
|
||||
|
||||
in_template_only, in_branch_only, in_both = verify_branch(branch_path)
|
||||
|
||||
print(f"Files in both: {len(in_both)}")
|
||||
print(f"Files in template only: {len(in_template_only)}")
|
||||
print(f"Files in branch only: {len(in_branch_only)}")
|
||||
print()
|
||||
|
||||
if in_template_only:
|
||||
print("❌ Missing from branch (expected from template):")
|
||||
for file in in_template_only:
|
||||
print(f" - {file}")
|
||||
print()
|
||||
|
||||
if in_branch_only:
|
||||
print("✅ Additional files in branch (renamed or new):")
|
||||
for file in in_branch_only:
|
||||
print(f" + {file}")
|
||||
print()
|
||||
|
||||
if not in_template_only and not in_branch_only:
|
||||
print("✅ Branch matches template perfectly")
|
||||
|
||||
print("="*70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user