package cli import ( "encoding/json" "fmt" "os" "github.com/spf13/cobra" "gitea.maximumdirect.net/eric/seriatim/internal/config" "gitea.maximumdirect.net/eric/seriatim/internal/report" triminternal "gitea.maximumdirect.net/eric/seriatim/internal/trim" ) func newTrimCommand() *cobra.Command { var opts config.TrimOptions cmd := &cobra.Command{ Use: "trim", Short: "Trim an existing seriatim transcript artifact by segment ID", RunE: func(cmd *cobra.Command, args []string) error { trimOpts := opts if !cmd.Flags().Changed("output-schema") { trimOpts.OutputSchema = "" } cfg, err := config.NewTrimConfig(trimOpts) if err != nil { return err } selector, err := triminternal.ParseSelector(cfg.Selector) if err != nil { return fmt.Errorf("invalid selector %q: %w", cfg.Selector, err) } data, err := os.ReadFile(cfg.InputFile) if err != nil { return fmt.Errorf("read --input-file %q: %w", cfg.InputFile, err) } artifact, err := triminternal.ParseArtifactJSON(data) if err != nil { return fmt.Errorf("--input-file %q: %w", cfg.InputFile, err) } mode := triminternal.ModeKeep if cfg.Mode == "remove" { mode = triminternal.ModeRemove } trimmed, err := triminternal.ApplyArtifact(artifact, triminternal.Options{ Mode: mode, Selector: selector, AllowEmpty: cfg.AllowEmpty, }) if err != nil { return err } outputSchema := artifact.Schema if cfg.OutputSchema != "" { outputSchema = cfg.OutputSchema } outputArtifact, err := triminternal.ConvertArtifact(trimmed.Artifact, outputSchema) if err != nil { return err } if err := triminternal.ValidateArtifact(outputArtifact); err != nil { return fmt.Errorf("validate trimmed output: %w", err) } if err := writeOutputJSON(cfg.OutputFile, outputArtifact.Value()); err != nil { return err } if cfg.ReportFile != "" { rpt := report.Report{ Metadata: report.Metadata{ Application: outputArtifact.Application(), Version: outputArtifact.Version(), InputReader: "trim-artifact", InputFiles: []string{cfg.InputFile}, OutputModules: []string{"json"}, }, Events: []report.Event{ report.Info("trim", "trim", fmt.Sprintf("mode=%s retained %d segment(s), removed %d segment(s)", cfg.Mode, len(trimmed.OldToNewID), len(trimmed.RemovedIDs))), report.Info("trim", "validate-output", fmt.Sprintf("validated %d output segment(s)", outputArtifact.SegmentCount())), report.Info("output", "json", "wrote transcript JSON"), }, } if err := report.WriteJSON(cfg.ReportFile, rpt); err != nil { return err } } return nil }, } flags := cmd.Flags() flags.StringVar(&opts.InputFile, "input-file", "", "input seriatim transcript artifact JSON file") flags.StringVar(&opts.OutputFile, "output-file", "", "output transcript JSON file") flags.StringVar(&opts.ReportFile, "report-file", "", "optional report JSON file") flags.StringVar(&opts.Keep, "keep", "", "segment ID selector to keep (for example: 1-10,15)") flags.StringVar(&opts.Remove, "remove", "", "segment ID selector to remove (for example: 1-10,15)") flags.StringVar(&opts.OutputSchema, "output-schema", "", "optional output JSON schema override: seriatim-minimal, seriatim-intermediate, or seriatim-full") flags.BoolVar(&opts.AllowEmpty, "allow-empty", false, "allow trimming to an empty transcript") return cmd } func writeOutputJSON(path string, value any) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() enc := json.NewEncoder(file) enc.SetIndent("", " ") return enc.Encode(value) }