From 766ff395c083469e60f0ed2332e6f2adeca97032 Mon Sep 17 00:00:00 2001 From: drompincen Date: Sun, 29 Mar 2026 15:31:13 -0600 Subject: [PATCH] Remove executable scripts from git, replace with instruction files Corporate security policies block repos containing binaries/executables. All .sh/.cmd/.ps1 scripts moved to gitignored scripts/local/ with recreatable instruction files in script-instructions/. Added start-here.md onboarding guide referenced from README. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/hooks/edit-log.sh | 15 - .claude/hooks/enrich-check.sh | 26 -- .claude/hooks/memory-sync.sh | 43 --- .claude/hooks/session-end.sh | 18 - .claude/hooks/statusline.sh | 90 ----- .claude/hooks/track-agents.sh | 9 - .claude/hooks/validate-plan.sh | 89 ----- .claude/settings.json | 22 +- .gitignore | 3 + README.md | 2 + run-cli.cmd | 21 - run-cli.sh | 8 - run-client.sh | 18 - run-mcp.cmd | 8 - run-mcp.sh | 5 - run-server.cmd | 18 - run-server.sh | 21 - script-instructions/hook-scripts.md | 505 +++++++++++++++++++++++++ script-instructions/run-scripts.md | 173 +++++++++ script-instructions/utility-scripts.md | 314 +++++++++++++++ scripts/enrich.sh | 80 ---- scripts/index-sessions.sh | 22 -- scripts/orchestrate.sh | 116 ------ start-here.md | 74 ++++ 24 files changed, 1082 insertions(+), 618 deletions(-) delete mode 100644 .claude/hooks/edit-log.sh delete mode 100644 .claude/hooks/enrich-check.sh delete mode 100644 .claude/hooks/memory-sync.sh delete mode 100644 .claude/hooks/session-end.sh delete mode 100644 .claude/hooks/statusline.sh delete mode 100644 .claude/hooks/track-agents.sh delete mode 100644 .claude/hooks/validate-plan.sh delete mode 100644 run-cli.cmd delete mode 100644 run-cli.sh delete mode 100644 run-client.sh delete mode 100644 run-mcp.cmd delete mode 100644 run-mcp.sh delete mode 100644 run-server.cmd delete mode 100644 run-server.sh create mode 100644 script-instructions/hook-scripts.md create mode 100644 script-instructions/run-scripts.md create mode 100644 script-instructions/utility-scripts.md delete mode 100644 scripts/enrich.sh delete mode 100644 scripts/index-sessions.sh delete mode 100644 scripts/orchestrate.sh create mode 100644 start-here.md diff --git a/.claude/hooks/edit-log.sh b/.claude/hooks/edit-log.sh deleted file mode 100644 index 69af431..0000000 --- a/.claude/hooks/edit-log.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# drom-flow edit logger — appends edit events to JSONL - -DIR="${CLAUDE_PROJECT_DIR:-.}" -LOG="$DIR/.claude/edit-log.jsonl" - -# Extract file_path from tool input (passed via stdin) -file_path="unknown" -if [ -n "$CLAUDE_TOOL_USE_INPUT" ]; then - fp=$(echo "$CLAUDE_TOOL_USE_INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4) - [ -n "$fp" ] && file_path="$fp" -fi - -timestamp=$(date +%s) -echo "{\"type\":\"edit\",\"file\":\"$file_path\",\"timestamp\":$timestamp}" >> "$LOG" diff --git a/.claude/hooks/enrich-check.sh b/.claude/hooks/enrich-check.sh deleted file mode 100644 index acbd79f..0000000 --- a/.claude/hooks/enrich-check.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Hook: Check if JavaDucker has artifacts pending enrichment -# Runs at session start and after index operations -# Outputs a reminder to the session if artifacts need enrichment - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" -HOST="${JAVADUCKER_HOST:-localhost}" -PORT="${JAVADUCKER_PORT:-8080}" -BASE_URL="http://${HOST}:${PORT}/api" - -# Check if server is running -if ! curl -sf "${BASE_URL}/health" > /dev/null 2>&1; then - exit 0 -fi - -# Check enrichment queue -QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=1" 2>/dev/null || echo '{"count":0}') -COUNT=$(echo "$QUEUE" | jq -r '.count' 2>/dev/null || echo "0") - -if [ "$COUNT" != "0" ] && [ -n "$COUNT" ]; then - # Get total pending count - FULL_QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=100" 2>/dev/null || echo '{"count":0}') - TOTAL=$(echo "$FULL_QUEUE" | jq -r '.count' 2>/dev/null || echo "0") - echo "[Content Intelligence] ${TOTAL} artifact(s) pending enrichment. Use javaducker_enrich_queue to see them, then classify/tag/extract points using the write MCP tools." -fi diff --git a/.claude/hooks/memory-sync.sh b/.claude/hooks/memory-sync.sh deleted file mode 100644 index c317e70..0000000 --- a/.claude/hooks/memory-sync.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -# drom-flow memory sync — inject session memory and check for in-progress plans on start - -DIR="${CLAUDE_PROJECT_DIR:-.}" -MEMORY="$DIR/context/MEMORY.md" -STATE_DIR="$DIR/.claude/.state" -PLANS_DIR="$DIR/drom-plans" - -# Initialize session state -mkdir -p "$STATE_DIR" -date +%s > "$STATE_DIR/session-start" -echo "0" > "$STATE_DIR/agent-count" -echo "0" > "$STATE_DIR/edit-count" - -# Load session memory -if [ -s "$MEMORY" ]; then - echo "[Session Memory Loaded]" - echo "---" - cat "$MEMORY" - echo "---" -else - echo "[No session memory found. Create context/MEMORY.md to persist context across sessions.]" -fi - -# Check for in-progress plans -if [ -d "$PLANS_DIR" ]; then - in_progress="" - for plan in "$PLANS_DIR"/*.md; do - [ -f "$plan" ] || continue - if grep -q "^status: in-progress" "$plan" 2>/dev/null; then - title=$(grep "^title:" "$plan" 2>/dev/null | sed 's/^title: *//') - chapter=$(grep "^current_chapter:" "$plan" 2>/dev/null | sed 's/^current_chapter: *//') - basename=$(basename "$plan") - in_progress="${in_progress}\n - ${basename} — \"${title}\" (Chapter ${chapter:-?})" - fi - done - if [ -n "$in_progress" ]; then - echo "" - echo "[In-Progress Plans Found]" - echo -e "The following plans were stopped midway and can be resumed:${in_progress}" - echo "Read the plan file to review progress and resume from the current chapter." - fi -fi diff --git a/.claude/hooks/session-end.sh b/.claude/hooks/session-end.sh deleted file mode 100644 index 7ee35af..0000000 --- a/.claude/hooks/session-end.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# drom-flow session end — remind to persist progress and update plans - -DIR="${CLAUDE_PROJECT_DIR:-.}" -PLANS_DIR="$DIR/drom-plans" - -echo "[Session ending. Update context/MEMORY.md with progress, findings, and next steps.]" - -# Remind about in-progress plans -if [ -d "$PLANS_DIR" ]; then - for plan in "$PLANS_DIR"/*.md; do - [ -f "$plan" ] || continue - if grep -q "^status: in-progress" "$plan" 2>/dev/null; then - title=$(grep "^title:" "$plan" 2>/dev/null | sed 's/^title: *//') - echo "[Plan in progress: \"${title}\" — update chapter status and step checkboxes before ending.]" - fi - done -fi diff --git a/.claude/hooks/statusline.sh b/.claude/hooks/statusline.sh deleted file mode 100644 index 083d4dc..0000000 --- a/.claude/hooks/statusline.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -# drom-flow statusline — git-aware status for Claude Code - -DIR="${CLAUDE_PROJECT_DIR:-.}" -STATE_DIR="$DIR/.claude/.state" - -# --- Version --- -DROMFLOW_VERSION="" -for vfile in "$DIR/VERSION" "$(dirname "${BASH_SOURCE[0]}")/../../../VERSION"; do - if [ -f "$vfile" ]; then - DROMFLOW_VERSION=$(tr -d '[:space:]' < "$vfile") - break - fi -done -DROMFLOW_VERSION="${DROMFLOW_VERSION:-dev}" - -# --- Session elapsed time --- -elapsed="" -if [ -f "$STATE_DIR/session-start" ]; then - start=$(cat "$STATE_DIR/session-start") - now=$(date +%s) - diff=$((now - start)) - mins=$((diff / 60)) - secs=$((diff % 60)) - if [ $mins -ge 60 ]; then - hrs=$((mins / 60)) - mins=$((mins % 60)) - elapsed="${hrs}h${mins}m" - else - elapsed="${mins}m${secs}s" - fi -fi - -# --- Plan progress (computed early so both git and no-git paths can use it) --- -plan_info="" -PLANS_DIR="$DIR/drom-plans" -if [ -d "$PLANS_DIR" ]; then - for plan in "$PLANS_DIR"/*.md; do - [ -f "$plan" ] || continue - # Match plans that are in-progress OR have any in-progress chapter (fallback for bad frontmatter) - if grep -q "^status: in-progress" "$plan" 2>/dev/null || grep -q '^\*\*Status:\*\* in-progress' "$plan" 2>/dev/null; then - cur=$(grep "^current_chapter:" "$plan" 2>/dev/null | sed 's/^current_chapter: *//') - total=$(grep -c "^## Chapter " "$plan" 2>/dev/null) -done_count=$(grep -c '^\*\*Status:\*\* completed' "$plan" 2>/dev/null) - plan_info="plan:ch${cur:-?}/${total:-?}(${done_count:-0}✓)" - break - fi - done -fi - -# --- Git info --- -branch=$(git branch --show-current 2>/dev/null || echo "no-git") -if [ "$branch" = "no-git" ]; then - nogit_status="drom-flow v$DROMFLOW_VERSION • [no-git] • ${elapsed:-0m0s}" - [ -n "$plan_info" ] && nogit_status="$nogit_status • $plan_info" - echo "$nogit_status" - exit 0 -fi - -staged=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ') -unstaged=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ') -untracked=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ') - -ahead=0 -behind=0 -upstream=$(git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null) -if [ $? -eq 0 ]; then - ahead=$(echo "$upstream" | awk '{print $1}') - behind=$(echo "$upstream" | awk '{print $2}') -fi - -# Compact git: +staged/-unstaged/?untracked -git_info="$branch +${staged}/-${unstaged}/?${untracked}" -[ "$ahead" -gt 0 ] || [ "$behind" -gt 0 ] && git_info="$git_info ↑${ahead}↓${behind}" - -# --- Edit count (from edit-log) --- -edits=0 -[ -f "$DIR/.claude/edit-log.jsonl" ] && edits=$(wc -l < "$DIR/.claude/edit-log.jsonl" | tr -d ' ') - -# --- Background agents (tracked via hook) --- -agents=0 -[ -f "$STATE_DIR/agent-count" ] && agents=$(cat "$STATE_DIR/agent-count" | tr -d '[:space:]') - -# --- Memory status --- -mem="off" -[ -s "$DIR/context/MEMORY.md" ] && mem="on" - -status="drom-flow v$DROMFLOW_VERSION • $git_info • ${elapsed:-0m0s} • edits:$edits • agents:$agents • mem:$mem" -[ -n "$plan_info" ] && status="$status • $plan_info" -echo "$status" diff --git a/.claude/hooks/track-agents.sh b/.claude/hooks/track-agents.sh deleted file mode 100644 index c66c10a..0000000 --- a/.claude/hooks/track-agents.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# drom-flow — track background agent count - -STATE_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/.state" -mkdir -p "$STATE_DIR" - -count=0 -[ -f "$STATE_DIR/agent-count" ] && count=$(cat "$STATE_DIR/agent-count" | tr -d '[:space:]') -echo $((count + 1)) > "$STATE_DIR/agent-count" diff --git a/.claude/hooks/validate-plan.sh b/.claude/hooks/validate-plan.sh deleted file mode 100644 index 7f97cb8..0000000 --- a/.claude/hooks/validate-plan.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash -# drom-flow — validate plan files written to drom-plans/ - -DIR="${CLAUDE_PROJECT_DIR:-.}" -PLANS_DIR="$DIR/drom-plans" - -# Extract file_path from tool input -file_path="" -if [ -n "$CLAUDE_TOOL_USE_INPUT" ]; then - fp=$(echo "$CLAUDE_TOOL_USE_INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4) - [ -n "$fp" ] && file_path="$fp" -fi - -# Only validate files in drom-plans/ -case "$file_path" in - */drom-plans/*.md|drom-plans/*.md) ;; - *) exit 0 ;; -esac - -[ ! -f "$file_path" ] && exit 0 - -errors="" - -# Check frontmatter exists -if ! head -1 "$file_path" | grep -q "^---"; then - errors="${errors}\n - Missing YAML frontmatter (must start with ---)" -fi - -# Check required frontmatter fields -for field in title status created updated current_chapter; do - if ! grep -q "^${field}:" "$file_path"; then - errors="${errors}\n - Missing frontmatter field: ${field}" - fi -done - -# Check status value -status=$(grep "^status:" "$file_path" | head -1 | sed 's/^status: *//') -case "$status" in - in-progress|completed|pending|abandoned) ;; - *) errors="${errors}\n - Invalid status: '${status}' (must be: in-progress, completed, pending, or abandoned)" ;; -esac - -# Check for at least one chapter -chapter_count=$(grep -c "^## Chapter " "$file_path" 2>/dev/null | tr -d '[:space:]') -chapter_count=${chapter_count:-0} -if [ "$chapter_count" -eq 0 ]; then - errors="${errors}\n - No chapters found (need at least one '## Chapter N: Title')" -fi - -# Check chapters have Status lines -chapters_without_status=0 -while IFS= read -r line; do - chapter_num=$(echo "$line" | grep -o "Chapter [0-9]*" | grep -o "[0-9]*") - if ! grep -A2 "^## Chapter ${chapter_num}:" "$file_path" | grep -q '^\*\*Status:\*\*'; then - chapters_without_status=$((chapters_without_status + 1)) - errors="${errors}\n - Chapter ${chapter_num} missing **Status:** line" - fi -done < <(grep "^## Chapter " "$file_path") - -# Check chapters have at least one step (checkbox) -while IFS= read -r line; do - chapter_num=$(echo "$line" | grep -o "Chapter [0-9]*" | grep -o "[0-9]*") - # Get content between this chapter and the next (or end of file) - next_section=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^## /{print NR; exit}" "$file_path") - if [ -n "$next_section" ]; then - step_count=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^## /{exit} found && /^- \[/" "$file_path" | wc -l) - else - step_count=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^- \[/" "$file_path" | wc -l) - fi - if [ "$step_count" -eq 0 ]; then - errors="${errors}\n - Chapter ${chapter_num} has no steps (need at least one '- [ ] ...')" - fi -done < <(grep "^## Chapter " "$file_path") - -# Check current_chapter points to a valid chapter -current=$(grep "^current_chapter:" "$file_path" | head -1 | sed 's/^current_chapter: *//') -if [ -n "$current" ] && [ "$chapter_count" -gt 0 ]; then - if ! grep -q "^## Chapter ${current}:" "$file_path"; then - errors="${errors}\n - current_chapter: ${current} does not match any chapter heading" - fi -fi - -if [ -n "$errors" ]; then - echo "PLAN VALIDATION FAILED: $(basename "$file_path")" - echo -e "Issues:${errors}" - echo "" - echo "Expected format: see /planner skill or drom-plans/ docs in CLAUDE.md" - exit 1 -fi diff --git a/.claude/settings.json b/.claude/settings.json index c8fd4b7..95e6561 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -6,12 +6,17 @@ "hooks": [ { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/edit-log.sh\"", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/edit-log.sh\"", "timeout": 3000 }, { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/validate-plan.sh\"", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/validate-plan.sh\"", + "timeout": 3000 + }, + { + "type": "command", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/javaducker-index.sh\"", "timeout": 3000 } ] @@ -21,7 +26,7 @@ "hooks": [ { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/track-agents.sh\"", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/track-agents.sh\"", "timeout": 3000 } ] @@ -32,13 +37,8 @@ "hooks": [ { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/memory-sync.sh\"", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/memory-sync.sh\"", "timeout": 5000 - }, - { - "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/enrich-check.sh\"", - "timeout": 10000 } ] } @@ -48,7 +48,7 @@ "hooks": [ { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/session-end.sh\"", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/session-end.sh\"", "timeout": 3000 } ] @@ -57,7 +57,7 @@ }, "statusLine": { "type": "command", - "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/statusline.sh\"" + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/scripts/local/hooks/statusline.sh\"" }, "permissions": { "allow": [ diff --git a/.gitignore b/.gitignore index 195f651..709d639 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ temp/ *.duckdb.wal .claude/.state/ .claude/edit-log.jsonl +.mcp.json +.claude/.javaducker/ +scripts/local/ diff --git a/README.md b/README.md index 181edce..e98bf27 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Code and content intelligence server for Claude Code. Indexes source files, documents, plans, notes, and threads into DuckDB with semantic search, dependency graphs, content classification, session history, and Reladomo support — all accessible via 49 MCP tools and 59 REST endpoints. +> **New here?** See [start-here.md](start-here.md) for setup instructions including how to recreate local scripts. + ## Tech Stack - **Java 21** + **Spring Boot 3.2** diff --git a/run-cli.cmd b/run-cli.cmd deleted file mode 100644 index 9a07113..0000000 --- a/run-cli.cmd +++ /dev/null @@ -1,21 +0,0 @@ -@echo off -cd /d "%~dp0" - -set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" -set "PATH=%JAVA_HOME%\bin;%PATH%" - -if "%HTTP_PORT%"=="" set HTTP_PORT=8080 -if "%JAVADUCKER_HOST%"=="" set JAVADUCKER_HOST=localhost - -if not exist "target\classes\com\javaducker\cli\InteractiveCli.class" ( - echo Compiling... - call mvn -q compile -DskipTests -) - -if not exist "target\dependency" ( - echo Copying dependencies... - call mvn -q dependency:copy-dependencies -) - -java -cp "target\classes;target\dependency\*" ^ - com.javaducker.cli.InteractiveCli --host %JAVADUCKER_HOST% --port %HTTP_PORT% %* diff --git a/run-cli.sh b/run-cli.sh deleted file mode 100644 index 64a5cb9..0000000 --- a/run-cli.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -cd "$(dirname "$0")" - -HTTP_PORT="${HTTP_PORT:-8080}" -JAVADUCKER_HOST="${JAVADUCKER_HOST:-localhost}" - -java -cp "target/javaducker-1.0.0.jar:target/dependency/*" \ - com.javaducker.cli.InteractiveCli --host "$JAVADUCKER_HOST" --port "$HTTP_PORT" "$@" diff --git a/run-client.sh b/run-client.sh deleted file mode 100644 index c97efed..0000000 --- a/run-client.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e -cd "$(dirname "$0")" - -# Build if needed -if [ ! -d target/dependency ] || [ ! -d target/classes ]; then - echo "Building project..." - mvn -q package -DskipTests -fi - -# If --enrich flag, run the enrichment script instead -if [ "$1" = "--enrich" ] || [ "$1" = "enrich" ]; then - shift - exec bash scripts/enrich.sh "$@" -fi - -java -cp "target/classes:target/dependency/*" \ - com.javaducker.client.JavaDuckerClient "$@" diff --git a/run-mcp.cmd b/run-mcp.cmd deleted file mode 100644 index 4c6b0db..0000000 --- a/run-mcp.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -set "PROJECT_ROOT=%~dp0" -if "%PROJECT_ROOT:~-1%"=="\" set "PROJECT_ROOT=%PROJECT_ROOT:~0,-1%" - -set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" -set "PATH=%JAVA_HOME%\bin;%PATH%" - -jbang "%~dp0JavaDuckerMcpServer.java" diff --git a/run-mcp.sh b/run-mcp.sh deleted file mode 100644 index bf59f5d..0000000 --- a/run-mcp.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -export PROJECT_ROOT="$SCRIPT_DIR" -exec jbang "$SCRIPT_DIR/JavaDuckerMcpServer.java" diff --git a/run-server.cmd b/run-server.cmd deleted file mode 100644 index 3ee93ef..0000000 --- a/run-server.cmd +++ /dev/null @@ -1,18 +0,0 @@ -@echo off -cd /d "%~dp0" - -set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" -set "PATH=%JAVA_HOME%\bin;%PATH%" - -echo Building project... -call mvn -q package -DskipTests - -if "%DB%"=="" set DB=data\javaducker.duckdb -if "%HTTP_PORT%"=="" set HTTP_PORT=8080 -if "%INTAKE_DIR%"=="" set INTAKE_DIR=temp\intake - -echo Starting JavaDucker Server on HTTP port %HTTP_PORT% -java -jar target\javaducker-1.0.0.jar ^ - --javaducker.db-path="%DB%" ^ - --server.port=%HTTP_PORT% ^ - --javaducker.intake-dir="%INTAKE_DIR%" %* diff --git a/run-server.sh b/run-server.sh deleted file mode 100644 index 61c054a..0000000 --- a/run-server.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -set -e -cd "$(dirname "$0")" - -# Build if needed -if [ ! -f target/javaducker-1.0.0.jar ]; then - echo "Building project..." - mvn -q package -DskipTests -fi - -# Default args -DB="${DB:-data/javaducker.duckdb}" -HTTP_PORT="${HTTP_PORT:-8080}" -INTAKE_DIR="${INTAKE_DIR:-temp/intake}" - -echo "Starting JavaDucker Server on HTTP port $HTTP_PORT" -java -jar target/javaducker-1.0.0.jar \ - --javaducker.db-path="$DB" \ - --server.port="$HTTP_PORT" \ - --javaducker.intake-dir="$INTAKE_DIR" \ - "$@" diff --git a/script-instructions/hook-scripts.md b/script-instructions/hook-scripts.md new file mode 100644 index 0000000..c1804f0 --- /dev/null +++ b/script-instructions/hook-scripts.md @@ -0,0 +1,505 @@ +# Hook Scripts + +Claude Code lifecycle hooks used by drom-flow. Recreate in `scripts/local/hooks/`. + +Referenced by `.claude/settings.json` — after recreating, hooks work automatically. + +--- + +## edit-log.sh + +Appends edit events to JSONL log for tracking file modifications. + +```bash +#!/bin/bash +# drom-flow edit logger — appends edit events to JSONL + +DIR="${CLAUDE_PROJECT_DIR:-.}" +LOG="$DIR/.claude/edit-log.jsonl" + +# Extract file_path from tool input (passed via stdin) +file_path="unknown" +if [ -n "$CLAUDE_TOOL_USE_INPUT" ]; then + fp=$(echo "$CLAUDE_TOOL_USE_INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4) + [ -n "$fp" ] && file_path="$fp" +fi + +timestamp=$(date +%s) +echo "{\"type\":\"edit\",\"file\":\"$file_path\",\"timestamp\":$timestamp}" >> "$LOG" +``` + +--- + +## enrich-check.sh + +Checks if JavaDucker has artifacts pending enrichment at session start. + +```bash +#!/bin/bash +# Hook: Check if JavaDucker has artifacts pending enrichment + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" +HOST="${JAVADUCKER_HOST:-localhost}" +PORT="${JAVADUCKER_PORT:-8080}" +BASE_URL="http://${HOST}:${PORT}/api" + +# Check if server is running +if ! curl -sf "${BASE_URL}/health" > /dev/null 2>&1; then + exit 0 +fi + +# Check enrichment queue +QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=1" 2>/dev/null || echo '{"count":0}') +COUNT=$(echo "$QUEUE" | jq -r '.count' 2>/dev/null || echo "0") + +if [ "$COUNT" != "0" ] && [ -n "$COUNT" ]; then + FULL_QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=100" 2>/dev/null || echo '{"count":0}') + TOTAL=$(echo "$FULL_QUEUE" | jq -r '.count' 2>/dev/null || echo "0") + echo "[Content Intelligence] ${TOTAL} artifact(s) pending enrichment. Use javaducker_enrich_queue to see them, then classify/tag/extract points using the write MCP tools." +fi +``` + +--- + +## memory-sync.sh + +Injects session memory and checks for in-progress plans on session start. + +```bash +#!/bin/bash +# drom-flow memory sync — inject session memory and check for in-progress plans on start + +DIR="${CLAUDE_PROJECT_DIR:-.}" +MEMORY="$DIR/context/MEMORY.md" +STATE_DIR="$DIR/.claude/.state" +PLANS_DIR="$DIR/drom-plans" + +# Initialize session state +mkdir -p "$STATE_DIR" +date +%s > "$STATE_DIR/session-start" +echo "0" > "$STATE_DIR/agent-count" +echo "0" > "$STATE_DIR/edit-count" + +# Load session memory +if [ -s "$MEMORY" ]; then + echo "[Session Memory Loaded]" + echo "---" + cat "$MEMORY" + echo "---" +else + echo "[No session memory found. Create context/MEMORY.md to persist context across sessions.]" +fi + +# Check for in-progress plans +if [ -d "$PLANS_DIR" ]; then + in_progress="" + for plan in "$PLANS_DIR"/*.md; do + [ -f "$plan" ] || continue + if grep -q "^status: in-progress" "$plan" 2>/dev/null; then + title=$(grep "^title:" "$plan" 2>/dev/null | sed 's/^title: *//') + chapter=$(grep "^current_chapter:" "$plan" 2>/dev/null | sed 's/^current_chapter: *//') + basename=$(basename "$plan") + in_progress="${in_progress}\n - ${basename} — \"${title}\" (Chapter ${chapter:-?})" + fi + done + if [ -n "$in_progress" ]; then + echo "" + echo "[In-Progress Plans Found]" + echo -e "The following plans were stopped midway and can be resumed:${in_progress}" + echo "Read the plan file to review progress and resume from the current chapter." + fi +fi + +# --- JavaDucker: auto-start and health check --- +. "$DIR/scripts/local/hooks/javaducker-check.sh" 2>/dev/null +if javaducker_available; then + if javaducker_healthy; then + echo "[JavaDucker: connected (port ${JAVADUCKER_HTTP_PORT:-8080})]" + else + echo "[JavaDucker: starting server...]" + if javaducker_start; then + echo "[JavaDucker: connected (port ${JAVADUCKER_HTTP_PORT:-8080})]" + else + echo "[JavaDucker: server starting in background — will be available shortly]" + fi + fi +fi +``` + +--- + +## session-end.sh + +Reminds to persist progress and checks JavaDucker hygiene at session end. + +```bash +#!/bin/bash +# drom-flow session end — remind to persist progress and update plans + +DIR="${CLAUDE_PROJECT_DIR:-.}" +PLANS_DIR="$DIR/drom-plans" + +echo "[Session ending. Update context/MEMORY.md with progress, findings, and next steps.]" + +# Remind about in-progress plans +if [ -d "$PLANS_DIR" ]; then + for plan in "$PLANS_DIR"/*.md; do + [ -f "$plan" ] || continue + if grep -q "^status: in-progress" "$plan" 2>/dev/null; then + title=$(grep "^title:" "$plan" 2>/dev/null | sed 's/^title: *//') + echo "[Plan in progress: \"${title}\" — update chapter status and step checkboxes before ending.]" + fi + done +fi + +# JavaDucker session-end hygiene +. "$DIR/scripts/local/hooks/javaducker-check.sh" 2>/dev/null +if javaducker_available && javaducker_healthy; then + edits=0 + [ -f "$DIR/.claude/edit-log.jsonl" ] && edits=$(wc -l < "$DIR/.claude/edit-log.jsonl" | tr -d ' ') + if [ "$edits" -gt 10 ]; then + echo "[JavaDucker: $edits files edited — run javaducker_index_health to check freshness.]" + fi + queue=$(curl -sf "http://localhost:${JAVADUCKER_HTTP_PORT:-8080}/api/enrich-queue?limit=1" 2>/dev/null) + if [ -n "$queue" ] && echo "$queue" | grep -q '"artifact_id"'; then + echo "[JavaDucker: un-enriched artifacts detected — run workflows/javaducker-hygiene.md Phase 2 to classify, tag, and extract points.]" + fi +fi +``` + +--- + +## statusline.sh + +Git-aware status line for Claude Code showing branch, edits, agents, and plan progress. + +```bash +#!/bin/bash +# drom-flow statusline — git-aware status for Claude Code + +DIR="${CLAUDE_PROJECT_DIR:-.}" +STATE_DIR="$DIR/.claude/.state" + +# --- Version --- +DROMFLOW_VERSION="" +for vfile in "$DIR/VERSION" "$(dirname "${BASH_SOURCE[0]}")/../../../VERSION"; do + if [ -f "$vfile" ]; then + DROMFLOW_VERSION=$(tr -d '[:space:]' < "$vfile") + break + fi +done +DROMFLOW_VERSION="${DROMFLOW_VERSION:-dev}" + +# --- Project root (bright cyan to pop) --- +PROJECT_ROOT="\033[1;36m$(basename "$(cd "$DIR" && pwd)")\033[0m" + +# --- Session elapsed time --- +elapsed="" +if [ -f "$STATE_DIR/session-start" ]; then + start=$(cat "$STATE_DIR/session-start") + now=$(date +%s) + diff=$((now - start)) + mins=$((diff / 60)) + secs=$((diff % 60)) + if [ $mins -ge 60 ]; then + hrs=$((mins / 60)) + mins=$((mins % 60)) + elapsed="${hrs}h${mins}m" + else + elapsed="${mins}m${secs}s" + fi +fi + +# --- Plan progress --- +plan_info="" +PLANS_DIR="$DIR/drom-plans" +if [ -d "$PLANS_DIR" ]; then + for plan in "$PLANS_DIR"/*.md; do + [ -f "$plan" ] || continue + if grep -q "^status: in-progress" "$plan" 2>/dev/null || grep -q '^\*\*Status:\*\* in-progress' "$plan" 2>/dev/null; then + cur=$(grep "^current_chapter:" "$plan" 2>/dev/null | sed 's/^current_chapter: *//') + total=$(grep -c "^## Chapter " "$plan" 2>/dev/null) + done_count=$(grep -c '^\*\*Status:\*\* completed' "$plan" 2>/dev/null) + plan_info="plan:ch${cur:-?}/${total:-?}(${done_count:-0}done)" + break + fi + done +fi + +# --- Git info --- +branch=$(git branch --show-current 2>/dev/null || echo "no-git") +if [ "$branch" = "no-git" ]; then + nogit_status="drom-flow v$DROMFLOW_VERSION | $PROJECT_ROOT | [no-git] | ${elapsed:-0m0s}" + [ -n "$plan_info" ] && nogit_status="$nogit_status | $plan_info" + echo -e "$nogit_status" + exit 0 +fi + +staged=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ') +unstaged=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ') +untracked=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ') + +ahead=0 +behind=0 +upstream=$(git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null) +if [ $? -eq 0 ]; then + ahead=$(echo "$upstream" | awk '{print $1}') + behind=$(echo "$upstream" | awk '{print $2}') +fi + +git_info="$branch +${staged}/-${unstaged}/?${untracked}" +[ "$ahead" -gt 0 ] || [ "$behind" -gt 0 ] && git_info="$git_info up${ahead}dn${behind}" + +# --- Edit count --- +edits=0 +[ -f "$DIR/.claude/edit-log.jsonl" ] && edits=$(wc -l < "$DIR/.claude/edit-log.jsonl" | tr -d ' ') + +# --- Background agents --- +agents=0 +[ -f "$STATE_DIR/agent-count" ] && agents=$(cat "$STATE_DIR/agent-count" | tr -d '[:space:]') + +# --- Memory status --- +mem="off" +[ -s "$DIR/context/MEMORY.md" ] && mem="on" + +# --- JavaDucker status --- +jd_icon="" +. "$DIR/scripts/local/hooks/javaducker-check.sh" 2>/dev/null +if javaducker_available; then + javaducker_healthy && jd_icon="JD" || jd_icon="JD(off)" +fi + +status="drom-flow v$DROMFLOW_VERSION | $PROJECT_ROOT | $git_info | ${elapsed:-0m0s} | edits:$edits | agents:$agents | mem:$mem" +[ -n "$jd_icon" ] && status="$status | $jd_icon" +[ -n "$plan_info" ] && status="$status | $plan_info" +echo -e "$status" +``` + +--- + +## track-agents.sh + +Increments background agent counter for statusline display. + +```bash +#!/bin/bash +# drom-flow — track background agent count + +STATE_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/.state" +mkdir -p "$STATE_DIR" + +count=0 +[ -f "$STATE_DIR/agent-count" ] && count=$(cat "$STATE_DIR/agent-count" | tr -d '[:space:]') +echo $((count + 1)) > "$STATE_DIR/agent-count" +``` + +--- + +## validate-plan.sh + +Validates plan files written to drom-plans/ have correct frontmatter and structure. + +```bash +#!/bin/bash +# drom-flow — validate plan files written to drom-plans/ + +DIR="${CLAUDE_PROJECT_DIR:-.}" +PLANS_DIR="$DIR/drom-plans" + +# Extract file_path from tool input +file_path="" +if [ -n "$CLAUDE_TOOL_USE_INPUT" ]; then + fp=$(echo "$CLAUDE_TOOL_USE_INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4) + [ -n "$fp" ] && file_path="$fp" +fi + +# Only validate files in drom-plans/ +case "$file_path" in + */drom-plans/*.md|drom-plans/*.md) ;; + *) exit 0 ;; +esac + +[ ! -f "$file_path" ] && exit 0 + +errors="" + +# Check frontmatter exists +if ! head -1 "$file_path" | grep -q "^---"; then + errors="${errors}\n - Missing YAML frontmatter (must start with ---)" +fi + +# Check required frontmatter fields +for field in title status created updated current_chapter; do + if ! grep -q "^${field}:" "$file_path"; then + errors="${errors}\n - Missing frontmatter field: ${field}" + fi +done + +# Check status value +status=$(grep "^status:" "$file_path" | head -1 | sed 's/^status: *//') +case "$status" in + in-progress|completed|pending|abandoned) ;; + *) errors="${errors}\n - Invalid status: '${status}' (must be: in-progress, completed, pending, or abandoned)" ;; +esac + +# Check for at least one chapter +chapter_count=$(grep -c "^## Chapter " "$file_path" 2>/dev/null | tr -d '[:space:]') +chapter_count=${chapter_count:-0} +if [ "$chapter_count" -eq 0 ]; then + errors="${errors}\n - No chapters found (need at least one '## Chapter N: Title')" +fi + +# Check chapters have Status lines +while IFS= read -r line; do + chapter_num=$(echo "$line" | grep -o "Chapter [0-9]*" | grep -o "[0-9]*") + if ! grep -A2 "^## Chapter ${chapter_num}:" "$file_path" | grep -q '^\*\*Status:\*\*'; then + chapters_without_status=$((chapters_without_status + 1)) + errors="${errors}\n - Chapter ${chapter_num} missing **Status:** line" + fi +done < <(grep "^## Chapter " "$file_path") + +# Check chapters have at least one step +while IFS= read -r line; do + chapter_num=$(echo "$line" | grep -o "Chapter [0-9]*" | grep -o "[0-9]*") + next_section=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^## /{print NR; exit}" "$file_path") + if [ -n "$next_section" ]; then + step_count=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^## /{exit} found && /^- \[/" "$file_path" | wc -l) + else + step_count=$(awk "/^## Chapter ${chapter_num}:/{found=1; next} found && /^- \[/" "$file_path" | wc -l) + fi + if [ "$step_count" -eq 0 ]; then + errors="${errors}\n - Chapter ${chapter_num} has no steps (need at least one '- [ ] ...')" + fi +done < <(grep "^## Chapter " "$file_path") + +# Check current_chapter points to a valid chapter +current=$(grep "^current_chapter:" "$file_path" | head -1 | sed 's/^current_chapter: *//') +if [ -n "$current" ] && [ "$chapter_count" -gt 0 ]; then + if ! grep -q "^## Chapter ${current}:" "$file_path"; then + errors="${errors}\n - current_chapter: ${current} does not match any chapter heading" + fi +fi + +if [ -n "$errors" ]; then + echo "PLAN VALIDATION FAILED: $(basename "$file_path")" + echo -e "Issues:${errors}" + echo "" + echo "Expected format: see /planner skill or drom-plans/ docs in CLAUDE.md" + exit 1 +fi +``` + +--- + +## javaducker-check.sh + +Guard and lifecycle functions for JavaDucker (sourced by other hooks). + +```bash +#!/bin/bash +# drom-flow — JavaDucker guard and lifecycle functions (sourced by other hooks) +# When .claude/.state/javaducker.conf does not exist, all functions return false. + +JAVADUCKER_CONF="${CLAUDE_PROJECT_DIR:-.}/.claude/.state/javaducker.conf" + +javaducker_available() { + [ -f "$JAVADUCKER_CONF" ] || return 1 + . "$JAVADUCKER_CONF" + [ -n "$JAVADUCKER_ROOT" ] +} + +javaducker_healthy() { + javaducker_available || return 1 + curl -sf "http://localhost:${JAVADUCKER_HTTP_PORT:-8080}/api/health" >/dev/null 2>&1 +} + +# Find a free TCP port in the 8080-8180 range +javaducker_find_free_port() { + for port in $(seq 8080 8180); do + if ! (echo >/dev/tcp/localhost/$port) 2>/dev/null; then + echo "$port" + return 0 + fi + done + echo "8080" +} + +# Start the server with project-local data paths +javaducker_start() { + javaducker_available || return 1 + javaducker_healthy && return 0 + + local db="${JAVADUCKER_DB:-${CLAUDE_PROJECT_DIR:-.}/.claude/.javaducker/javaducker.duckdb}" + local intake="${JAVADUCKER_INTAKE:-${CLAUDE_PROJECT_DIR:-.}/.claude/.javaducker/intake}" + local port="${JAVADUCKER_HTTP_PORT:-8080}" + + mkdir -p "$(dirname "$db")" "$intake" + + # Check if the configured port is taken; if so, find a free one + if (echo >/dev/tcp/localhost/$port) 2>/dev/null; then + if curl -sf "http://localhost:$port/api/health" >/dev/null 2>&1; then + return 0 + fi + port=$(javaducker_find_free_port) + sed -i "s/^JAVADUCKER_HTTP_PORT=.*/JAVADUCKER_HTTP_PORT=$port/" "$JAVADUCKER_CONF" + export JAVADUCKER_HTTP_PORT="$port" + fi + + DB="$db" HTTP_PORT="$port" INTAKE_DIR="$intake" \ + nohup bash "${JAVADUCKER_ROOT}/run-server.sh" >/dev/null 2>&1 & + + # Wait for startup + for i in 1 2 3 4 5 6 7 8; do + sleep 1 + if curl -sf "http://localhost:$port/api/health" >/dev/null 2>&1; then + return 0 + fi + done + return 1 +} +``` + +--- + +## javaducker-index.sh + +Indexes modified files in JavaDucker after edits (fire-and-forget). + +```bash +#!/bin/bash +# drom-flow — index modified files in JavaDucker after edits +# Triggered by PostToolUse on Write|Edit|MultiEdit +# Fire-and-forget: does not block the edit. + +DIR="${CLAUDE_PROJECT_DIR:-.}" +. "$DIR/scripts/local/hooks/javaducker-check.sh" 2>/dev/null +javaducker_healthy || exit 0 + +# Extract file_path from tool input +file_path="" +if [ -n "$CLAUDE_TOOL_USE_INPUT" ]; then + fp=$(echo "$CLAUDE_TOOL_USE_INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4) + [ -n "$fp" ] && file_path="$fp" +fi +[ -z "$file_path" ] && exit 0 +[ -f "$file_path" ] || exit 0 + +# Index via REST API (background, fire-and-forget) +abs_path=$(realpath "$file_path" 2>/dev/null || echo "$file_path") +curl -sf -X POST "http://localhost:${JAVADUCKER_HTTP_PORT:-8080}/api/upload-file" \ + -H "Content-Type: application/json" \ + -d "{\"file_path\":\"$abs_path\"}" \ + >/dev/null 2>&1 & +``` + +--- + +## Setup + +To recreate all hooks from this instructions file: + +1. Create `scripts/local/hooks/` directory if it doesn't exist +2. Copy each code block above into the corresponding file in `scripts/local/hooks/` +3. Make executable: `chmod +x scripts/local/hooks/*.sh` +4. Hooks are referenced by `.claude/settings.json` and will work automatically once recreated diff --git a/script-instructions/run-scripts.md b/script-instructions/run-scripts.md new file mode 100644 index 0000000..2f4b925 --- /dev/null +++ b/script-instructions/run-scripts.md @@ -0,0 +1,173 @@ +# Run Scripts + +These scripts launch the JavaDucker application in different modes. +Recreate them in `scripts/local/` (which is gitignored). + +--- + +## run-cli.sh + +```bash +#!/bin/bash +cd "$(dirname "$0")/.." + +DB="${DB:-data/javaducker.duckdb}" +INTAKE_DIR="${INTAKE_DIR:-temp/intake}" + +echo "Building project..." +mvn -q package -DskipTests + +echo "Starting JavaDucker CLI" +java -cp target/javaducker-1.0.0.jar \ + com.drom.javaducker.cli.CliMain \ + --javaducker.db-path="$DB" \ + --javaducker.intake-dir="$INTAKE_DIR" "$@" +``` + +--- + +## run-cli.cmd + +```cmd +@echo off +cd /d "%~dp0\.." + +set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" +set "PATH=%JAVA_HOME%\bin;%PATH%" + +echo Building project... +call mvn -q package -DskipTests + +echo Starting JavaDucker CLI +java -cp target\javaducker-1.0.0.jar ^ + com.drom.javaducker.cli.CliMain ^ + --javaducker.db-path="data\javaducker.duckdb" ^ + --javaducker.intake-dir="temp\intake" %* +``` + +--- + +## run-client.sh + +```bash +#!/bin/bash +# Run the MCP client (interactive CLI over stdio transport) +cd "$(dirname "$0")/.." + +DB="${DB:-data/javaducker.duckdb}" +HTTP_PORT="${HTTP_PORT:-8080}" +INTAKE_DIR="${INTAKE_DIR:-temp/intake}" + +echo "Building project..." +mvn -q package -DskipTests + +echo "Starting JavaDucker MCP Client..." +java -cp target/javaducker-1.0.0.jar \ + com.drom.javaducker.McpClientRunner \ + --javaducker.db-path="$DB" \ + --server.port="$HTTP_PORT" \ + --javaducker.intake-dir="$INTAKE_DIR" "$@" +``` + +--- + +## run-mcp.sh + +```bash +#!/bin/bash +cd "$(dirname "$0")/.." + +DB="${DB:-data/javaducker.duckdb}" +HTTP_PORT="${HTTP_PORT:-8080}" +INTAKE_DIR="${INTAKE_DIR:-temp/intake}" + +mvn -q package -DskipTests 1>&2 + +java -jar target/javaducker-1.0.0.jar \ + --javaducker.db-path="$DB" \ + --server.port="$HTTP_PORT" \ + --javaducker.intake-dir="$INTAKE_DIR" \ + --spring.main.web-application-type=none "$@" +``` + +--- + +## run-mcp.cmd + +```cmd +@echo off +cd /d "%~dp0\.." + +set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" +set "PATH=%JAVA_HOME%\bin;%PATH%" + +echo Building project... 1>&2 +call mvn -q package -DskipTests 1>&2 + +if "%DB%"=="" set DB=data\javaducker.duckdb +if "%HTTP_PORT%"=="" set HTTP_PORT=8080 +if "%INTAKE_DIR%"=="" set INTAKE_DIR=temp\intake + +java -jar target\javaducker-1.0.0.jar ^ + --javaducker.db-path="%DB%" ^ + --server.port=%HTTP_PORT% ^ + --javaducker.intake-dir="%INTAKE_DIR%" ^ + --spring.main.web-application-type=none %* +``` + +--- + +## run-server.sh + +```bash +#!/bin/bash +cd "$(dirname "$0")/.." + +DB="${DB:-data/javaducker.duckdb}" +HTTP_PORT="${HTTP_PORT:-8080}" +INTAKE_DIR="${INTAKE_DIR:-temp/intake}" + +echo "Building project..." +mvn -q package -DskipTests + +echo "Starting JavaDucker Server on HTTP port $HTTP_PORT" +java -jar target/javaducker-1.0.0.jar \ + --javaducker.db-path="$DB" \ + --server.port="$HTTP_PORT" \ + --javaducker.intake-dir="$INTAKE_DIR" "$@" +``` + +--- + +## run-server.cmd + +```cmd +@echo off +cd /d "%~dp0\.." + +set "JAVA_HOME=C:\Users\drom\.jdks\openjdk-23.0.2" +set "PATH=%JAVA_HOME%\bin;%PATH%" + +echo Building project... +call mvn -q package -DskipTests + +if "%DB%"=="" set DB=data\javaducker.duckdb +if "%HTTP_PORT%"=="" set HTTP_PORT=8080 +if "%INTAKE_DIR%"=="" set INTAKE_DIR=temp\intake + +echo Starting JavaDucker Server on HTTP port %HTTP_PORT% +java -jar target\javaducker-1.0.0.jar ^ + --javaducker.db-path="%DB%" ^ + --server.port=%HTTP_PORT% ^ + --javaducker.intake-dir="%INTAKE_DIR%" %* +``` + +--- + +## Setup + +To recreate these scripts from this instructions file: + +1. Create `scripts/local/` directory if it doesn't exist +2. Copy each code block above into the corresponding file in `scripts/local/` +3. On Linux/macOS, make `.sh` files executable: `chmod +x scripts/local/*.sh` diff --git a/script-instructions/utility-scripts.md b/script-instructions/utility-scripts.md new file mode 100644 index 0000000..94df039 --- /dev/null +++ b/script-instructions/utility-scripts.md @@ -0,0 +1,314 @@ +# Utility Scripts + +Pipeline and orchestration scripts. Recreate in `scripts/local/`. + +--- + +## enrich.sh + +Content Intelligence enrichment script — processes the JavaDucker enrichment queue via REST API. + +```bash +#!/bin/bash +# Content Intelligence Enrichment Script +# Triggered by Claude Code hooks to process the enrichment queue. +# Calls JavaDucker REST API to fetch pending artifacts, then uses Claude Code +# to classify, tag, extract salient points, and assess freshness. +# +# Usage: bash scripts/local/enrich.sh [--limit N] [--host HOST] [--port PORT] +set -e + +HOST="${JAVADUCKER_HOST:-localhost}" +PORT="${JAVADUCKER_PORT:-8080}" +LIMIT=10 +BASE_URL="http://${HOST}:${PORT}/api" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Parse args +while [[ $# -gt 0 ]]; do + case $1 in + --limit) LIMIT="$2"; shift 2 ;; + --host) HOST="$2"; BASE_URL="http://${HOST}:${PORT}/api"; shift 2 ;; + --port) PORT="$2"; BASE_URL="http://${HOST}:${PORT}/api"; shift 2 ;; + *) shift ;; + esac +done + +# Check if server is running +if ! curl -sf "${BASE_URL}/health" > /dev/null 2>&1; then + echo "[enrich] JavaDucker server not running at ${BASE_URL}, skipping enrichment" + exit 0 +fi + +# Fetch enrichment queue +QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=${LIMIT}") +COUNT=$(echo "$QUEUE" | jq -r '.count') + +if [ "$COUNT" = "0" ] || [ -z "$COUNT" ]; then + echo "[enrich] No artifacts pending enrichment" + exit 0 +fi + +echo "[enrich] Processing ${COUNT} artifacts from enrichment queue" + +# Process each artifact +echo "$QUEUE" | jq -r '.queue[].artifact_id' | while read -r ARTIFACT_ID; do + echo "[enrich] Processing artifact: ${ARTIFACT_ID}" + + # Fetch artifact text + TEXT_RESPONSE=$(curl -sf "${BASE_URL}/text/${ARTIFACT_ID}" 2>/dev/null || echo '{"error":"not found"}') + if echo "$TEXT_RESPONSE" | jq -e '.error' > /dev/null 2>&1; then + echo "[enrich] Skipping ${ARTIFACT_ID}: no text available" + continue + fi + + FILE_NAME=$(echo "$QUEUE" | jq -r --arg id "$ARTIFACT_ID" '.queue[] | select(.artifact_id == $id) | .file_name') + EXTRACTED_TEXT=$(echo "$TEXT_RESPONSE" | jq -r '.extracted_text') + + # Fetch related artifacts for freshness evaluation + RELATED=$(curl -sf "${BASE_URL}/related-by-concept/${ARTIFACT_ID}" 2>/dev/null || echo '{"related":[]}') + + echo "[enrich] Artifact ready for enrichment:" + echo " ID: ${ARTIFACT_ID}" + echo " File: ${FILE_NAME}" + echo " Text length: ${#EXTRACTED_TEXT}" + echo " Related artifacts: $(echo "$RELATED" | jq -r '.count')" + + # Mark as enriching (claim the artifact) + curl -sf -X POST "${BASE_URL}/freshness" \ + -H "Content-Type: application/json" \ + -d "{\"artifactId\":\"${ARTIFACT_ID}\",\"freshness\":\"current\"}" > /dev/null 2>&1 || true + + echo "[enrich] Artifact ${ARTIFACT_ID} queued for Claude Code enrichment" +done + +echo "[enrich] Enrichment pass complete" +``` + +--- + +## index-sessions.sh + +Indexes Claude Code session transcripts for the current project. + +```bash +#!/usr/bin/env bash +# Index Claude Code session transcripts for the current project +set -euo pipefail + +JAVADUCKER_PORT="${HTTP_PORT:-8080}" +JAVADUCKER_HOST="${JAVADUCKER_HOST:-localhost}" +BASE_URL="http://${JAVADUCKER_HOST}:${JAVADUCKER_PORT}/api" + +# Find the project sessions directory +PROJECT_ROOT="${PROJECT_ROOT:-.}" +PROJECT_HASH=$(echo -n "$(cd "$PROJECT_ROOT" && pwd)" | md5sum | cut -d' ' -f1) +SESSIONS_DIR="$HOME/.claude/projects/${PROJECT_HASH}" + +if [ ! -d "$SESSIONS_DIR" ]; then + echo "No sessions directory found at $SESSIONS_DIR" + exit 0 +fi + +echo "Indexing sessions from $SESSIONS_DIR..." +curl -s -X POST "${BASE_URL}/index-sessions" \ + -H "Content-Type: application/json" \ + -d "{\"projectPath\": \"${SESSIONS_DIR}\", \"incremental\": true}" | jq . +``` + +--- + +## orchestrate.sh + +Closed-loop orchestration template for drom-flow pipelines. + +```bash +#!/bin/bash +# drom-flow orchestration script template +# Copy and customize this for your project's pipeline. +# +# Usage: +# ./scripts/local/orchestrate.sh [--iteration N] [--max N] [--check-only] +# +# Output: +# Writes JSON report to ./reports/iteration-N.json +# Exit 0 = all pass, Exit 1 = issues remain, Exit 2 = error + +set -euo pipefail + +# --- Configuration (customize these) --- +CHECK_CMD="echo 'Override CHECK_CMD with your test/check command'" +REPORT_DIR="./reports" +MAX_ITERATIONS=10 +# ---------------------------------------- + +# Parse arguments +ITERATION=1 +CHECK_ONLY=false +while [[ $# -gt 0 ]]; do + case $1 in + --iteration) ITERATION="$2"; shift 2 ;; + --max) MAX_ITERATIONS="$2"; shift 2 ;; + --check-only) CHECK_ONLY=true; shift ;; + *) echo "Unknown arg: $1"; exit 2 ;; + esac +done + +mkdir -p "$REPORT_DIR" + +run_check() { + local iter=$1 + local report="$REPORT_DIR/iteration-${iter}.json" + local start_time=$(date +%s) + + echo "[orchestrate] Iteration $iter — running check..." + + # Run the check command, capture output + local exit_code=0 + local output + output=$(eval "$CHECK_CMD" 2>&1) || exit_code=$? + + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + + # Write report + cat > "$report" </dev/null || echo "\"$output\"") +} +INNEREOF + + echo "[orchestrate] Report written to $report (exit code: $exit_code, ${duration}s)" + return $exit_code +} + +compare_iterations() { + local prev="$REPORT_DIR/iteration-$(($1 - 1)).json" + local curr="$REPORT_DIR/iteration-$1.json" + + if [ ! -f "$prev" ]; then + echo "[orchestrate] No previous iteration to compare" + return 0 + fi + + local prev_exit=$(python3 -c "import json; print(json.load(open('$prev'))['exitCode'])" 2>/dev/null || echo "1") + local curr_exit=$(python3 -c "import json; print(json.load(open('$curr'))['exitCode'])" 2>/dev/null || echo "1") + + echo "[orchestrate] Previous exit: $prev_exit → Current exit: $curr_exit" + + if [ "$curr_exit" -gt "$prev_exit" ]; then + echo "[orchestrate] WARNING: Possible regression detected" + return 1 + fi + return 0 +} + +# --- Main --- + +if [ "$CHECK_ONLY" = true ]; then + run_check "$ITERATION" + exit $? +fi + +echo "[orchestrate] Starting closed loop: iteration $ITERATION, max $MAX_ITERATIONS" + +while [ "$ITERATION" -le "$MAX_ITERATIONS" ]; do + if run_check "$ITERATION"; then + echo "[orchestrate] ALL CHECKS PASSED at iteration $ITERATION" + exit 0 + fi + + if [ "$ITERATION" -gt 1 ]; then + if ! compare_iterations "$ITERATION"; then + echo "[orchestrate] Regression at iteration $ITERATION — stopping for review" + exit 1 + fi + fi + + echo "[orchestrate] Issues remain. Report: $REPORT_DIR/iteration-${ITERATION}.json" + echo "[orchestrate] Waiting for fixes before next iteration..." + exit 1 + +done + +echo "[orchestrate] Max iterations ($MAX_ITERATIONS) reached" +exit 1 +``` + +--- + +## enrich-batch.ps1 + +PowerShell batch enrichment script for classifying, tagging, and extracting points. + +```powershell +param( + [string]$Base = "http://localhost:8081/api", + [string]$ArtifactsCsv, # pipe-delimited: id|file_name + [string]$DocType, # ADR, MEETING_NOTES, etc. + [string]$TagsJson, # JSON array of {tag,tag_type,source} + [string]$PointsMode = "none" # "none", "auto", or path to points JSON file +) + +function Post($path, $body) { + $json = $body | ConvertTo-Json -Depth 5 -Compress + try { + Invoke-RestMethod "$Base$path" -Method POST -ContentType "application/json" -Body $json + } catch { + Write-Warning "POST $path failed: $_" + } +} + +$lines = $ArtifactsCsv -split "`n" | Where-Object { $_.Trim() -ne "" } +$total = $lines.Count +$i = 0 + +foreach ($line in $lines) { + $parts = $line.Trim() -split "\|" + $id = $parts[0] + $fname = $parts[1] + $i++ + + # Classify + if ($DocType) { + Post "/classify" @{ artifactId=$id; docType=$DocType; confidence=1; method="llm" } + } + + # Tag + if ($TagsJson) { + $tags = $TagsJson | ConvertFrom-Json + Post "/tag" @{ artifactId=$id; tags=$tags } + } + + # Points from file + if ($PointsMode -ne "none" -and $PointsMode -ne "auto" -and (Test-Path $PointsMode)) { + $pointsData = Get-Content $PointsMode -Raw | ConvertFrom-Json + $artifactPoints = $pointsData | Where-Object { $_.artifact_id -eq $id } + if ($artifactPoints) { + Post "/salient-points" @{ artifactId=$id; points=$artifactPoints.points } + } + } + + # Mark enriched + Post "/mark-enriched" @{ artifactId=$id } + + if ($i % 10 -eq 0 -or $i -eq $total) { + Write-Output " [$i/$total] enriched" + } +} +Write-Output "Done: $total artifacts enriched as $DocType" +``` + +--- + +## Setup + +To recreate these scripts: + +1. Create `scripts/local/` directory if it doesn't exist +2. Copy each code block above into the corresponding file in `scripts/local/` +3. On Linux/macOS, make `.sh` files executable: `chmod +x scripts/local/*.sh` diff --git a/scripts/enrich.sh b/scripts/enrich.sh deleted file mode 100644 index d97f368..0000000 --- a/scripts/enrich.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -# Content Intelligence Enrichment Script -# Triggered by Claude Code hooks to process the enrichment queue. -# Calls JavaDucker REST API to fetch pending artifacts, then uses Claude Code -# to classify, tag, extract salient points, and assess freshness. -# -# Usage: bash scripts/enrich.sh [--limit N] [--host HOST] [--port PORT] -set -e - -HOST="${JAVADUCKER_HOST:-localhost}" -PORT="${JAVADUCKER_PORT:-8080}" -LIMIT=10 -BASE_URL="http://${HOST}:${PORT}/api" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Parse args -while [[ $# -gt 0 ]]; do - case $1 in - --limit) LIMIT="$2"; shift 2 ;; - --host) HOST="$2"; BASE_URL="http://${HOST}:${PORT}/api"; shift 2 ;; - --port) PORT="$2"; BASE_URL="http://${HOST}:${PORT}/api"; shift 2 ;; - *) shift ;; - esac -done - -# Check if server is running -if ! curl -sf "${BASE_URL}/health" > /dev/null 2>&1; then - echo "[enrich] JavaDucker server not running at ${BASE_URL}, skipping enrichment" - exit 0 -fi - -# Fetch enrichment queue -QUEUE=$(curl -sf "${BASE_URL}/enrich-queue?limit=${LIMIT}") -COUNT=$(echo "$QUEUE" | jq -r '.count') - -if [ "$COUNT" = "0" ] || [ -z "$COUNT" ]; then - echo "[enrich] No artifacts pending enrichment" - exit 0 -fi - -echo "[enrich] Processing ${COUNT} artifacts from enrichment queue" - -# Process each artifact -echo "$QUEUE" | jq -r '.queue[].artifact_id' | while read -r ARTIFACT_ID; do - echo "[enrich] Processing artifact: ${ARTIFACT_ID}" - - # Fetch artifact text - TEXT_RESPONSE=$(curl -sf "${BASE_URL}/text/${ARTIFACT_ID}" 2>/dev/null || echo '{"error":"not found"}') - if echo "$TEXT_RESPONSE" | jq -e '.error' > /dev/null 2>&1; then - echo "[enrich] Skipping ${ARTIFACT_ID}: no text available" - continue - fi - - FILE_NAME=$(echo "$QUEUE" | jq -r --arg id "$ARTIFACT_ID" '.queue[] | select(.artifact_id == $id) | .file_name') - EXTRACTED_TEXT=$(echo "$TEXT_RESPONSE" | jq -r '.extracted_text') - - # Fetch related artifacts for freshness evaluation - RELATED=$(curl -sf "${BASE_URL}/related-by-concept/${ARTIFACT_ID}" 2>/dev/null || echo '{"related":[]}') - - # Build the enrichment prompt for Claude Code - # The actual LLM call happens when Claude Code processes this script's output - # For now, output the artifact info so the calling Claude Code session can enrich it - echo "[enrich] Artifact ready for enrichment:" - echo " ID: ${ARTIFACT_ID}" - echo " File: ${FILE_NAME}" - echo " Text length: ${#EXTRACTED_TEXT}" - echo " Related artifacts: $(echo "$RELATED" | jq -r '.count')" - - # Mark as enriching (claim the artifact) - curl -sf -X POST "${BASE_URL}/freshness" \ - -H "Content-Type: application/json" \ - -d "{\"artifactId\":\"${ARTIFACT_ID}\",\"freshness\":\"current\"}" > /dev/null 2>&1 || true - - # The actual classification, tagging, and point extraction is done by Claude Code - # using the MCP tools (javaducker_classify, javaducker_tag, javaducker_extract_points, etc.) - # This script provides the pipeline structure; Claude Code provides the intelligence. - echo "[enrich] Artifact ${ARTIFACT_ID} queued for Claude Code enrichment" -done - -echo "[enrich] Enrichment pass complete" diff --git a/scripts/index-sessions.sh b/scripts/index-sessions.sh deleted file mode 100644 index ebaf948..0000000 --- a/scripts/index-sessions.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# Index Claude Code session transcripts for the current project -set -euo pipefail - -JAVADUCKER_PORT="${HTTP_PORT:-8080}" -JAVADUCKER_HOST="${JAVADUCKER_HOST:-localhost}" -BASE_URL="http://${JAVADUCKER_HOST}:${JAVADUCKER_PORT}/api" - -# Find the project sessions directory -PROJECT_ROOT="${PROJECT_ROOT:-.}" -PROJECT_HASH=$(echo -n "$(cd "$PROJECT_ROOT" && pwd)" | md5sum | cut -d' ' -f1) -SESSIONS_DIR="$HOME/.claude/projects/${PROJECT_HASH}" - -if [ ! -d "$SESSIONS_DIR" ]; then - echo "No sessions directory found at $SESSIONS_DIR" - exit 0 -fi - -echo "Indexing sessions from $SESSIONS_DIR..." -curl -s -X POST "${BASE_URL}/index-sessions" \ - -H "Content-Type: application/json" \ - -d "{\"projectPath\": \"${SESSIONS_DIR}\", \"incremental\": true}" | jq . diff --git a/scripts/orchestrate.sh b/scripts/orchestrate.sh deleted file mode 100644 index 22a510b..0000000 --- a/scripts/orchestrate.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -# drom-flow orchestration script template -# Copy and customize this for your project's pipeline. -# -# Usage: -# ./scripts/orchestrate.sh [--iteration N] [--max N] [--check-only] -# -# Output: -# Writes JSON report to ./reports/iteration-N.json -# Exit 0 = all pass, Exit 1 = issues remain, Exit 2 = error - -set -euo pipefail - -# --- Configuration (customize these) --- -CHECK_CMD="echo 'Override CHECK_CMD with your test/check command'" -REPORT_DIR="./reports" -MAX_ITERATIONS=10 -# ---------------------------------------- - -# Parse arguments -ITERATION=1 -CHECK_ONLY=false -while [[ $# -gt 0 ]]; do - case $1 in - --iteration) ITERATION="$2"; shift 2 ;; - --max) MAX_ITERATIONS="$2"; shift 2 ;; - --check-only) CHECK_ONLY=true; shift ;; - *) echo "Unknown arg: $1"; exit 2 ;; - esac -done - -mkdir -p "$REPORT_DIR" - -run_check() { - local iter=$1 - local report="$REPORT_DIR/iteration-${iter}.json" - local start_time=$(date +%s) - - echo "[orchestrate] Iteration $iter — running check..." - - # Run the check command, capture output - local exit_code=0 - local output - output=$(eval "$CHECK_CMD" 2>&1) || exit_code=$? - - local end_time=$(date +%s) - local duration=$((end_time - start_time)) - - # Write report - cat > "$report" </dev/null || echo "\"$output\"") -} -EOF - - echo "[orchestrate] Report written to $report (exit code: $exit_code, ${duration}s)" - return $exit_code -} - -compare_iterations() { - local prev="$REPORT_DIR/iteration-$(($1 - 1)).json" - local curr="$REPORT_DIR/iteration-$1.json" - - if [ ! -f "$prev" ]; then - echo "[orchestrate] No previous iteration to compare" - return 0 - fi - - local prev_exit=$(python3 -c "import json; print(json.load(open('$prev'))['exitCode'])" 2>/dev/null || echo "1") - local curr_exit=$(python3 -c "import json; print(json.load(open('$curr'))['exitCode'])" 2>/dev/null || echo "1") - - echo "[orchestrate] Previous exit: $prev_exit → Current exit: $curr_exit" - - if [ "$curr_exit" -gt "$prev_exit" ]; then - echo "[orchestrate] WARNING: Possible regression detected" - return 1 - fi - return 0 -} - -# --- Main --- - -if [ "$CHECK_ONLY" = true ]; then - run_check "$ITERATION" - exit $? -fi - -echo "[orchestrate] Starting closed loop: iteration $ITERATION, max $MAX_ITERATIONS" - -while [ "$ITERATION" -le "$MAX_ITERATIONS" ]; do - if run_check "$ITERATION"; then - echo "[orchestrate] ALL CHECKS PASSED at iteration $ITERATION" - exit 0 - fi - - if [ "$ITERATION" -gt 1 ]; then - if ! compare_iterations "$ITERATION"; then - echo "[orchestrate] Regression at iteration $ITERATION — stopping for review" - exit 1 - fi - fi - - echo "[orchestrate] Issues remain. Report: $REPORT_DIR/iteration-${ITERATION}.json" - echo "[orchestrate] Waiting for fixes before next iteration..." - # Script exits here — Claude reads the report, spawns fix agents, - # then re-runs: ./scripts/orchestrate.sh --iteration $((ITERATION+1)) - exit 1 - -done - -echo "[orchestrate] Max iterations ($MAX_ITERATIONS) reached" -exit 1 diff --git a/start-here.md b/start-here.md new file mode 100644 index 0000000..487f7e6 --- /dev/null +++ b/start-here.md @@ -0,0 +1,74 @@ +# Start Here + +After cloning this repo, executable scripts are not included (corporate policy). Follow these steps to set up your local environment. + +## Prerequisites + +- **Java 21+** (OpenJDK recommended) +- **Maven 3.9+** +- **bash** (Linux/macOS/WSL) or **cmd** (Windows) +- **curl** and **jq** (for utility scripts) + +## 1. Recreate Local Scripts + +All script content is stored in `script-instructions/` as markdown files with embedded code blocks. Recreate them into the gitignored `scripts/local/` directory: + +```bash +mkdir -p scripts/local/hooks +``` + +Then copy the code blocks from each instruction file into the corresponding script file: + +| Instruction File | Scripts Created In | +|---|---| +| `script-instructions/run-scripts.md` | `scripts/local/run-*.sh`, `scripts/local/run-*.cmd` | +| `script-instructions/utility-scripts.md` | `scripts/local/enrich.sh`, `scripts/local/index-sessions.sh`, `scripts/local/orchestrate.sh`, `scripts/local/enrich-batch.ps1` | +| `script-instructions/hook-scripts.md` | `scripts/local/hooks/*.sh` (all Claude Code hooks) | + +Make scripts executable (Linux/macOS/WSL): + +```bash +chmod +x scripts/local/*.sh scripts/local/hooks/*.sh +``` + +## 2. Build the Project + +```bash +mvn clean package -DskipTests +``` + +## 3. Run + +**HTTP Server** (REST API + MCP): +```bash +bash scripts/local/run-server.sh +``` + +**MCP stdio mode** (for Claude Code integration): +```bash +bash scripts/local/run-mcp.sh +``` + +**CLI client**: +```bash +bash scripts/local/run-cli.sh +``` + +## 4. Claude Code Hooks + +The `.claude/settings.json` file references hook scripts in `scripts/local/hooks/`. These provide: + +- **memory-sync** — loads session memory and checks for in-progress plans on start +- **session-end** — reminds to persist progress +- **edit-log** — tracks file edits in JSONL +- **validate-plan** — validates drom-plans/ file structure +- **statusline** — git-aware status bar +- **track-agents** — counts background agents +- **javaducker-check** — JavaDucker lifecycle functions +- **javaducker-index** — auto-indexes edited files + +If hooks are missing, Claude Code will show warnings but still work. Recreate from `script-instructions/hook-scripts.md`. + +## 5. Why No Scripts in Git? + +Executable files (`.sh`, `.cmd`, `.ps1`) are excluded from version control to comply with corporate security policies that block repos containing binaries or executable scripts. The `script-instructions/` folder preserves the exact content so scripts can be recreated on any machine.