Skip to main content

skill-cite

Verify citation claims against Literature/ index and Zotero library. Invoke for /cite command.

Zur Installation springen

Quellinformationen

Repository
benbrastmckie/nvim
Letzte Quellaktivität
24. August 2026 um 22:21
Erkannte Sprache von SKILL.md
Englisch
Sterne
444
Forks
459

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
skill-cite
description
Verify citation claims against Literature/ index and Zotero library. Invoke for /cite command.
allowed-tools
Bash, Read, Write, Edit, AskUserQuestion
# Cite Skill (Direct Execution) Direct execution skill for verifying citation claims in task artifacts against the Literature/ index and Zotero library. Extracts citations, searches for source matches, scores confidence, presents findings interactively, and creates tasks for unverified claims. **Key behavior**: Users always see citation findings BEFORE any tasks are created. Users select which unverified/gap claims to address via interactive prompts. ## Context References Reference (do not load eagerly): - Path: `@specs/state.json` - Machine state - Path: `@specs/TODO.md` - Current task list - Path: `@specs/literature/index.json` - Literature index for keyword matching --- ## Execution ### Step 1: Parse Arguments Extract task number, optional description text, and flags from command input: ```bash # Parse from command input args="$ARGUMENTS" # Extract task number (first numeric argument) task_num=$(echo "$args" | grep -oP '^\s*\K\d+' | head -1) # Check for --gaps flag (also show gap items separately) show_gaps=false if echo "$args" | grep -q -- '--gaps'; then show_gaps=true fi # Check for optional description text (everything after flags/number) description_override=$(echo "$args" | sed 's/--[a-z-]*//g' | sed "s/$task_num//" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//') ``` If no task number is provided and no direct file path is given, report and exit: ``` ## Error: No Task Specified Usage: /cite N [--gaps] N Task number to verify citations for --gaps Also flag citations found but with no PDF source Example: /cite 42 /cite 42 --gaps ``` ### Step 2: Generate Session ID Generate session ID for tracking: ```bash source .claude/scripts/lib/common.sh session_id="$(common_session_id)" ``` ### Step 3: Locate Task Artifacts Read state.json to find the task slug and locate artifacts: ```bash project_root="$(pwd)" state_file="$project_root/specs/state.json" # Find task slug from state.json task_slug=$(jq -r --argjson num "$task_num" \ '.active_projects[] | select(.project_number == $num) | .project_name' \ "$state_file") # Also check archive if [ -z "$task_slug" ] || [ "$task_slug" = "null" ]; then task_slug=$(jq -r --argjson num "$task_num" \ '.archive[]? | select(.project_number == $num) | .project_name' \ "$state_file" 2>/dev/null) fi # Construct task directory path (zero-padded 3 digits) task_dir=$(printf "specs/%03d_%s" "$task_num" "$task_slug") if [ ! -d "$project_root/$task_dir" ]; then echo "Error: Task directory not found: $task_dir" exit 1 fi # Glob all artifact files (reports, plans, summaries) artifact_files=() while IFS= read -r -d '' f; do artifact_files+=("$f") done < <(find "$project_root/$task_dir" -name "*.md" -print0 2>/dev/null) if [ ${#artifact_files[@]} -eq 0 ]; then echo "No artifact files found in $task_dir" echo "Nothing to verify." exit 0 fi ``` ### Step 4: Extract Citations For each artifact file, run `cite-extract.sh` and aggregate results: ```bash script_dir="$project_root/.claude/extensions/literature/scripts" all_citations=() # JSON objects as strings total_found=0 for artifact_file in "${artifact_files[@]}"; do rel_path="${artifact_file#$project_root/}" # Run cite-extract.sh on the file raw_output=$("$script_dir/cite-extract.sh" --format=json "$artifact_file" 2>/dev/null) || { exit_code=$? # exit code 2 = no citations found (normal), anything else = error if [ $exit_code -ne 2 ]; then echo "Warning: cite-extract.sh failed for $rel_path (exit $exit_code)" >&2 fi continue } # Add source_file field to each result and accumulate # cite-extract.sh output schema: [{claim, source_text, line_number, confidence, pattern_type}] enriched=$(echo "$raw_output" | jq --arg file "$rel_path" \ '[.[] | . + {source_file: $file}]' 2>/dev/null) || continue count=$(echo "$enriched" | jq 'length' 2>/dev/null || echo 0) total_found=$((total_found + count)) # Append to all_citations (collect as JSON array strings to merge later) all_citations+=("$enriched") done # Merge all citation arrays into one if [ ${#all_citations[@]} -eq 0 ]; then combined_citations="[]" else combined_citations=$(printf '%s\n' "${all_citations[@]}" | jq -s 'add // []') fi ``` ### Step 5: Handle No Citations Found If no citations were extracted: ``` ## No Citations Found **Task**: #{N} — {task_slug} **Artifacts Scanned**: {count} files in {task_dir} No citation patterns detected across task artifacts. Patterns searched: author_year, parenthetical, phrase_attribution, theorem_attr, direct_quote, numeric_bracket, alpha_num_bracket, latex_cite ``` Exit gracefully without prompts. ### Step 6: Search Literature/ Index For each unique citation claim, extract query terms and search `specs/literature/index.json`: ```bash if [ -n "${LITERATURE_DIR:-}" ] && [ -d "$LITERATURE_DIR" ]; then lit_index="$LITERATURE_DIR/index.json" else lit_index="$project_root/specs/literature/index.json" fi index_available=false if [ -f "$lit_index" ]; then index_available=true fi # For each citation, extract key terms from source_text and match against index # index.json entry schema: {id, title, keywords[], file, summary} # Match strategy: count how many query terms appear in (title + keywords joined) score_against_index() { local source_text="$1" local query_terms # Extract significant words (strip common stop words, lowercase, split) query_terms=$(echo "$source_text" | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9 ]/ /g' \ | tr ' ' '\n' \ | grep -vE '^(a|an|the|in|on|at|of|to|for|and|or|by|as|is|are|was|were|be|that|this|from|with|it|its|et|al|pp|vol|no|doi)$' \ | grep -v '^[0-9]\{1,4\}$' \ | sort -u | tr '\n' ' ') if [ -z "$query_terms" ] || [ "$index_available" = "false" ]; then echo "0" return fi # Count term overlaps in title + keywords fields jq -r --arg terms "$query_terms" ' .entries // [] | map( (.title + " " + ((.keywords // []) | join(" "))) | ascii_downcase | . as $haystack | ($terms | split(" ") | map(select(length > 2)) | map(if (($haystack | test(.; "i")) // false) then 1 else 0 end) | add // 0) ) | max // 0 ' "$lit_index" 2>/dev/null || echo "0" } ``` ### Step 7: Search Zotero For each unique citation claim, search via `zotero-search.sh`: ```bash # Check if Zotero is configured (zotero-search.sh exits 1 if library not found) zotero_available=false if "$script_dir/zotero-search.sh" --limit=1 --format=json "test" &>/dev/null; then zotero_available=true elif [ $? -eq 2 ]; then # exit 2 = no results (library exists, query returned nothing) zotero_available=true fi search_zotero() { local source_text="$1" if [ "$zotero_available" = "false" ]; then echo "[]" return fi # zotero-search.sh accepts query terms as positional args # Output schema: [{citation_key, title, authors, year, score, pdf_paths, abstract_snippet}] local query_words query_words=$(echo "$source_text" | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9 ]/ /g' \ | tr ' ' '\n' \ | grep -vE '^(a|an|the|in|on|at|of|to|for|and|or|by|as|is|et|al|pp)$' \ | grep -v '^[0-9]\{1,4\}$' \ | sort -u | head -8 | tr '\n' ' ') if [ -z "$query_words" ]; then echo "[]" return fi # shellcheck disable=SC2086 "$script_dir/zotero-search.sh" --limit=5 --format=json $query_words 2>/dev/null \ || echo "[]" } ``` ### Step 8: Score Confidence Apply scoring to each extracted citation and classify it: **Scoring thresholds** (based on search results): - **confirmed**: Zotero top result score >= 3 OR index keyword overlap >= 2 - **partial**: Zotero top result score 1–2 OR index keyword overlap == 1 - **unconfirmed**: No match in either Zotero or Literature/ index - **gap**: Citation pattern found but source text suggests a specific work that exists in index/Zotero yet has no associated PDF ```bash score_citation() { local source_text="$1" local pattern_type="$2" local extract_confidence="$3" # from cite-extract.sh (0.5–0.9) # Get index overlap score local index_overlap index_overlap=$(score_against_index "$source_text") # Get Zotero results local zotero_results zotero_top_score zotero_results=$(search_zotero "$source_text") zotero_top_score=$(echo "$zotero_results" | jq '.[0].score // 0' 2>/dev/null || echo 0) # Classify local status best_match if [ "$zotero_top_score" -ge 3 ] 2>/dev/null || [ "$index_overlap" -ge 2 ] 2>/dev/null; then status="confirmed" best_match=$(echo "$zotero_results" | jq -r '.[0] | "\(.authors[0] // "Unknown") (\(.year // "n.d.")). \(.title)"' 2>/dev/null \ || echo "Literature index match (overlap: $index_overlap)") elif [ "$zotero_top_score" -ge 1 ] 2>/dev/null || [ "$index_overlap" -ge 1 ] 2>/dev/null; then status="partial" best_match=$(echo "$zotero_results" | jq -r '.[0] | "\(.authors[0] // "Unknown") (\(.year // "n.d.")). \(.title)"' 2>/dev/null \ || echo "Weak literature index match (overlap: $index_overlap)") else status="unconfirmed" best_match="No match found" fi # Check for gap: pattern found but PDF unavailable if [ "$status" != "unconfirmed" ] && [ "$show_gaps" = "true" ]; then local pdf_count pdf_count=$(echo "$zotero_results" | jq '.[0].pdf_paths // [] | length' 2>/dev/null || echo 0) if [ "$pdf_count" -eq 0 ] && [ "$zotero_top_score" -ge 1 ] 2>/dev/null; then status="gap" fi fi
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen