Add automation scripts and sync memory files

Scripts added:
- sync-memory.sh: Syncs Claude project memories to version control
- cleanup-plans.sh: Archives old Claude plan files
- scripts/README.md: Documentation for automation scripts

Features:
- Auto-sync mode for cron jobs
- Dry-run mode for testing
- Plan archival with timestamp preservation
- Support for multiple hiveops projects

Memory files synced:
- dlx-ansible (141 lines)
- hiveops-mgmt (45 lines)
- hiveops-incident (15 lines)
- hiveops-browser (25 lines)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
directlx 2026-02-09 13:49:23 -05:00
parent 1d9896e6a1
commit 9f27515721
6 changed files with 546 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# HiveOps Browser Project Memory
## Architecture
- **Browser**: Electron app at `/source/hiveops-src/hiveops-browser`
- **Mgmt Server**: Spring Boot at `/source/hiveops-src/hiveops-mgmt`
- IPC pattern: kebab-case channels (`get-config`), camelCase in preload (`getConfig`)
- API client returns `{ success, data }` or `{ success, error, status, code }`
- DTOs use Lombok `@Data @Builder @NoArgsConstructor @AllArgsConstructor`
- Services use `@RequiredArgsConstructor @Slf4j`, `@Transactional(readOnly=true)` for reads
- Controllers use `@RestController @RequestMapping("/api/v1/...")` with OpenAPI annotations
- Security: 3 filter chains (API@Order1, Portal@Order2, Default@Order3)
- Flyway migrations in `db/migration/`, H2 dev data in `db/h2/data.sql` (MERGE INTO syntax)
- H2 uses `CHAR(10)` for newlines (no `E'...\n...'` like PostgreSQL)
## Key Files
- `src/main/main.js` - Main process, IPC handlers, window management (~1400 lines)
- `src/main/api-client.js` - Axios-based API client
- `src/main/preload.js` - contextBridge IPC exposure
- Window pattern: check if exists, focus; else create BrowserWindow with preload
## Legal Content (Added Feb 2026)
- Legal API: `GET /api/v1/legal` and `GET /api/v1/legal/{section}` (public, no auth)
- 4 settings in `global_settings`: `legal.copyright`, `legal.license`, `legal.usagePolicy`, `legal.disclaimers`
- About page has tabbed layout: Info, License, Usage Policy, Disclaimers
- About window size: 650x700

View File

@ -0,0 +1,15 @@
# HiveOps Incident - Key Learnings
## Project Structure
- Frontend: SvelteKit at `frontend/`, backend: Spring Boot at `backend/`
- CSS is split between standalone `.css` files (Dashboard, IncidentList, AtmHistory, JournalEvents, CreateIncident) and scoped `<style>` blocks in `.svelte` files (AtmProperties, IncidentWorkflow, MultiSelectDropdown, AtmInfoCard, AtmSelector)
- Global styles: `app.css` (`:root` vars) and `App.svelte` (global selectors with `:global()`)
## Typography System (implemented)
- All font sizes use CSS custom properties defined in `app.css :root`
- Variables: `--font-size-page-title` (1.4rem), `--font-size-section-title` (1.1rem), `--font-size-card-title` (0.95rem), `--font-size-body` (0.95rem), `--font-size-body-sm` (0.9rem), `--font-size-label` (0.85rem), `--font-size-caption` (0.8rem), `--font-size-tiny` (0.75rem), `--font-size-stat-value` (2rem), `--font-size-icon` (1.1rem), `--font-size-icon-sm` (0.9rem), `--font-size-subtitle` (0.85rem)
- `font-family` removed from `app.css` `:root`, kept in `App.svelte` global
## Build
- `cd frontend && npm run build` - quick (~2s), pre-existing unused CSS selector warnings in App.svelte dark mode styles are normal
- Git hosting: Gitea (not GitHub/GitLab)

View File

