358 lines
11 KiB
Bash
Executable File
358 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# MD Hub Secure - Progress Monitor
|
|
# Watches the project directory and updates docs/STATUS.md with progress
|
|
# Exits if no filesystem changes detected between iterations
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="/Users/tim/developer/md-hub-secure"
|
|
DOCS_DIR="$PROJECT_DIR/docs"
|
|
STATUS_FILE="$DOCS_DIR/STATUS.md"
|
|
HASH_FILE="$DOCS_DIR/.last_state_hash"
|
|
ITERATION=0
|
|
MAX_ITERATIONS=1000 # Safety limit
|
|
START_TIME=$(date +%s)
|
|
|
|
# Colors for terminal output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log() {
|
|
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" >&2
|
|
}
|
|
|
|
warn() {
|
|
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" >&2
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" >&2
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" >&2
|
|
}
|
|
|
|
# Compute hash of all tracked files (excluding node_modules, .git, etc.)
|
|
compute_state_hash() {
|
|
find "$PROJECT_DIR" \
|
|
-type f \
|
|
-not -path '*/node_modules/*' \
|
|
-not -path '*/.git/*' \
|
|
-not -path '*/data/*' \
|
|
-not -path '*/vendor/*' \
|
|
-not -name '.last_state_hash' \
|
|
-not -name 'STATUS.md' \
|
|
-not -name '*.lock' \
|
|
| sort \
|
|
| xargs stat -f '%m %N' 2>/dev/null \
|
|
| md5 -q
|
|
}
|
|
|
|
# Check if filesystem changed
|
|
has_changes() {
|
|
local current_hash
|
|
current_hash=$(compute_state_hash)
|
|
|
|
if [[ -f "$HASH_FILE" ]]; then
|
|
local last_hash
|
|
last_hash=$(cat "$HASH_FILE")
|
|
if [[ "$current_hash" == "$last_hash" ]]; then
|
|
return 1 # No changes
|
|
fi
|
|
fi
|
|
|
|
echo "$current_hash" > "$HASH_FILE"
|
|
return 0 # Changes detected (or first run)
|
|
}
|
|
|
|
# Count lines of code by language
|
|
analyze_codebase() {
|
|
log "Analyzing codebase..."
|
|
|
|
local go_files=0
|
|
local go_lines=0
|
|
local ts_files=0
|
|
local ts_lines=0
|
|
local md_files=0
|
|
local md_lines=0
|
|
local total_files=0
|
|
local docker_files=0
|
|
|
|
# Count Go files
|
|
if command -v find &> /dev/null; then
|
|
go_files=$(find "$PROJECT_DIR" -name '*.go' -not -path '*/vendor/*' -not -path '*/node_modules/*' | wc -l)
|
|
go_lines=$(find "$PROJECT_DIR" -name '*.go' -not -path '*/vendor/*' -not -path '*/node_modules/*' -exec cat {} + 2>/dev/null | wc -l)
|
|
|
|
ts_files=$(find "$PROJECT_DIR" -name '*.ts' -o -name '*.tsx' | grep -v node_modules | wc -l)
|
|
ts_lines=$(find "$PROJECT_DIR" -name '*.ts' -o -name '*.tsx' | grep -v node_modules | xargs cat 2>/dev/null | wc -l)
|
|
|
|
md_files=$(find "$PROJECT_DIR" -name '*.md' | wc -l)
|
|
md_lines=$(find "$PROJECT_DIR" -name '*.md' -exec cat {} + 2>/dev/null | wc -l)
|
|
|
|
docker_files=$(find "$PROJECT_DIR" -name 'Dockerfile*' -o -name 'docker-compose*' | wc -l)
|
|
|
|
total_files=$(find "$PROJECT_DIR" -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/data/*' | wc -l)
|
|
fi
|
|
|
|
echo "$go_files|$go_lines|$ts_files|$ts_lines|$md_files|$md_lines|$total_files|$docker_files"
|
|
}
|
|
|
|
# Check git status if available
|
|
check_git_status() {
|
|
if [[ -d "$PROJECT_DIR/.git" ]]; then
|
|
cd "$PROJECT_DIR"
|
|
local branch=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
local uncommitted=$(git status --short 2>/dev/null | wc -l | tr -d ' ')
|
|
local last_commit=$(git log -1 --format="%h - %s (%cr)" 2>/dev/null || echo "No commits yet")
|
|
echo "$branch|$uncommitted|$last_commit"
|
|
else
|
|
echo "no-git|0|N/A"
|
|
fi
|
|
}
|
|
|
|
# Check if Docker is configured
|
|
check_docker() {
|
|
if [[ -f "$PROJECT_DIR/docker-compose.yml" ]]; then
|
|
echo "yes"
|
|
else
|
|
echo "no"
|
|
fi
|
|
}
|
|
|
|
# Check milestone progress
|
|
check_milestones() {
|
|
local completed=0
|
|
local in_progress=0
|
|
local total=6
|
|
|
|
if [[ -f "$DOCS_DIR/milestones/milestone-01-foundation.md" ]]; then
|
|
# Check for markers in milestone files
|
|
for i in $(seq 1 6); do
|
|
local file="$DOCS_DIR/milestones/milestone-0${i}-*.md"
|
|
if ls $file 1> /dev/null 2>&1; then
|
|
if grep -q "COMPLETED" $file 2>/dev/null; then
|
|
completed=$((completed + 1))
|
|
elif grep -q "IN_PROGRESS" $file 2>/dev/null; then
|
|
in_progress=$((in_progress + 1))
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
echo "$completed|$in_progress|$total"
|
|
}
|
|
|
|
# Generate code review comments
|
|
generate_code_review() {
|
|
log "Scanning for code review items..."
|
|
|
|
local review=""
|
|
|
|
# Check for TODO/FIXME/XXX in code
|
|
if find "$PROJECT_DIR" -name '*.go' -o -name '*.ts' -o -name '*.tsx' | grep -v node_modules | xargs grep -n 'TODO\|FIXME\|XXX\|HACK' 2>/dev/null | head -20 > /tmp/todos.txt; then
|
|
local todo_count=$(wc -l < /tmp/todos.txt | tr -d ' ')
|
|
if [[ $todo_count -gt 0 ]]; then
|
|
review+="### Action Items Found ($todo_count)\n\n"
|
|
review+="| File | Line | Comment |\n"
|
|
review+="|------|------|---------|\n"
|
|
while IFS=: read -r file line comment; do
|
|
# Extract just the filename
|
|
local fname=$(basename "$file")
|
|
review+="| $fname | $line | $comment |\n"
|
|
done < /tmp/todos.txt
|
|
review+="\n"
|
|
fi
|
|
fi
|
|
|
|
# Check for security issues
|
|
if find "$PROJECT_DIR" -name '*.go' | xargs grep -l 'http.Get\|http.Post\|ioutil.ReadFile' 2>/dev/null | head -5 > /tmp/security.txt; then
|
|
local sec_count=$(wc -l < /tmp/security.txt | tr -d ' ')
|
|
if [[ $sec_count -gt 0 ]]; then
|
|
review+="### Security Review Notes\n\n"
|
|
review+="- Found $sec_count files using potentially unsafe operations\n"
|
|
review+="- Ensure all HTTP requests have timeouts\n"
|
|
review+="- Validate all file paths before reading\n\n"
|
|
fi
|
|
fi
|
|
|
|
# Check for error handling
|
|
if find "$PROJECT_DIR" -name '*.go' | xargs grep -n '_ = ' 2>/dev/null | head -10 > /tmp/errors.txt; then
|
|
local err_count=$(wc -l < /tmp/errors.txt | tr -d ' ')
|
|
if [[ $err_count -gt 0 ]]; then
|
|
review+="### Error Handling ($err_count ignored errors)\n\n"
|
|
review+="Several errors are being silently ignored. Consider handling or logging these.\n\n"
|
|
fi
|
|
fi
|
|
|
|
echo -e "$review"
|
|
}
|
|
|
|
# Generate the status report
|
|
generate_status() {
|
|
local iteration=$1
|
|
local now=$(date '+%Y-%m-%d %H:%M:%S UTC')
|
|
local elapsed=$(( $(date +%s) - START_TIME ))
|
|
local elapsed_str="$((elapsed / 3600))h $(((elapsed % 3600) / 60))m"
|
|
|
|
# Gather metrics
|
|
IFS='|' read -r go_files go_lines ts_files ts_lines md_files md_lines total_files docker_files <<< "$(analyze_codebase)"
|
|
IFS='|' read -r git_branch uncommitted last_commit <<< "$(check_git_status)"
|
|
IFS='|' read -r completed in_progress total_milestones <<< "$(check_milestones)"
|
|
local has_docker=$(check_docker)
|
|
|
|
# Generate code review
|
|
local review=$(generate_code_review)
|
|
|
|
# Calculate progress percentage
|
|
local progress=$((completed * 100 / total_milestones))
|
|
|
|
cat > "$STATUS_FILE" << EOF
|
|
# Project Status Report
|
|
|
|
**Generated:** $now
|
|
**Monitoring Duration:** $elapsed_str
|
|
**Iteration:** $iteration
|
|
**Branch:** $git_branch
|
|
|
|
## Quick Stats
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| Total Files | $total_files |
|
|
| Go Files | $go_files (${go_lines} lines) |
|
|
| TypeScript Files | $ts_files (${ts_lines} lines) |
|
|
| Markdown Files | $md_files (${md_lines} lines) |
|
|
| Docker Config | $has_docker |
|
|
| Uncommitted Changes | $uncommitted |
|
|
| Milestones Complete | $completed/$total_milestones (${progress}%) |
|
|
|
|
## Recent Activity
|
|
|
|
**Last Commit:** $last_commit
|
|
|
|
## Milestone Progress
|
|
|
|
- [ ] Milestone 1: Foundation
|
|
- [ ] Milestone 2: Sync Protocol
|
|
- [ ] Milestone 3: Authentication
|
|
- [ ] Milestone 4: Collaboration
|
|
- [ ] Milestone 5: Search & UI
|
|
- [ ] Milestone 6: Production
|
|
|
|
**Current Focus:** $([[ $in_progress -gt 0 ]] && echo "In Progress" || echo "Not Started")
|
|
|
|
$review
|
|
|
|
## Notes for Human Review
|
|
|
|
EOF
|
|
|
|
# Add contextual notes based on what we see
|
|
if [[ $go_files -eq 0 ]]; then
|
|
echo ">> **No Go files found.** Codex may be working on other aspects or hasn't started yet." >> "$STATUS_FILE"
|
|
elif [[ $go_files -lt 5 ]]; then
|
|
echo ">> **Early stage.** Only $go_files Go files exist. Foundation work likely in progress." >> "$STATUS_FILE"
|
|
else
|
|
echo ">> **Active development.** $go_files Go files with ${go_lines} lines of code." >> "$STATUS_FILE"
|
|
fi
|
|
|
|
if [[ $uncommitted -gt 0 ]]; then
|
|
echo ">> **$uncommitted uncommitted changes** detected. Codex may be mid-work." >> "$STATUS_FILE"
|
|
fi
|
|
|
|
if [[ -n "$review" ]]; then
|
|
echo ">> **Code review items found.** See Action Items above." >> "$STATUS_FILE"
|
|
fi
|
|
|
|
cat >> "$STATUS_FILE" << EOF
|
|
|
|
## Files Changed This Session
|
|
|
|
EOF
|
|
|
|
# List recently modified files
|
|
if [[ -f "$HASH_FILE" ]]; then
|
|
find "$PROJECT_DIR" \
|
|
-type f \
|
|
-mtime -0.007 \
|
|
-not -path '*/node_modules/*' \
|
|
-not -path '*/.git/*' \
|
|
-not -path '*/data/*' \
|
|
-not -name '.last_state_hash' \
|
|
-not -name 'STATUS.md' \
|
|
| sort | head -20 \
|
|
| sed 's|^| - |' >> "$STATUS_FILE" || true
|
|
fi
|
|
|
|
cat >> "$STATUS_FILE" << EOF
|
|
|
|
---
|
|
|
|
*This report auto-generated by MD Hub Secure progress monitor.*
|
|
*Next update in ~10 minutes or when filesystem changes detected.*
|
|
EOF
|
|
|
|
success "Status report updated: $STATUS_FILE"
|
|
}
|
|
|
|
# Main monitoring loop
|
|
main() {
|
|
log "Starting MD Hub Secure progress monitor..."
|
|
log "Project: $PROJECT_DIR"
|
|
log "Watching for filesystem changes every 10 minutes"
|
|
log "Press Ctrl+C to stop, or wait for auto-exit on inactivity"
|
|
|
|
# Ensure docs directory exists
|
|
mkdir -p "$DOCS_DIR"
|
|
|
|
while true; do
|
|
ITERATION=$((ITERATION + 1))
|
|
|
|
if [[ $ITERATION -gt $MAX_ITERATIONS ]]; then
|
|
warn "Maximum iterations ($MAX_ITERATIONS) reached. Stopping."
|
|
exit 0
|
|
fi
|
|
|
|
log "Iteration $ITERATION - checking for changes..."
|
|
|
|
if [[ $ITERATION -gt 1 ]] && ! has_changes; then
|
|
warn "No filesystem changes detected since last check."
|
|
warn "Stopping monitor (Codex may have finished or is inactive)."
|
|
|
|
# Add final note to status
|
|
cat >> "$STATUS_FILE" << EOF
|
|
|
|
## Monitor Stopped
|
|
|
|
**Stopped:** $(date '+%Y-%m-%d %H:%M:%S UTC')
|
|
**Reason:** No filesystem activity detected
|
|
|
|
No changes were observed between this check and the previous one.
|
|
This could mean:
|
|
- Codex has completed work
|
|
- Codex is paused or idle
|
|
- No files have been modified recently
|
|
|
|
To restart monitoring, run: `./scripts/monitor.sh`
|
|
EOF
|
|
exit 0
|
|
fi
|
|
|
|
generate_status $ITERATION
|
|
|
|
success "Iteration $ITERATION complete. Sleeping for 10 minutes..."
|
|
sleep 600 # 10 minutes
|
|
done
|
|
}
|
|
|
|
# Handle signals
|
|
trap 'error "Monitor interrupted"; exit 1' INT TERM
|
|
|
|
# Run main loop
|
|
main
|