cmd refactor

quietlight
May 19, 2026, 3:58 AM
YF5AJWFBEKNWN556IXJCCLMHPOYNQM2IMNBIGQ3ABI6IR5NXCV7QC

Dependencies

Change contents

  • replacement in cmd/xxhash.go at line 24
    [3.1037966][3.1037966:1038406]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak xxhash --file <path>\n\n")
    fmt.Fprintf(os.Stderr, "Compute XXH64 hash of a file (same format stored in database).\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak xxhash --file recording.wav\n")
    fmt.Fprintf(os.Stderr, " skraak xxhash --file /path/to/audio.wav | jq '.hash'\n")
    }
    [3.1037966]
    [3.1038406]
    fs.Usage = usagePrinter(fs,
    "skraak xxhash --file <path>",
    "Compute XXH64 hash of a file (same format stored in database).",
    "skraak xxhash --file recording.wav",
    "skraak xxhash --file /path/to/audio.wav | jq '.hash'",
    )
  • replacement in cmd/xxhash.go at line 35
    [3.1038464][3.1038464:1038486](),[3.1038544][3.1038544:1038557](),[3.1038557][3.2008:2050]()
    if *filePath == "" {
    fs.Usage()
    return fmt.Errorf("--file is required")
    [3.1038464]
    [3.1038570]
    if err := requireFlags(fs, map[string]any{"--file": *filePath}); err != nil {
    return err
  • replacement in cmd/time.go at line 25
    [3.1040959][3.1040959:1041261]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak time\n\n")
    fmt.Fprintf(os.Stderr, "Get the current system time with timezone information.\n\n")
    fmt.Fprintf(os.Stderr, "Examples:\n")
    fmt.Fprintf(os.Stderr, " skraak time\n")
    fmt.Fprintf(os.Stderr, " skraak time | jq '.iso'\n")
    }
    [3.1040959]
    [3.1041261]
    fs.Usage = usagePrinter(fs,
    "skraak time",
    "Get the current system time with timezone information.",
    "skraak time",
    "skraak time | jq '.iso'",
    )
  • replacement in cmd/sql.go at line 32
    [3.1042695][3.1042695:1043203]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak sql --db <path> [options] <query>\n\n")
    fmt.Fprintf(os.Stderr, "Execute a SQL SELECT query against the database.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak sql --db ./db/skraak.duckdb \"SELECT COUNT(*) FROM file WHERE active = true\"\n")
    fmt.Fprintf(os.Stderr, " skraak sql --db ./db/skraak.duckdb --limit 10 \"SELECT * FROM dataset\"\n")
    }
    [3.1042695]
    [3.1043203]
    fs.Usage = usagePrinter(fs,
    "skraak sql --db <path> [options] <query>",
    "Execute a SQL SELECT query against the database.",
    "skraak sql --db ./db/skraak.duckdb \"SELECT COUNT(*) FROM file WHERE active = true\"",
    "skraak sql --db ./db/skraak.duckdb --limit 10 \"SELECT * FROM dataset\"",
    )
  • replacement in cmd/sql.go at line 43
    [3.1043261][3.1043261:1043281](),[3.1043337][3.1043337:1043350](),[3.1043350][3.2742:2782]()
    if *dbPath == "" {
    fs.Usage()
    return fmt.Errorf("--db is required")
    [3.1043261]
    [3.1043363]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath}); err != nil {
    return err
  • replacement in cmd/replay.go at line 52
    [3.1045642][3.1045642:1046207]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak replay events [options]\n\n")
    fmt.Fprintf(os.Stderr, "Replay event log into database.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak replay events --db ./backup.duckdb --log ./events.jsonl\n")
    fmt.Fprintf(os.Stderr, " skraak replay events --db ./backup.duckdb --log ./events.jsonl --dry-run\n")
    fmt.Fprintf(os.Stderr, " skraak replay events --db ./backup.duckdb --log ./events.jsonl --last 10\n")
    }
    [3.1045642]
    [3.1046207]
    fs.Usage = usagePrinter(fs,
    "skraak replay events [options]",
    "Replay event log into database.",
    "skraak replay events --db ./backup.duckdb --log ./events.jsonl",
    "skraak replay events --db ./backup.duckdb --log ./events.jsonl --dry-run",
    "skraak replay events --db ./backup.duckdb --log ./events.jsonl --last 10",
    )
  • replacement in cmd/replay.go at line 64
    [3.1046265][3.3218:3294]()
    if err := checkFlags(fs, "--db", *dbPath, "--log", *logPath); err != nil {
    [3.1046265]
    [3.3294]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--log": *logPath}); err != nil {
  • replacement in cmd/prepend.go at line 38
    [3.1052277][3.1052277:1053160]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak prepend --folder <path> --prefix <string> [--recursive] [--dry-run]\n\n")
    fmt.Fprintf(os.Stderr, "Rename files by prepending a prefix.\n\n")
    fmt.Fprintf(os.Stderr, "Target files:\n")
    fmt.Fprintf(os.Stderr, " - *.wav, *.WAV (must start with datestring YYYYMMDD_HHMMSS)\n")
    fmt.Fprintf(os.Stderr, " - *.wav.data, *.WAV.data (must start with datestring YYYYMMDD_HHMMSS)\n")
    fmt.Fprintf(os.Stderr, " - log.txt (exact name, always renamed)\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak prepend --folder ./recordings --prefix LOC001\n")
    fmt.Fprintf(os.Stderr, " skraak prepend --folder ./data --prefix SITE_A --recursive\n")
    fmt.Fprintf(os.Stderr, " skraak prepend --folder ./test --prefix TEST --dry-run\n")
    }
    [3.1052277]
    [3.1053160]
    fs.Usage = usagePrinter(fs,
    "skraak prepend --folder <path> --prefix <string> [--recursive] [--dry-run]",
    "Rename files by prepending a prefix.\n\n"+
    "Target files:\n"+
    " - *.wav, *.WAV (must start with datestring YYYYMMDD_HHMMSS)\n"+
    " - *.wav.data, *.WAV.data (must start with datestring YYYYMMDD_HHMMSS)\n"+
    " - log.txt (exact name, always renamed)",
    "skraak prepend --folder ./recordings --prefix LOC001",
    "skraak prepend --folder ./data --prefix SITE_A --recursive",
    "skraak prepend --folder ./test --prefix TEST --dry-run",
    )
  • replacement in cmd/prepend.go at line 54
    [3.1053218][3.1053218:1053238](),[3.1053298][3.1053298:1053311](),[3.1053311][3.3671:3715](),[3.3715][3.1053324:1053327](),[3.1053324][3.1053324:1053327](),[3.1053328][3.1053328:1053348](),[3.1053408][3.1053408:1053421](),[3.1053421][3.3716:3760]()
    if *folder == "" {
    fs.Usage()
    return fmt.Errorf("--folder is required")
    }
    if *prefix == "" {
    fs.Usage()
    return fmt.Errorf("--prefix is required")
    [3.1053218]
    [3.1053434]
    if err := requireFlags(fs, map[string]any{"--folder": *folder, "--prefix": *prefix}); err != nil {
    return err
  • edit in cmd/pattern.go at line 7
    [3.1053980][3.1053980:1053986]()
    "os"
  • replacement in cmd/pattern.go at line 33
    [3.1054922][3.1054922:1055375]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak pattern create [options]\n\n")
    fmt.Fprintf(os.Stderr, "Create a new cyclic recording pattern.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740\n")
    fmt.Fprintf(os.Stderr, " # Creates 60s record / 1740s sleep = 30 min cycle\n")
    }
    [3.1054922]
    [3.1055375]
    fs.Usage = usagePrinter(fs,
    "skraak pattern create [options]",
    "Create a new cyclic recording pattern.",
    "skraak pattern create --db ./db/skraak.duckdb --record 60 --sleep 1740",
    "# Creates 60s record / 1740s sleep = 30 min cycle",
    )
  • replacement in cmd/pattern.go at line 44
    [3.1055433][3.1055433:1055461](),[3.1055461][3.3968:4074](),[3.4074][3.3011:3141](),[3.3011][3.3011:3141](),[3.3141][3.4075:4092]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath); err != nil {
    return err
    }
    if err := checkNonZeroFlags(fs,
    struct {
    Name string
    Value int
    }{"--record", *record},
    struct {
    Name string
    Value int
    }{"--sleep", *sleep},
    ); err != nil {
    [3.1055433]
    [3.4092]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--record": *record,
    "--sleep": *sleep,
    }); err != nil {
  • replacement in cmd/pattern.go at line 78
    [3.1056602][3.1056602:1057009]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak pattern update [options]\n\n")
    fmt.Fprintf(os.Stderr, "Update an existing recording pattern. Only provided fields are updated.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30\n")
    }
    [3.1056602]
    [3.1057009]
    fs.Usage = usagePrinter(fs,
    "skraak pattern update [options]",
    "Update an existing recording pattern. Only provided fields are updated.",
    "skraak pattern update --db ./db/skraak.duckdb --id pattern123 --record 30",
    )
  • replacement in cmd/pattern.go at line 88
    [3.1057067][3.4265:4335]()
    if err := checkFlags(fs, "--db", *dbPath, "--id", *id); err != nil {
    [3.1057067]
    [3.4335]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--id": *id}); err != nil {
  • replacement in cmd/metadata.go at line 30
    [3.1059066][3.1059066:1059502]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak metadata --file <path>\n\n")
    fmt.Fprintf(os.Stderr, "Extract metadata from a WAV file header.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak metadata --file recording.wav\n")
    fmt.Fprintf(os.Stderr, " skraak metadata --file /path/to/audio.wav | jq '.duration_seconds'\n")
    }
    [3.1059066]
    [3.1059502]
    fs.Usage = usagePrinter(fs,
    "skraak metadata --file <path>",
    "Extract metadata from a WAV file header.",
    "skraak metadata --file recording.wav",
    "skraak metadata --file /path/to/audio.wav | jq '.duration_seconds'",
    )
  • replacement in cmd/metadata.go at line 41
    [3.1059560][3.1059560:1059582](),[3.1059640][3.1059640:1059653](),[3.1059653][3.4613:4655]()
    if *filePath == "" {
    fs.Usage()
    return fmt.Errorf("--file is required")
    [3.1059560]
    [3.1059666]
    if err := requireFlags(fs, map[string]any{"--file": *filePath}); err != nil {
    return err
  • edit in cmd/location.go at line 7
    [3.1060644][3.1060644:1060650]()
    "os"
  • replacement in cmd/location.go at line 41
    [3.1062082][3.1062082:1062524]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak location create [options]\n\n")
    fmt.Fprintf(os.Stderr, "Create a new location with GPS coordinates.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak location create --db ./db/skraak.duckdb --dataset abc123 --name \"Site A\" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland\n")
    }
    [3.1062082]
    [3.1062524]
    fs.Usage = usagePrinter(fs,
    "skraak location create [options]",
    "Create a new location with GPS coordinates.",
    "skraak location create --db ./db/skraak.duckdb --dataset abc123 --name \"Site A\" --lat -36.85 --lon 174.76 --timezone Pacific/Auckland",
    )
  • replacement in cmd/location.go at line 51
    [3.1062582][3.4865:5013]()
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--name", *name, "--lat", *lat, "--lon", *lon, "--timezone", *tz); err != nil {
    [3.1062582]
    [3.5013]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--name": *name,
    "--lat": *lat,
    "--lon": *lon,
    "--timezone": *tz,
    }); err != nil {
  • replacement in cmd/location.go at line 104
    [3.1064497][3.1064497:1064901]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak location update [options]\n\n")
    fmt.Fprintf(os.Stderr, "Update an existing location. Only provided fields are updated.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak location update --db ./db/skraak.duckdb --id loc123 --name \"New Name\"\n")
    }
    [3.1064497]
    [3.1064901]
    fs.Usage = usagePrinter(fs,
    "skraak location update [options]",
    "Update an existing location. Only provided fields are updated.",
    "skraak location update --db ./db/skraak.duckdb --id loc123 --name \"New Name\"",
    )
  • replacement in cmd/location.go at line 114
    [3.1064959][3.5289:5359]()
    if err := checkFlags(fs, "--db", *dbPath, "--id", *id); err != nil {
    [3.1064959]
    [3.5359]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--id": *id}); err != nil {
  • replacement in cmd/isnight.go at line 46
    [3.1068168][3.1068168:1069083]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak isnight --file <path> --lat <lat> --lng <lng> [--timezone <tz>] [--brief]\n\n")
    fmt.Fprintf(os.Stderr, "Determine if a WAV file was recorded at night based on file metadata and GPS coordinates.\n\n")
    fmt.Fprintf(os.Stderr, "Uses the recording midpoint (not start time) for astronomical calculations.\n")
    fmt.Fprintf(os.Stderr, "Timestamp resolution: AudioMoth comment → filename → file modification time.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak isnight --file recording.wav --lat -36.85 --lng 174.76\n")
    fmt.Fprintf(os.Stderr, " skraak isnight --file recording.wav --lat -36.85 --lng 174.76 --timezone Pacific/Auckland\n")
    fmt.Fprintf(os.Stderr, " skraak isnight --file recording.wav --lat 51.51 --lng -0.13 | jq '.solar_night'\n")
    }
    [3.1068168]
    [3.1069083]
    fs.Usage = usagePrinter(fs,
    "skraak isnight --file <path> --lat <lat> --lng <lng> [--timezone <tz>] [--brief]",
    "Determine if a WAV file was recorded at night based on file metadata and GPS coordinates.\n\n"+
    "Uses the recording midpoint (not start time) for astronomical calculations.\n"+
    "Timestamp resolution: AudioMoth comment → filename → file modification time.",
    "skraak isnight --file recording.wav --lat -36.85 --lng 174.76",
    "skraak isnight --file recording.wav --lat -36.85 --lng 174.76 --timezone Pacific/Auckland",
    "skraak isnight --file recording.wav --lat 51.51 --lng -0.13 | jq '.solar_night'",
    )
  • replacement in cmd/isnight.go at line 60
    [3.1069141][3.1069141:1069163](),[3.1069221][3.1069221:1069234](),[3.1069234][3.5931:5973]()
    if *filePath == "" {
    fs.Usage()
    return fmt.Errorf("--file is required")
    [3.1069141]
    [3.1069247]
    if err := requireFlags(fs, map[string]any{"--file": *filePath}); err != nil {
    return err
  • replacement in cmd/import.go at line 75
    [3.1073108][3.1073108:1073555]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak import bulk [options]\n\n")
    fmt.Fprintf(os.Stderr, "Bulk import WAV files across multiple locations/clusters using a CSV file.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nCSV format: location_name,location_id,directory_path,date_range,sample_rate,file_count\n")
    fmt.Fprintf(os.Stderr, "\nMonitor progress: tail -f <log-file>\n")
    }
    [3.1073108]
    [3.1073555]
    fs.Usage = usagePrinter(fs,
    "skraak import bulk [options]",
    "Bulk import WAV files across multiple locations/clusters using a CSV file.\n\n"+
    "CSV format: location_name,location_id,directory_path,date_range,sample_rate,file_count\n\n"+
    "Monitor progress: tail -f <log-file>",
    )
  • replacement in cmd/import.go at line 86
    [3.1073613][3.1073613:1073641](),[3.1073641][3.615:735]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--csv", *csvPath, "--log", *logPath); err != nil {
    [3.1073613]
    [3.735]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--csv": *csvPath,
    "--log": *logPath,
    }); err != nil {
  • replacement in cmd/import.go at line 149
    [3.1076027][3.1076027:1076451]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak import file [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import a single WAV file into the database.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --file /path/to/file.wav\n")
    }
    [3.1076027]
    [3.1076451]
    fs.Usage = usagePrinter(fs,
    "skraak import file [options]",
    "Import a single WAV file into the database.",
    "skraak import file --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --file /path/to/file.wav",
    )
  • replacement in cmd/import.go at line 159
    [3.1076509][3.1076509:1076537](),[3.1076537][3.944:1099]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--location", *locationID, "--cluster", *clusterID, "--file", *filePath); err != nil {
    [3.1076509]
    [3.1099]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--location": *locationID,
    "--cluster": *clusterID,
    "--file": *filePath,
    }); err != nil {
  • replacement in cmd/import.go at line 217
    [3.1078768][3.1078768:1079206]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak import folder [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import all WAV files from a folder into the database.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --folder /path/to/folder\n")
    }
    [3.1078768]
    [3.1079206]
    fs.Usage = usagePrinter(fs,
    "skraak import folder [options]",
    "Import all WAV files from a folder into the database.",
    "skraak import folder --db ./db/skraak.duckdb --dataset abc123 --location loc456 --cluster clust789 --folder /path/to/folder",
    )
  • replacement in cmd/import.go at line 227
    [3.1079264][3.1079264:1079292](),[3.1079292][3.1284:1443]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--location", *locationID, "--cluster", *clusterID, "--folder", *folderPath); err != nil {
    [3.1079264]
    [3.1443]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--location": *locationID,
    "--cluster": *clusterID,
    "--folder": *folderPath,
    }); err != nil {
  • replacement in cmd/import.go at line 312
    [3.1082555][3.1082555:1083840]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak import segments [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import segments from AviaNZ .data files into the database.\n")
    fmt.Fprintf(os.Stderr, "Applies species/calltype mapping from JSON file.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nMapping file format:\n")
    fmt.Fprintf(os.Stderr, " {\n")
    fmt.Fprintf(os.Stderr, " \"GSK\": {\"species\": \"Roroa\", \"calltypes\": {\"Male\": \"Male - Solo\"}},\n")
    fmt.Fprintf(os.Stderr, " \"Don't Know\": {\"species\": \"Don't Know\"}\n")
    fmt.Fprintf(os.Stderr, " }\n")
    fmt.Fprintf(os.Stderr, "\nInvariants:\n")
    fmt.Fprintf(os.Stderr, " - All file hashes must already exist in database for the cluster\n")
    fmt.Fprintf(os.Stderr, " - All files must have no existing labels (fresh imports only)\n")
    fmt.Fprintf(os.Stderr, " - All filters, species, and calltypes must exist in database\n")
    fmt.Fprintf(os.Stderr, " - Bookmark flags are ignored (not stored in database)\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak import segments --db ./db/skraak.duckdb --dataset dset_id123 --location loc_id456 --cluster clust_id789 --folder /path/to/data --mapping mapping.json\n")
    }
    [3.1082555]
    [3.1083840]
    fs.Usage = usagePrinter(fs,
    "skraak import segments [options]",
    "Import segments from AviaNZ .data files into the database.\n"+
    "Applies species/calltype mapping from JSON file.\n\n"+
    "Mapping file format:\n"+
    " {\n"+
    " \"GSK\": {\"species\": \"Roroa\", \"calltypes\": {\"Male\": \"Male - Solo\"}},\n"+
    " \"Don't Know\": {\"species\": \"Don't Know\"}\n"+
    " }\n\n"+
    "Invariants:\n"+
    " - All file hashes must already exist in database for the cluster\n"+
    " - All files must have no existing labels (fresh imports only)\n"+
    " - All filters, species, and calltypes must exist in database\n"+
    " - Bookmark flags are ignored (not stored in database)",
    "skraak import segments --db ./db/skraak.duckdb --dataset dset_id123 --location loc_id456 --cluster clust_id789 --folder /path/to/data --mapping mapping.json",
    )
  • replacement in cmd/import.go at line 333
    [3.1083898][3.1083898:1083926](),[3.1083926][3.1658:1844]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--location", *locationID, "--cluster", *clusterID, "--folder", *folderPath, "--mapping", *mappingPath); err != nil {
    [3.1083898]
    [3.1844]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--location": *locationID,
    "--cluster": *clusterID,
    "--folder": *folderPath,
    "--mapping": *mappingPath,
    }); err != nil {
  • replacement in cmd/import.go at line 408
    [3.1086938][3.1086938:1087681]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak import unstructured [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import WAV files into an unstructured dataset.\n")
    fmt.Fprintf(os.Stderr, "Files are stored with minimal metadata (hash, duration, sample_rate, file modification time).\n")
    fmt.Fprintf(os.Stderr, "No location/cluster hierarchy required.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak import unstructured --db ./db/skraak.duckdb --dataset abc123 --folder /path/to/folder\n")
    fmt.Fprintf(os.Stderr, " skraak import unstructured --db ./db/skraak.duckdb --dataset abc123 --folder /path/to/folder --recursive=false\n")
    }
    [3.1086938]
    [3.1087681]
    fs.Usage = usagePrinter(fs,
    "skraak import unstructured [options]",
    "Import WAV files into an unstructured dataset.\n"+
    "Files are stored with minimal metadata (hash, duration, sample_rate, file modification time).\n"+
    "No location/cluster hierarchy required.",
    "skraak import unstructured --db ./db/skraak.duckdb --dataset abc123 --folder /path/to/folder",
    "skraak import unstructured --db ./db/skraak.duckdb --dataset abc123 --folder /path/to/folder --recursive=false",
    )
  • replacement in cmd/import.go at line 421
    [3.1087739][3.1087739:1087767](),[3.1087767][3.2097:2204]()
    // Validate required flags
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--folder", *folderPath); err != nil {
    [3.1087739]
    [3.2204]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--folder": *folderPath,
    }); err != nil {
  • replacement in cmd/export.go at line 58
    [3.1090875][3.1090875:1091572]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak export dataset --db <path> --id <dataset_id> --output <path> [options]\n\n")
    fmt.Fprintf(os.Stderr, "Export a dataset with all related data to a new DuckDB database.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb\n")
    fmt.Fprintf(os.Stderr, " skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb --dry-run\n")
    fmt.Fprintf(os.Stderr, " skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb --force\n")
    }
    [3.1090875]
    [3.1091572]
    fs.Usage = usagePrinter(fs,
    "skraak export dataset --db <path> --id <dataset_id> --output <path> [options]",
    "Export a dataset with all related data to a new DuckDB database.",
    "skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb",
    "skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb --dry-run",
    "skraak export dataset --db ./db/skraak.duckdb --id abc123 --output export.duckdb --force",
    )
  • replacement in cmd/export.go at line 70
    [3.1091630][3.6759:6857]()
    if err := checkFlags(fs, "--db", *dbPath, "--id", *datasetID, "--output", *output); err != nil {
    [3.1091630]
    [3.6857]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--id": *datasetID, "--output": *output}); err != nil {
  • edit in cmd/dataset.go at line 7
    [3.1092574][3.1092574:1092580]()
    "os"
  • replacement in cmd/dataset.go at line 34
    [3.1093645][3.1093645:1094146]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak create dataset [options]\n\n")
    fmt.Fprintf(os.Stderr, "Create a new dataset.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak create dataset --db ./db/skraak.duckdb --name \"My Dataset\"\n")
    fmt.Fprintf(os.Stderr, " skraak create dataset --db ./db/skraak.duckdb --name \"Training Data\" --type train --description \"For ML training\"\n")
    }
    [3.1093645]
    [3.1094146]
    fs.Usage = usagePrinter(fs,
    "skraak create dataset [options]",
    "Create a new dataset.",
    "skraak create dataset --db ./db/skraak.duckdb --name \"My Dataset\"",
    "skraak create dataset --db ./db/skraak.duckdb --name \"Training Data\" --type train --description \"For ML training\"",
    )
  • replacement in cmd/dataset.go at line 45
    [3.1094204][3.7081:7155]()
    if err := checkFlags(fs, "--db", *dbPath, "--name", *name); err != nil {
    [3.1094204]
    [3.7155]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--name": *name}); err != nil {
  • replacement in cmd/dataset.go at line 77
    [3.1095375][3.1095375:1095883]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak update dataset [options]\n\n")
    fmt.Fprintf(os.Stderr, "Update an existing dataset. Only provided fields are updated.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak update dataset --db ./db/skraak.duckdb --id abc123 --name \"Updated Name\"\n")
    fmt.Fprintf(os.Stderr, " skraak update dataset --db ./db/skraak.duckdb --id abc123 --type train\n")
    }
    [3.1095375]
    [3.1095883]
    fs.Usage = usagePrinter(fs,
    "skraak update dataset [options]",
    "Update an existing dataset. Only provided fields are updated.",
    "skraak update dataset --db ./db/skraak.duckdb --id abc123 --name \"Updated Name\"",
    "skraak update dataset --db ./db/skraak.duckdb --id abc123 --type train",
    )
  • replacement in cmd/dataset.go at line 88
    [3.1095941][3.7328:7398]()
    if err := checkFlags(fs, "--db", *dbPath, "--id", *id); err != nil {
    [3.1095941]
    [3.7398]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--id": *id}); err != nil {
  • replacement in cmd/common_test.go at line 17
    [2.13913][2.13913:13949]()
    func TestCheckFlags(t *testing.T) {
    [2.13913]
    [2.13949]
    func TestRequireFlags(t *testing.T) {
  • replacement in cmd/common_test.go at line 20
    [2.13991][2.13991:14014]()
    pairs []string
    [2.13991]
    [2.14014]
    required map[string]any
  • replacement in cmd/common_test.go at line 24
    [2.14060][2.14060:14418]()
    {"all present", []string{"--db", "x.duckdb", "--id", "abc"}, false, nil},
    {"empty value", []string{"--db", "", "--id", "abc"}, true, []string{"--db"}},
    {"multiple missing", []string{"--db", "", "--id", ""}, true, []string{"--db", "--id"}},
    {"no pairs", []string{}, false, nil},
    {"single missing", []string{"--name", ""}, true, []string{"--name"}},
    [2.14060]
    [2.14418]
    {"all string present", map[string]any{"--db": "x.duckdb", "--id": "abc"}, false, nil},
    {"empty string", map[string]any{"--db": "", "--id": "abc"}, true, []string{"--db"}},
    {"multiple missing strings", map[string]any{"--db": "", "--id": ""}, true, []string{"--db", "--id"}},
    {"no flags", map[string]any{}, false, nil},
    {"all int non-zero", map[string]any{"--n": 5, "--m": 1}, false, nil},
    {"zero int", map[string]any{"--n": 0, "--m": 1}, true, []string{"--n"}},
    {"multiple zero ints", map[string]any{"--n": 0, "--m": 0}, true, []string{"--m", "--n"}},
    {"negative int is allowed", map[string]any{"--n": -1}, false, nil},
    {"mixed types", map[string]any{"--db": "x", "--n": 0}, true, []string{"--n"}},
  • replacement in cmd/common_test.go at line 36
    [2.14487][2.14487:14538]()
    err := checkFlags(silentFlagSet(), tt.pairs...)
    [2.14487]
    [2.14538]
    err := requireFlags(silentFlagSet(), tt.required)
  • replacement in cmd/common_test.go at line 51
    [2.14832][2.14832:14924]()
    func TestCheckNonZeroFlags(t *testing.T) {
    type pair = struct {
    Name string
    Value int
    [2.14832]
    [2.14924]
    func TestRequireFlagsUnsupportedType(t *testing.T) {
    err := requireFlags(silentFlagSet(), map[string]any{"--bad": 1.5})
    if err == nil || !strings.Contains(err.Error(), "unsupported type") {
    t.Fatalf("expected unsupported type error, got %v", err)
  • edit in cmd/common_test.go at line 56
    [2.14927][2.14927:15770]()
    tests := []struct {
    name string
    pairs []pair
    wantErr bool
    wantMissing []string
    }{
    {"all non-zero", []pair{{"--n", 5}, {"--m", 1}}, false, nil},
    {"zero value", []pair{{"--n", 0}, {"--m", 1}}, true, []string{"--n"}},
    {"multiple zero", []pair{{"--n", 0}, {"--m", 0}}, true, []string{"--n", "--m"}},
    {"empty pairs", nil, false, nil},
    {"negative is allowed", []pair{{"--n", -1}}, false, nil},
    }
    for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
    err := checkNonZeroFlags(silentFlagSet(), tt.pairs...)
    if (err != nil) != tt.wantErr {
    t.Fatalf("err=%v, wantErr=%v", err, tt.wantErr)
    }
    if tt.wantErr {
    for _, name := range tt.wantMissing {
    if !strings.Contains(err.Error(), name) {
    t.Errorf("err %q missing flag name %q", err.Error(), name)
    }
    }
    }
    })
    }
  • edit in cmd/common.go at line 7
    [3.1098355]
    [3.1098355]
    "sort"
  • replacement in cmd/common.go at line 26
    [3.4189][3.7783:8067]()
    // checkFlags checks that the given flag values are non-empty strings.
    // Returns an error if any are empty (does not call os.Exit).
    // Each pair is (flagName, flagValue) — e.g. checkFlags(fs, "--db", *dbPath, "--id", *id)
    func checkFlags(fs *flag.FlagSet, pairs ...string) error {
    [3.4189]
    [3.4470]
    // requireFlags reports an error listing any required flags whose values are
    // still at their zero value. Strings must be non-empty; ints must be non-zero.
    // Map iteration is randomised, so the returned list is sorted for stability.
    func requireFlags(fs *flag.FlagSet, required map[string]any) error {
  • replacement in cmd/common.go at line 31
    [3.4492][3.4492:4593]()
    for i := 0; i < len(pairs); i += 2 {
    if pairs[i+1] == "" {
    missing = append(missing, pairs[i])
    [3.4492]
    [3.4593]
    for name, v := range required {
    switch x := v.(type) {
    case string:
    if x == "" {
    missing = append(missing, name)
    }
    case int:
    if x == 0 {
    missing = append(missing, name)
    }
    default:
    return fmt.Errorf("requireFlags: unsupported type for %s: %T", name, v)
  • edit in cmd/common.go at line 46
    [3.4623]
    [3.4698]
    sort.Strings(missing)
  • replacement in cmd/common.go at line 53
    [3.4730][3.8141:8334](),[3.8334][3.4958:4983](),[3.4958][3.4958:4983](),[3.4983][3.8335:8346](),[3.8346][3.4988:5094](),[3.4988][3.4988:5094]()
    // checkNonZeroFlags checks that the given int flag values are non-zero.
    // Returns an error if any are zero (does not call os.Exit).
    func checkNonZeroFlags(fs *flag.FlagSet, pairs ...struct {
    Name string
    Value int
    }) error {
    var missing []string
    for _, p := range pairs {
    if p.Value == 0 {
    missing = append(missing, p.Name)
    [3.4730]
    [3.8347]
    // usagePrinter returns an fs.Usage closure that prints a structured header,
    // the auto-generated flag table (via fs.PrintDefaults), and optional examples.
    // The flag table reflects the flags as registered — no manual duplication.
    func usagePrinter(fs *flag.FlagSet, synopsis, description string, examples ...string) func() {
    return func() {
    fmt.Fprintf(os.Stderr, "Usage: %s\n\n", synopsis)
    if description != "" {
    fmt.Fprintf(os.Stderr, "%s\n\n", description)
  • replacement in cmd/common.go at line 62
    [3.8351][3.8351:8449]()
    }
    if len(missing) > 0 {
    fs.Usage()
    return fmt.Errorf("missing required flags: %v", missing)
    [3.8351]
    [3.8449]
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    if len(examples) > 0 {
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    for _, ex := range examples {
    fmt.Fprintf(os.Stderr, " %s\n", ex)
    }
    }
  • edit in cmd/common.go at line 71
    [3.8452][3.8452:8464]()
    return nil
  • edit in cmd/cluster.go at line 7
    [3.1098895][3.1098895:1098901]()
    "os"
  • replacement in cmd/cluster.go at line 41
    [3.1100387][3.1100387:1100815]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak cluster create [options]\n\n")
    fmt.Fprintf(os.Stderr, "Create a new cluster for grouping recordings.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name \"2024-01\" --sample-rate 250000\n")
    }
    [3.1100387]
    [3.1100815]
    fs.Usage = usagePrinter(fs,
    "skraak cluster create [options]",
    "Create a new cluster for grouping recordings.",
    "skraak cluster create --db ./db/skraak.duckdb --dataset abc123 --location loc456 --name \"2024-01\" --sample-rate 250000",
    )
  • replacement in cmd/cluster.go at line 51
    [3.1100873][3.8952:9108]()
    if err := checkFlags(fs, "--db", *dbPath, "--dataset", *datasetID, "--location", *locationID, "--name", *name, "--sample-rate", *sampleRate); err != nil {
    [3.1100873]
    [3.9108]
    if err := requireFlags(fs, map[string]any{
    "--db": *dbPath,
    "--dataset": *datasetID,
    "--location": *locationID,
    "--name": *name,
    "--sample-rate": *sampleRate,
    }); err != nil {
  • replacement in cmd/cluster.go at line 101
    [3.1102472][3.1102472:1102875]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak cluster update [options]\n\n")
    fmt.Fprintf(os.Stderr, "Update an existing cluster. Only provided fields are updated.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak cluster update --db ./db/skraak.duckdb --id clust123 --name \"New Name\"\n")
    }
    [3.1102472]
    [3.1102875]
    fs.Usage = usagePrinter(fs,
    "skraak cluster update [options]",
    "Update an existing cluster. Only provided fields are updated.",
    "skraak cluster update --db ./db/skraak.duckdb --id clust123 --name \"New Name\"",
    )
  • replacement in cmd/cluster.go at line 111
    [3.1102933][3.9334:9404]()
    if err := checkFlags(fs, "--db", *dbPath, "--id", *id); err != nil {
    [3.1102933]
    [3.9404]
    if err := requireFlags(fs, map[string]any{"--db": *dbPath, "--id": *id}); err != nil {
  • replacement in cmd/calls_propagate.go at line 85
    [3.1113906][3.1113906:1115259]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls propagate [options]\n\n")
    fmt.Fprintf(os.Stderr, "Propagate verified classifications from one filter to another within a .data file\n")
    fmt.Fprintf(os.Stderr, "or across every .data file in a folder.\n\n")
    fmt.Fprintf(os.Stderr, "Only source labels with certainty=100 and matching --species are considered.\n")
    fmt.Fprintf(os.Stderr, "Target labels (filter=--to) are updated when their certainty is 70 or 0.\n")
    fmt.Fprintf(os.Stderr, "Updated target labels are set to certainty=90; file reviewer is set to \"Skraak\".\n")
    fmt.Fprintf(os.Stderr, "Targets already at certainty=100 or 90 are left alone.\n")
    fmt.Fprintf(os.Stderr, "Files that do not contain both --from and --to filter labels are skipped.\n\n")
    fmt.Fprintf(os.Stderr, "Exactly one of --file or --folder is required.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls propagate --file rec.wav.data \\\n")
    fmt.Fprintf(os.Stderr, " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi\n\n")
    fmt.Fprintf(os.Stderr, " skraak calls propagate --folder ./recordings \\\n")
    fmt.Fprintf(os.Stderr, " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi\n")
    }
    [3.1113906]
    [3.1115259]
    fs.Usage = usagePrinter(fs,
    "skraak calls propagate [options]",
    "Propagate verified classifications from one filter to another within a .data file\n"+
    "or across every .data file in a folder.\n\n"+
    "Only source labels with certainty=100 and matching --species are considered.\n"+
    "Target labels (filter=--to) are updated when their certainty is 70 or 0.\n"+
    "Updated target labels are set to certainty=90; file reviewer is set to \"Skraak\".\n"+
    "Targets already at certainty=100 or 90 are left alone.\n"+
    "Files that do not contain both --from and --to filter labels are skipped.\n\n"+
    "Exactly one of --file or --folder is required.",
    "skraak calls propagate --file rec.wav.data \\",
    " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi",
    "",
    "skraak calls propagate --folder ./recordings \\",
    " --from opensoundscape-kiwi-1.2 --to opensoundscape-kiwi-1.5 --species Kiwi",
    )
  • replacement in cmd/calls_propagate.go at line 111
    [3.1115471][3.4681:4774]()
    if err := checkFlags(fs, "--from", *from, "--to", *to, "--species", *species); err != nil {
    [3.1115471]
    [3.4774]
    if err := requireFlags(fs, map[string]any{"--from": *from, "--to": *to, "--species": *species}); err != nil {
  • replacement in cmd/calls_clip_labels.go at line 25
    [3.1129522][3.1129522:1130800]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls clip-labels [options]\n\n")
    fmt.Fprintf(os.Stderr, "Generate an OpenSoundScape clip_labels-format CSV from .data files.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nSegment policy:\n")
    fmt.Fprintf(os.Stderr, " - Real species → contributes mapped class to overlapping clips.\n")
    fmt.Fprintf(os.Stderr, " - Mapped to __NEGATIVE__ → clip emitted, all class columns False;\n")
    fmt.Fprintf(os.Stderr, " overrides positives in the same clip.\n")
    fmt.Fprintf(os.Stderr, " - Mapped to __IGNORE__ → segment contributes no labels to clips.\n")
    fmt.Fprintf(os.Stderr, " - Gaps → clip emitted with all class columns False.\n")
    fmt.Fprintf(os.Stderr, "\nIf --output exists: append. Column-set mismatch → hard error.\n")
    fmt.Fprintf(os.Stderr, "Duplicate (file, start_time, end_time) row → hard error on first.\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls clip-labels --folder ./recordings --mapping ./mapping.json\n")
    fmt.Fprintf(os.Stderr, " skraak calls clip-labels --folder ./recordings --mapping ./mapping.json \\\n")
    fmt.Fprintf(os.Stderr, " --filter opensoundscape-multi-1.0\n")
    }
    [3.1129522]
    [3.1130800]
    fs.Usage = usagePrinter(fs,
    "skraak calls clip-labels [options]",
    "Generate an OpenSoundScape clip_labels-format CSV from .data files.\n\n"+
    "Segment policy:\n"+
    " - Real species → contributes mapped class to overlapping clips.\n"+
    " - Mapped to __NEGATIVE__ → clip emitted, all class columns False;\n"+
    " overrides positives in the same clip.\n"+
    " - Mapped to __IGNORE__ → segment contributes no labels to clips.\n"+
    " - Gaps → clip emitted with all class columns False.\n\n"+
    "If --output exists: append. Column-set mismatch → hard error.\n"+
    "Duplicate (file, start_time, end_time) row → hard error on first.",
    "skraak calls clip-labels --folder ./recordings --mapping ./mapping.json",
    "skraak calls clip-labels --folder ./recordings --mapping ./mapping.json \\",
    " --filter opensoundscape-multi-1.0",
    )
  • replacement in cmd/calls_clip_labels.go at line 45
    [3.1130858][3.6435:6519]()
    if err := checkFlags(fs, "--folder", *folder, "--mapping", *mapping); err != nil {
    [3.1130858]
    [3.6519]
    if err := requireFlags(fs, map[string]any{"--folder": *folder, "--mapping": *mapping}); err != nil {
  • edit in cmd/calls_clip.go at line 12
    [3.16068][3.1133178:1134304](),[3.1133178][3.1133178:1134304](),[3.1134304][3.2351:3028](),[3.3028][3.1135310:1136301](),[3.1135310][3.1135310:1136301](),[3.1137041][3.1137041:1137042]()
    func printClipUsage() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls clip [options]\n\n")
    fmt.Fprintf(os.Stderr, "Generate audio clips and spectrogram images from .data file segments.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fmt.Fprintf(os.Stderr, " --file <path> Path to .data file (required if no --folder)\n")
    fmt.Fprintf(os.Stderr, " --folder <path> Path to folder containing .data files (required if no --file)\n")
    fmt.Fprintf(os.Stderr, " --output <path> Output folder for generated clips (required)\n")
    fmt.Fprintf(os.Stderr, " --prefix <name> Prefix for output filenames (required)\n")
    fmt.Fprintf(os.Stderr, " --filter <name> Filter by ML model name (optional)\n")
    fmt.Fprintf(os.Stderr, " --species <name> Filter by species, optionally with calltype (e.g. Kiwi, Kiwi+Duet)\n")
    fmt.Fprintf(os.Stderr, " --certainty <int> Filter by certainty value (0-100, optional)\n")
    fmt.Fprintf(os.Stderr, " --size <int> Spectrogram image size in pixels (224-896, default 224)\n")
    fmt.Fprintf(os.Stderr, " --color Apply L4 colormap to spectrogram (default: grayscale)\n")
    fmt.Fprintf(os.Stderr, " --night Only clip recordings made during solar night (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --day Only clip recordings made during solar day (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --location <lat,lng[,tz]> GPS coordinates and optional IANA timezone\n")
    fmt.Fprintf(os.Stderr, " e.g. --location \"-36.85,174.76\" or --location \"-36.85,174.76,Pacific/Auckland\"\n")
    fmt.Fprintf(os.Stderr, " Required with --night or --day. Timezone defaults to UTC.\n")
    fmt.Fprintf(os.Stderr, " Not needed for AudioMoth data (UTC from WAV comment).\n")
    fmt.Fprintf(os.Stderr, "\nOutput files:\n")
    fmt.Fprintf(os.Stderr, " <prefix>_<basename>_<start>_<end>.png # spectrogram image\n")
    fmt.Fprintf(os.Stderr, " <prefix>_<basename>_<start>_<end>.wav # audio clip (16kHz if downsampled)\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " # Clip all segments from a single file\n")
    fmt.Fprintf(os.Stderr, " skraak calls clip --file recording.data --output ./clips --prefix train\n\n")
    fmt.Fprintf(os.Stderr, " # Clip only Kiwi segments with color spectrograms at 448px\n")
    fmt.Fprintf(os.Stderr, " skraak calls clip --folder ./data --output ./clips --prefix kiwi \\\n")
    fmt.Fprintf(os.Stderr, " --filter opensoundscape-kiwi-1.2 --species Kiwi --size 448 --color\n\n")
    fmt.Fprintf(os.Stderr, " # Clip Kiwi Duet calls\n")
    fmt.Fprintf(os.Stderr, " skraak calls clip --folder ./data --output ./clips --prefix duet \\\n")
    fmt.Fprintf(os.Stderr, " --filter opensoundscape-kiwi-1.2 --species Kiwi+Duet\n")
    }
  • replacement in cmd/calls_clip.go at line 29
    [3.297][3.27486:27532](),[3.27486][3.27486:27532]()
    func parseClipArgs(args []string) clipFlags {
    [3.297]
    [3.298]
    // Returns the parsed flags and the FlagSet (whose Usage is invoked by the caller on error).
    func parseClipArgs(args []string) (clipFlags, *flag.FlagSet) {
  • replacement in cmd/calls_clip.go at line 37
    [3.754][3.754:838]()
    species := fs.String("species", "", "Filter by species, optionally with calltype")
    [3.754]
    [3.838]
    species := fs.String("species", "", "Filter by species, optionally with calltype (e.g. Kiwi, Kiwi+Duet)")
  • replacement in cmd/calls_clip.go at line 39
    [3.913][3.913:1294]()
    size := fs.Int("size", 0, "Spectrogram image size in pixels (224-896)")
    color := fs.Bool("color", false, "Apply L4 colormap to spectrogram")
    night := fs.Bool("night", false, "Only clip recordings made during solar night")
    day := fs.Bool("day", false, "Only clip recordings made during solar day")
    location := fs.String("location", "", "GPS coordinates and optional timezone")
    [3.913]
    [3.1294]
    size := fs.Int("size", 0, "Spectrogram image size in pixels (224-896, default 224)")
    color := fs.Bool("color", false, "Apply L4 colormap to spectrogram (default: grayscale)")
    night := fs.Bool("night", false, "Only clip recordings made during solar night (requires --location)")
    day := fs.Bool("day", false, "Only clip recordings made during solar day (requires --location)")
    location := fs.String("location", "", "GPS coordinates and optional IANA timezone, e.g. \"-36.85,174.76\" or \"-36.85,174.76,Pacific/Auckland\". Required with --night/--day. Not needed for AudioMoth data.")
  • replacement in cmd/calls_clip.go at line 45
    [3.1295][3.1295:1322]()
    fs.Usage = printClipUsage
    [3.1295]
    [3.1322]
    fs.Usage = usagePrinter(fs,
    "skraak calls clip [options]",
    "Generate audio clips and spectrogram images from .data file segments.\n\n"+
    "Output files:\n"+
    " <prefix>_<basename>_<start>_<end>.png # spectrogram image\n"+
    " <prefix>_<basename>_<start>_<end>.wav # audio clip (16kHz if downsampled)",
    "# Clip all segments from a single file",
    "skraak calls clip --file recording.data --output ./clips --prefix train",
    "",
    "# Clip only Kiwi segments with color spectrograms at 448px",
    "skraak calls clip --folder ./data --output ./clips --prefix kiwi \\",
    " --filter opensoundscape-kiwi-1.2 --species Kiwi --size 448 --color",
    "",
    "# Clip Kiwi Duet calls",
    "skraak calls clip --folder ./data --output ./clips --prefix duet \\",
    " --filter opensoundscape-kiwi-1.2 --species Kiwi+Duet",
    )
  • replacement in cmd/calls_clip.go at line 82
    [3.1757][3.1140312:1140315](),[3.1140312][3.1140312:1140315]()
    }
    [3.1757]
    [3.28052]
    }, fs
  • replacement in cmd/calls_clip.go at line 86
    [3.6771][3.6771:6855](),[3.6855][3.1140344:1140367](),[3.28158][3.1140344:1140367](),[3.1140344][3.1140344:1140367]()
    // Returns an error instead of exiting.
    func validateClipFlags(f clipFlags) error {
    missing := []string{}
    [3.6771]
    [3.28159]
    func validateClipFlags(fs *flag.FlagSet, f clipFlags) error {
  • replacement in cmd/calls_clip.go at line 88
    [3.28196][3.1140400:1140453](),[3.1140400][3.1140400:1140453](),[3.1140453][3.28197:28218](),[3.28218][3.1140472:1140512](),[3.1140472][3.1140472:1140512]()
    missing = append(missing, "--file or --folder")
    }
    if f.output == "" {
    missing = append(missing, "--output")
    [3.28196]
    [3.1140512]
    fs.Usage()
    return fmt.Errorf("missing required flags: [--file or --folder]")
  • replacement in cmd/calls_clip.go at line 91
    [3.1140515][3.28219:28240](),[3.28240][3.1140534:1140574](),[3.1140534][3.1140534:1140574]()
    if f.prefix == "" {
    missing = append(missing, "--prefix")
    [3.1140515]
    [3.1140574]
    if err := requireFlags(fs, map[string]any{"--output": f.output, "--prefix": f.prefix}); err != nil {
    return err
  • edit in cmd/calls_clip.go at line 94
    [3.1140577][3.1140577:1140600](),[3.1140675][3.1140675:1140694](),[3.1140694][3.6856:6915](),[3.6915][3.1140707:1140710](),[3.1140707][3.1140707:1140710]()
    if len(missing) > 0 {
    printClipUsage()
    return fmt.Errorf("missing required flags: %v", missing)
    }
  • replacement in cmd/calls_clip.go at line 95
    [3.1140810][3.1140810:1140829]()
    printClipUsage()
    [3.28264]
    [3.6916]
    fs.Usage()
  • replacement in cmd/calls_clip.go at line 99
    [3.3239][3.1140973:1140992](),[3.1140973][3.1140973:1140992]()
    printClipUsage()
    [3.3166]
    [3.6981]
    fs.Usage()
  • replacement in cmd/calls_clip.go at line 118
    [3.7093][3.28884:28910](),[3.28884][3.28884:28910](),[3.28910][3.7094:7140]()
    f := parseClipArgs(args)
    if err := validateClipFlags(f); err != nil {
    [3.7093]
    [3.7140]
    f, fs := parseClipArgs(args)
    if err := validateClipFlags(fs, f); err != nil {
  • replacement in cmd/calls.go at line 108
    [3.1158662][3.1158662:1160294]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls from-preds [options]\n\n")
    fmt.Fprintf(os.Stderr, "Extract clustered bird calls from ML predictions CSV.\n")
    fmt.Fprintf(os.Stderr, "Reads prediction CSV with columns: file, start_time, end_time, <ebird_codes...>\n")
    fmt.Fprintf(os.Stderr, "Each row is a clip with 1=present, 0=absent for each species.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nOutput:\n")
    fmt.Fprintf(os.Stderr, " With --dot-data=true (default): Writes .data files alongside audio files, outputs JSON summary\n")
    fmt.Fprintf(os.Stderr, " With --dot-data=false: Outputs JSON with clustered calls only (no .data files)\n")
    fmt.Fprintf(os.Stderr, "\nFilter name:\n")
    fmt.Fprintf(os.Stderr, " If --filter is provided, uses that value.\n")
    fmt.Fprintf(os.Stderr, " Otherwise, parses from CSV filename: prefix_filter_date.csv -> filter\n")
    fmt.Fprintf(os.Stderr, " Example: predsST_opensoundscape-kiwi-1.2_2025-11-12.csv -> opensoundscape-kiwi-1.2\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " # Write .data files (default)\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv predictions.csv\n")
    fmt.Fprintf(os.Stderr, "\n")
    fmt.Fprintf(os.Stderr, " # JSON output only (no .data files)\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv predictions.csv --dot-data=false > calls.json\n")
    fmt.Fprintf(os.Stderr, "\n")
    fmt.Fprintf(os.Stderr, " # Override filter name\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-preds --csv preds.csv --filter my-custom-filter\n")
    }
    [3.1158662]
    [3.1160294]
    fs.Usage = usagePrinter(fs,
    "skraak calls from-preds [options]",
    "Extract clustered bird calls from ML predictions CSV.\n"+
    "Reads prediction CSV with columns: file, start_time, end_time, <ebird_codes...>\n"+
    "Each row is a clip with 1=present, 0=absent for each species.\n\n"+
    "Output:\n"+
    " With --dot-data=true (default): Writes .data files alongside audio files, outputs JSON summary\n"+
    " With --dot-data=false: Outputs JSON with clustered calls only (no .data files)\n\n"+
    "Filter name:\n"+
    " If --filter is provided, uses that value.\n"+
    " Otherwise, parses from CSV filename: prefix_filter_date.csv -> filter\n"+
    " Example: predsST_opensoundscape-kiwi-1.2_2025-11-12.csv -> opensoundscape-kiwi-1.2",
    "# Write .data files (default)",
    "skraak calls from-preds --csv predictions.csv",
    "",
    "# JSON output only (no .data files)",
    "skraak calls from-preds --csv predictions.csv --dot-data=false > calls.json",
    "",
    "# Override filter name",
    "skraak calls from-preds --csv preds.csv --filter my-custom-filter",
    )
  • replacement in cmd/calls.go at line 134
    [3.1160352][3.1160352:1160380](),[3.1160380][3.10456:10515]()
    // Validate required flags
    if err := checkFlags(fs, "--csv", *csvPath); err != nil {
    [3.1160352]
    [3.10515]
    if err := requireFlags(fs, map[string]any{"--csv": *csvPath}); err != nil {
  • replacement in cmd/calls.go at line 207
    [3.1163080][3.1163080:1163675]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls show-images [options]\n\n")
    fmt.Fprintf(os.Stderr, "Display spectrogram images for each segment in a .data file.\n")
    fmt.Fprintf(os.Stderr, "Images are output using the Kitty graphics protocol (or Sixel with --sixel, iTerm2 with --iterm).\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls show-images --file recording.wav.data\n")
    fmt.Fprintf(os.Stderr, " skraak calls show-images --file recording.wav.data --color\n")
    }
    [3.1163080]
    [3.1163675]
    fs.Usage = usagePrinter(fs,
    "skraak calls show-images [options]",
    "Display spectrogram images for each segment in a .data file.\n"+
    "Images are output using the Kitty graphics protocol (or Sixel with --sixel, iTerm2 with --iterm).",
    "skraak calls show-images --file recording.wav.data",
    "skraak calls show-images --file recording.wav.data --color",
    )
  • replacement in cmd/calls.go at line 219
    [3.1163733][3.1163733:1163761](),[3.1163761][3.10911:10972]()
    // Validate required flags
    if err := checkFlags(fs, "--file", *filePath); err != nil {
    [3.1163733]
    [3.10972]
    if err := requireFlags(fs, map[string]any{"--file": *filePath}); err != nil {
  • replacement in cmd/calls.go at line 274
    [3.1165793][3.1165793:1166825]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls from-birda [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import BirdNET results to .data files.\n")
    fmt.Fprintf(os.Stderr, "Reads *.BirdNET.results.csv files and creates/merges .data files.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nBehavior:\n")
    fmt.Fprintf(os.Stderr, " - Filter is always 'BirdNET' (parsed from filename)\n")
    fmt.Fprintf(os.Stderr, " - If .data file exists with BirdNET filter: error (refuses to clobber)\n")
    fmt.Fprintf(os.Stderr, " - If .data file exists with different filter: merge segments\n")
    fmt.Fprintf(os.Stderr, " - Confidence (0.0-1.0) converted to certainty (0-100)\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-birda --folder ./recordings\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-birda --file recording.BirdNET.results.csv\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-birda --folder ./recordings --delete\n")
    }
    [3.1165793]
    [3.1166825]
    fs.Usage = usagePrinter(fs,
    "skraak calls from-birda [options]",
    "Import BirdNET results to .data files.\n"+
    "Reads *.BirdNET.results.csv files and creates/merges .data files.\n\n"+
    "Behavior:\n"+
    " - Filter is always 'BirdNET' (parsed from filename)\n"+
    " - If .data file exists with BirdNET filter: error (refuses to clobber)\n"+
    " - If .data file exists with different filter: merge segments\n"+
    " - Confidence (0.0-1.0) converted to certainty (0-100)",
    "skraak calls from-birda --folder ./recordings",
    "skraak calls from-birda --file recording.BirdNET.results.csv",
    "skraak calls from-birda --folder ./recordings --delete",
    )
  • replacement in cmd/calls.go at line 375
    [3.1169838][3.1169838:1170952]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls from-raven [options]\n\n")
    fmt.Fprintf(os.Stderr, "Import Raven selections to .data files.\n")
    fmt.Fprintf(os.Stderr, "Reads *.selections.txt files and creates/merges .data files.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nBehavior:\n")
    fmt.Fprintf(os.Stderr, " - Filter is always 'Raven' (parsed from filename)\n")
    fmt.Fprintf(os.Stderr, " - If .data file exists with Raven filter: error (refuses to clobber)\n")
    fmt.Fprintf(os.Stderr, " - If .data file exists with different filter: merge segments\n")
    fmt.Fprintf(os.Stderr, " - Frequency range preserved from Raven selections\n")
    fmt.Fprintf(os.Stderr, " - Certainty defaults to 70 (no confidence metric in Raven)\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-raven --folder ./recordings\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-raven --file recording.Table.1.selections.txt\n")
    fmt.Fprintf(os.Stderr, " skraak calls from-raven --folder ./recordings --delete\n")
    }
    [3.1169838]
    [3.1170952]
    fs.Usage = usagePrinter(fs,
    "skraak calls from-raven [options]",
    "Import Raven selections to .data files.\n"+
    "Reads *.selections.txt files and creates/merges .data files.\n\n"+
    "Behavior:\n"+
    " - Filter is always 'Raven' (parsed from filename)\n"+
    " - If .data file exists with Raven filter: error (refuses to clobber)\n"+
    " - If .data file exists with different filter: merge segments\n"+
    " - Frequency range preserved from Raven selections\n"+
    " - Certainty defaults to 70 (no confidence metric in Raven)",
    "skraak calls from-raven --folder ./recordings",
    "skraak calls from-raven --file recording.Table.1.selections.txt",
    "skraak calls from-raven --folder ./recordings --delete",
    )
  • replacement in cmd/calls.go at line 498
    [3.1174907][3.1174907:1176249]()
    fs.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage: skraak calls summarise [options]\n\n")
    fmt.Fprintf(os.Stderr, "Summarise all .data files in a folder.\n")
    fmt.Fprintf(os.Stderr, "Outputs JSON with segments array and summary statistics.\n\n")
    fmt.Fprintf(os.Stderr, "Options:\n")
    fs.PrintDefaults()
    fmt.Fprintf(os.Stderr, "\nOutput includes:\n")
    fmt.Fprintf(os.Stderr, " - segments: array of all segments with labels (omitted with --brief)\n")
    fmt.Fprintf(os.Stderr, " - data_files_read: count of successfully parsed .data files\n")
    fmt.Fprintf(os.Stderr, " - data_files_skipped: list of files that failed to parse\n")
    fmt.Fprintf(os.Stderr, " - total_segments: total number of segments\n")
    fmt.Fprintf(os.Stderr, " - filters: per-filter statistics (segments, species counts)\n")
    fmt.Fprintf(os.Stderr, " - review_status: unreviewed/confirmed/dont_know counts\n")
    fmt.Fprintf(os.Stderr, " - operators/reviewers: unique values found\n")
    fmt.Fprintf(os.Stderr, "\nExamples:\n")
    fmt.Fprintf(os.Stderr, " skraak calls summarise --folder ./recordings > summary.json\n")
    fmt.Fprintf(os.Stderr, " skraak calls summarise --folder ./recordings --brief > summary.json # summary only\n")
    fmt.Fprintf(os.Stderr, " skraak calls summarise --folder ./recordings --filter opensoundscape-kiwi-1.2 --brief\n")
    }
    [3.1174907]
    [3.1176249]
    fs.Usage = usagePrinter(fs,
    "skraak calls summarise [options]",
    "Summarise all .data files in a folder.\n"+
    "Outputs JSON with segments array and summary statistics.\n\n"+
    "Output includes:\n"+
    " - segments: array of all segments with labels (omitted with --brief)\n"+
    " - data_files_read: count of successfully parsed .data files\n"+
    " - data_files_skipped: list of files that failed to parse\n"+
    " - total_segments: total number of segments\n"+
    " - filters: per-filter statistics (segments, species counts)\n"+
    " - review_status: unreviewed/confirmed/dont_know counts\n"+
    " - operators/reviewers: unique values found",
    "skraak calls summarise --folder ./recordings > summary.json",
    "skraak calls summarise --folder ./recordings --brief > summary.json # summary only",
    "skraak calls summarise --folder ./recordings --filter opensoundscape-kiwi-1.2 --brief",
    )
  • replacement in cmd/calls.go at line 519
    [3.1176307][3.1176307:1176335](),[3.1176335][3.11674:11735]()
    // Validate required flags
    if err := checkFlags(fs, "--folder", *folder); err != nil {
    [3.1176307]
    [3.11735]
    if err := requireFlags(fs, map[string]any{"--folder": *folder}); err != nil {