(args []string)
| 174 | } |
| 175 | |
| 176 | func runExec(args []string) error { |
| 177 | localArgs, codexArgs := splitArgsAtDoubleDash(args) |
| 178 | |
| 179 | fs := flag.NewFlagSet(commandName+" exec", flag.ContinueOnError) |
| 180 | fs.SetOutput(os.Stderr) |
| 181 | |
| 182 | inPathFlag := fs.String("in", "", "Path to markdown spec file") |
| 183 | mode := fs.String("mode", "codex", "Prompt target mode (currently only: codex)") |
| 184 | printPrompt := fs.Bool("stdout", false, "Print generated prompt to stdout before launching Codex") |
| 185 | copyPrompt := fs.Bool("copy", false, "Also copy generated prompt to clipboard") |
| 186 | dryRun := fs.Bool("dry-run", false, "Print Codex command and exit without launching") |
| 187 | appendFile := stringListFlag{} |
| 188 | appendText := stringListFlag{} |
| 189 | fs.Var(&appendText, "append", "Additional instruction text to append (repeatable)") |
| 190 | fs.Var(&appendFile, "append-file", "Path to file with additional instructions (repeatable)") |
| 191 | |
| 192 | if err := fs.Parse(normalizeArgs(localArgs)); err != nil { |
| 193 | if errors.Is(err, flag.ErrHelp) { |
| 194 | return nil |
| 195 | } |
| 196 | return err |
| 197 | } |
| 198 | |
| 199 | if len(codexArgs) > 0 && isCodexSubcommand(codexArgs[0]) { |
| 200 | return fmt.Errorf("spec exec expects Codex option flags after --, not subcommands (got %q)", codexArgs[0]) |
| 201 | } |
| 202 | |
| 203 | path := strings.TrimSpace(*inPathFlag) |
| 204 | remaining := fs.Args() |
| 205 | if path == "" && len(remaining) > 0 { |
| 206 | path = strings.TrimSpace(remaining[0]) |
| 207 | remaining = remaining[1:] |
| 208 | } |
| 209 | if path == "" { |
| 210 | printUsage() |
| 211 | return errors.New("missing markdown spec file path") |
| 212 | } |
| 213 | |
| 214 | if len(remaining) > 0 { |
| 215 | appendText = append(appendText, strings.Join(remaining, " ")) |
| 216 | } |
| 217 | |
| 218 | bundle, err := buildPromptBundle(path, *mode) |
| 219 | if err != nil { |
| 220 | return err |
| 221 | } |
| 222 | |
| 223 | extraBlocks := make([]string, 0, len(appendText)+len(appendFile)) |
| 224 | for _, t := range appendText { |
| 225 | trimmed := strings.TrimSpace(t) |
| 226 | if trimmed != "" { |
| 227 | extraBlocks = append(extraBlocks, trimmed) |
| 228 | } |
| 229 | } |
| 230 | for _, filePath := range appendFile { |
| 231 | contentBytes, err := os.ReadFile(strings.TrimSpace(filePath)) |
| 232 | if err != nil { |
| 233 | return fmt.Errorf("read append-file %q: %w", filePath, err) |
no test coverage detected