@ -0,0 +1,45 @@
# HiveOps Management Portal - Memory
## Admin Password Reset Feature
Successfully implemented admin password reset functionality for portal users.
### Key Implementation Details
**Backend Components:**
- `AuditLog.java`: Added USER_PASSWORD_RESET, USER_ENABLED, USER_DISABLED enum values
- `AuditService.java`: Added logPasswordReset(), logUserEnabled(), logUserDisabled() methods
- `UserRepository.java`: Added search() method with JPQL query for email/name search
- `ResetPasswordRequest.java`: DTO with password validation (min 8, max 100 chars)
- `UserService.java`: Enhanced with findAll(), searchUsers(), resetPassword(), enableUser(), disableUser()
- `PortalUserController.java`: New controller at /portal/users with ADMIN role authorization
- `SecurityConfig.java`: Added authorization rule for /portal/users/** requiring ADMIN role
**Frontend Components:**
- `list.html`: User list with search, role filter, and pagination
- `view.html`: User details with action buttons (reset password, enable/disable)
- `reset-password.html`: Password reset form with validation
- `base.html`: Added Users menu item in sidebar (ADMIN only)
### Security Patterns
- BCrypt password encoding (strength 12)
- @PreAuthorize("hasRole('ADMIN')") on controller
- Prevents admin from disabling own account
- All actions logged to audit_logs table with admin email, target user email, and IP address
### Common Patterns in Portal
- Flash messages via RedirectAttributes (success/error)
- getClientIp() helper extracts IP from X-Forwarded-For or remote address
- Pagination with PageRequest.of(page, size, Sort)
- Bootstrap 5 styling with badges for status/role
- Thymeleaf validation with .invalid-feedback for errors
### Testing Checklist
1. Login as admin@directlx.dev / admin123
2. Navigate to Users menu (visible to ADMIN only)
3. Search users by email/name
4. View user details
5. Reset password (validate min 8 chars, matching confirmation)
6. Enable/disable user (cannot disable self)
7. Verify audit log entries created
8. Test login with new password

123
scripts/README.md Normal file
View File

@ -0,0 +1,123 @@
# Scripts Directory
Automation scripts for managing Claude configurations and memory.
## sync-memory.sh
Synchronizes Claude's in-memory project knowledge to this version-controlled repository.
### Usage
```bash
# Interactive sync with commit prompt
./scripts/sync-memory.sh
# Auto-commit with timestamp
./scripts/sync-memory.sh --auto
# Preview changes without modifying files
./scripts/sync-memory.sh --dry-run
```
### What It Syncs
- `~/.claude/projects/-source-dlx-src-dlx-ansible/memory/MEMORY.md``memory/dlx-ansible/MEMORY.md`
- `~/.claude/projects/-source-hiveops-src-hiveops-mgmt/memory/MEMORY.md``memory/hiveops-mgmt/MEMORY.md`
- `~/.claude/projects/-source-hiveops-src-hiveops-incident/memory/MEMORY.md``memory/hiveops-incident/MEMORY.md`
- `~/.claude/projects/-source-hiveops-src-hiveops-browser/memory/MEMORY.md``memory/hiveops-browser/MEMORY.md`
### Automatic Syncing
#### Option 1: Cron Job (Scheduled)
Run sync every day at 2 AM:
```bash
# Add to crontab
crontab -e
# Add this line:
0 2 * * * cd /source/dlx-src/dlx-claude && ./scripts/sync-memory.sh --auto && git push origin main 2>&1 | logger -t claude-sync
```
#### Option 2: Git Hook (On Commit)
Create a post-commit hook in projects to trigger sync:
```bash
# In ~/.claude/hooks/ (if Claude supports this)
# Or manually run after significant work sessions
```
#### Option 3: Manual (Recommended)
Run manually after completing significant work:
```bash
cd /source/dlx-src/dlx-claude
./scripts/sync-memory.sh
git push origin main
```
### Workflow
1. Work with Claude Code on infrastructure projects
2. Claude updates memory files in `~/.claude/projects/`
3. Run sync script to version control knowledge
4. Push to Gitea for team sharing and backup
### Best Practices
- Run sync after completing major tasks
- Review changes before committing (use interactive mode)
- Use `--dry-run` to preview changes first
- Keep memory files under 200 lines (Claude's limit)
- Document critical learnings immediately
## cleanup-plans.sh
Manages Claude's plan files by archiving old plans to keep the directory clean.
### Usage
```bash
# List all plan files with ages
./scripts/cleanup-plans.sh
# Archive plans older than 30 days
./scripts/cleanup-plans.sh --archive
# Archive all plans
./scripts/cleanup-plans.sh --archive-all
# Change age threshold (e.g., 60 days)
./scripts/cleanup-plans.sh --archive --days 60
```
### What It Does
Claude Code creates plan files in `~/.claude/plans/` for each planning session. Over time, these accumulate. This script:
1. Lists all plan files with their ages
2. Identifies plans older than a threshold (default: 30 days)
3. Archives old plans to `~/.claude/plans-archive/YYYY-MM-DD/`
4. Preserves recent plans in the active directory
### When to Use
- Monthly cleanup: Archive plans older than 30 days
- Project cleanup: Archive all plans when starting fresh
- Regular maintenance: Keep the plans directory manageable
### Archive Location
Archived plans are moved to: `~/.claude/plans-archive/YYYY-MM-DD/`
This preserves plans by archive date, allowing you to:
- Reference old planning sessions if needed
- Restore plans if accidentally archived
- Delete archive folders when truly no longer needed
---
**Created**: 2026-02-09

156
scripts/cleanup-plans.sh Executable file
View File

@ -0,0 +1,156 @@
#!/bin/bash
# cleanup-plans.sh - Archive old Claude plan files
#
# Claude Code creates plan files in ~/.claude/plans/ for each planning session.
# This script helps manage these files by archiving old plans to keep the directory clean.
#
# Usage:
# ./scripts/cleanup-plans.sh # List plans and show what would be archived
# ./scripts/cleanup-plans.sh --archive # Archive plans older than 30 days
# ./scripts/cleanup-plans.sh --archive-all # Archive all plans
# ./scripts/cleanup-plans.sh --days 60 # Change age threshold (default: 30)
set -e
PLANS_DIR="$HOME/.claude/plans"
ARCHIVE_DIR="$HOME/.claude/plans-archive"
DAYS_OLD=30
ACTION="list"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--archive)
ACTION="archive"
shift
;;
--archive-all)
ACTION="archive_all"
shift
;;
--days)
DAYS_OLD="$2"
shift 2
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--archive] [--archive-all] [--days N]"
exit 1
;;
esac
done
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Claude Plan Files Cleanup ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
echo
# Check if plans directory exists
if [ ! -d "$PLANS_DIR" ]; then
echo -e "${YELLOW}No plans directory found at: $PLANS_DIR${NC}"
exit 0
fi
# Count files
TOTAL_COUNT=$(find "$PLANS_DIR" -type f -name "*.md" | wc -l)
echo -e "Total plan files: ${GREEN}$TOTAL_COUNT${NC}"
if [ $TOTAL_COUNT -eq 0 ]; then
echo -e "${GREEN}No plan files to clean up.${NC}"
exit 0
fi
# Find old files
if [ "$ACTION" = "archive_all" ]; then
OLD_FILES=$(find "$PLANS_DIR" -type f -name "*.md")
OLD_COUNT=$TOTAL_COUNT
else
OLD_FILES=$(find "$PLANS_DIR" -type f -name "*.md" -mtime +$DAYS_OLD)
if [ -z "$OLD_FILES" ]; then
OLD_COUNT=0
else
OLD_COUNT=$(echo "$OLD_FILES" | wc -l)
fi
fi
RECENT_COUNT=$((TOTAL_COUNT - OLD_COUNT))
echo -e "Plans older than $DAYS_OLD days: ${YELLOW}$OLD_COUNT${NC}"
echo -e "Recent plans: ${GREEN}$RECENT_COUNT${NC}"
echo
# List mode
if [ "$ACTION" = "list" ]; then
echo -e "${BLUE}Plan Files by Age:${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
# List all files sorted by modification time
find "$PLANS_DIR" -type f -name "*.md" -printf "%T+ %p\n" | sort -r | while read -r line; do
timestamp=$(echo "$line" | cut -d' ' -f1 | cut -d'+' -f1)
filepath=$(echo "$line" | cut -d' ' -f2-)
filename=$(basename "$filepath")
# Calculate age in days
file_time=$(stat -c %Y "$filepath")
current_time=$(date +%s)
age_days=$(( (current_time - file_time) / 86400 ))
if [ $age_days -gt $DAYS_OLD ]; then
echo -e "${YELLOW}📄${NC} $timestamp $filename (${age_days}d old)"
else
echo -e "${GREEN}📄${NC} $timestamp $filename (${age_days}d old)"
fi
done
echo
echo -e "${YELLOW}To archive old plans, run:${NC}"
echo -e " ./scripts/cleanup-plans.sh --archive"
echo
echo -e "${YELLOW}To archive all plans, run:${NC}"
echo -e " ./scripts/cleanup-plans.sh --archive-all"
exit 0
fi
# Archive mode
if [ "$ACTION" = "archive" ] || [ "$ACTION" = "archive_all" ]; then
if [ $OLD_COUNT -eq 0 ]; then
echo -e "${GREEN}No old plan files to archive.${NC}"
exit 0
fi
# Create archive directory with timestamp
TIMESTAMP=$(date '+%Y-%m-%d')
ARCHIVE_PATH="$ARCHIVE_DIR/$TIMESTAMP"
mkdir -p "$ARCHIVE_PATH"
echo -e "${BLUE}Archiving $OLD_COUNT plan files to:${NC}"
echo -e " $ARCHIVE_PATH"
echo
# Move files
ARCHIVED=0
echo "$OLD_FILES" | while read -r filepath; do
if [ -f "$filepath" ]; then
filename=$(basename "$filepath")
mv "$filepath" "$ARCHIVE_PATH/"
echo -e "${GREEN}${NC} Archived: $filename"
ARCHIVED=$((ARCHIVED + 1))
fi
done
echo
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "Archived: $OLD_COUNT files"
echo -e "Archive location: $ARCHIVE_PATH"
echo -e "Remaining: $RECENT_COUNT files"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo
echo -e "${GREEN}Cleanup complete!${NC}"
fi

182
scripts/sync-memory.sh Executable file
View File

@ -0,0 +1,182 @@
#!/bin/bash
# sync-memory.sh - Sync Claude memory files to dlx-claude repository
#
# This script copies Claude's in-memory project knowledge to the version-controlled
# dlx-claude repository for backup and team sharing.
#
# Usage:
# ./scripts/sync-memory.sh # Interactive sync with git commit
# ./scripts/sync-memory.sh --auto # Auto-commit with timestamp
# ./scripts/sync-memory.sh --dry-run # Preview changes only
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
CLAUDE_HOME="$HOME/.claude"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Parse arguments
DRY_RUN=false
AUTO_COMMIT=false
for arg in "$@"; do
case $arg in
--dry-run)
DRY_RUN=true
shift
;;
--auto)
AUTO_COMMIT=true
shift
;;
*)
echo "Unknown option: $arg"
echo "Usage: $0 [--dry-run] [--auto]"
exit 1
;;
esac
done
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Claude Memory Sync to dlx-claude Repository ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
echo
# Define sync mappings: source -> destination
declare -A SYNC_MAP=(
["$CLAUDE_HOME/projects/-source-dlx-src-dlx-ansible/memory/MEMORY.md"]="$REPO_ROOT/memory/dlx-ansible/MEMORY.md"
["$CLAUDE_HOME/projects/-source-hiveops-src-hiveops-mgmt/memory/MEMORY.md"]="$REPO_ROOT/memory/hiveops-mgmt/MEMORY.md"
["$CLAUDE_HOME/projects/-source-hiveops-src-hiveops-incident/memory/MEMORY.md"]="$REPO_ROOT/memory/hiveops-incident/MEMORY.md"
["$CLAUDE_HOME/projects/-source-hiveops-src-hiveops-browser/memory/MEMORY.md"]="$REPO_ROOT/memory/hiveops-browser/MEMORY.md"
)
# Check if running in dry-run mode
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}[DRY RUN MODE]${NC} No files will be modified."
echo
fi
# Sync files
SYNCED_COUNT=0
SKIPPED_COUNT=0
CHANGED_FILES=()
for source in "${!SYNC_MAP[@]}"; do
dest="${SYNC_MAP[$source]}"
if [ ! -f "$source" ]; then
echo -e "${YELLOW}${NC} Skipped (source not found): $(basename "$source")"
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
continue
fi
# Create destination directory if needed
dest_dir="$(dirname "$dest")"
if [ "$DRY_RUN" = false ]; then
mkdir -p "$dest_dir"
fi
# Check if files differ
if [ -f "$dest" ] && cmp -s "$source" "$dest"; then
echo -e "${GREEN}${NC} No changes: $(basename "$source")"
else
if [ "$DRY_RUN" = false ]; then
cp "$source" "$dest"
echo -e "${GREEN}${NC} Synced: $(basename "$source")"
CHANGED_FILES+=("$dest")
else
echo -e "${BLUE}~${NC} Would sync: $(basename "$source")"
fi
SYNCED_COUNT=$((SYNCED_COUNT + 1))
fi
done
echo
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "Files synced: $SYNCED_COUNT"
echo -e "Files skipped: $SKIPPED_COUNT"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo
# Exit if dry-run or no changes
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Dry run complete. No files were modified.${NC}"
exit 0
fi
if [ $SYNCED_COUNT -eq 0 ]; then
echo -e "${GREEN}All memory files are up to date. Nothing to commit.${NC}"
exit 0
fi
# Git operations
cd "$REPO_ROOT"
# Check if we're in a git repository
if [ ! -d ".git" ]; then
echo -e "${RED}Error: Not a git repository: $REPO_ROOT${NC}"
exit 1
fi
# Stage changed files
echo -e "${BLUE}Staging changed files...${NC}"
for file in "${CHANGED_FILES[@]}"; do
git add "$file"
echo " + $(git diff --cached --stat "$file" | head -1)"
done
# Show git status
echo
echo -e "${BLUE}Git status:${NC}"
git status --short
# Commit changes
if [ "$AUTO_COMMIT" = true ]; then
# Auto-commit with timestamp
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
git commit -m "Auto-sync Claude memory files - $TIMESTAMP
Updated memory files:
$(for file in "${CHANGED_FILES[@]}"; do echo " - $(basename "$file")"; done)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
echo
echo -e "${GREEN}✓ Changes committed automatically${NC}"
echo
echo -e "${YELLOW}Push to remote:${NC}"
echo " git push origin main"
else
# Interactive commit
echo
echo -e "${YELLOW}Ready to commit changes.${NC}"
echo
read -p "Commit message (or Enter for default): " COMMIT_MSG
if [ -z "$COMMIT_MSG" ]; then
COMMIT_MSG="Sync Claude memory files
Updated memory files:
$(for file in "${CHANGED_FILES[@]}"; do echo " - $(basename "$file")"; done)"
fi
git commit -m "$COMMIT_MSG
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
echo
echo -e "${GREEN}✓ Changes committed${NC}"
echo
echo -e "${YELLOW}Push to remote:${NC}"
echo " git push origin main"
fi
echo
echo -e "${GREEN}Memory sync complete!${NC}"