Coverage for little_loops / cli / issues / fingerprint.py: 0%
32 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-15 17:27 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-15 17:27 -0500
1"""ll-issues fingerprint: extract structured fingerprint from an issue file."""
3from __future__ import annotations
5import argparse
6import json
7import re
8import sys
9from pathlib import Path
10from typing import TYPE_CHECKING
12if TYPE_CHECKING:
13 from little_loops.config import BRConfig
16def cmd_fingerprint(config: BRConfig, args: argparse.Namespace) -> int:
17 """Extract structured fingerprint (id, files_to_modify, key_terms) from an issue file.
19 Reads the issue file and outputs JSON with:
20 - id: issue ID from frontmatter (falls back to filename parsing)
21 - files_to_modify: sorted list of file paths from the Integration Map section
22 - key_terms: sorted list of significant words after stop-word filtering
24 Args:
25 config: Project configuration.
26 args: Parsed arguments with .issue_path.
28 Returns:
29 0 on success, 1 on error.
30 """
31 from little_loops.parallel.file_hints import extract_file_hints
32 from little_loops.text_utils import extract_words
34 issue_path = Path(args.issue_path)
35 if not issue_path.is_absolute():
36 issue_path = config.project_root / issue_path
38 if not issue_path.is_file():
39 print(f"Error: issue file not found: {issue_path}", file=sys.stderr)
40 return 1
42 content = issue_path.read_text()
44 # Extract issue ID from frontmatter
45 issue_id = ""
46 fm_match = re.search(r"^---\n(.*?)^---", content, re.MULTILINE | re.DOTALL)
47 if fm_match:
48 id_match = re.search(r"^id:\s*(\S+)", fm_match.group(1), re.MULTILINE)
49 if id_match:
50 issue_id = id_match.group(1)
51 if not issue_id:
52 # Fallback: parse from filename (P3-ENH-1801-title -> ENH-1801)
53 stem = issue_path.stem
54 parts = stem.split("-")
55 issue_id = "-".join(parts[1:3]) if len(parts) >= 3 else stem
57 hints = extract_file_hints(content, issue_id=issue_id)
58 key_terms = extract_words(content)
60 result = {
61 "id": issue_id,
62 "files_to_modify": sorted(hints.files),
63 "key_terms": sorted(key_terms),
64 }
66 print(json.dumps(result, indent=2))
67 return 0