{"record":{"id":"453ee83c9d4366bf","repo":"paperclipai/paperclip","slug":"usage-publish-announcements-ts-directory-staging-name-dry","errorCode":null,"errorMessage":"Usage: publish-announcements.ts [directory] [--staging name] [--dry-run | --publish]","messagePattern":"Usage: publish-announcements\\.ts \\[directory\\] \\[--staging name\\] \\[--dry-run \\| --publish\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/publish-announcements.ts","lineNumber":27,"sourceCode":"import { validateAnnouncementAnimation } from \"../server/src/services/announcement-animation.js\";\n\nexport function announcementPublishPrefix(staging?: string, hostPrefix?: string) {\n  if (hostPrefix !== undefined && !/^[a-z0-9-]+(?:\\/[a-z0-9-]+)*$/.test(hostPrefix)) {\n    throw new Error(\"Invalid PAPERCLIP_PAGE_DEFAULT_PREFIX: use lowercase path segments without leading or trailing slashes\");\n  }\n  const prefix = staging === undefined ? \"announcements/v1\" : `announcements/staging/${announcementIdSchema.parse(staging)}/v1`;\n  return hostPrefix ? `${hostPrefix}/${prefix}` : prefix;\n}\n\nexport function parseAnnouncementPublishArgs(args: string[]) {\n  let sourceDirectory: string | undefined;\n  let staging: string | undefined;\n  let mode: \"publish\" | \"dry-run\" | undefined;\n  const usage = \"Usage: publish-announcements.ts [directory] [--staging name] [--dry-run | --publish]\";\n  for (let index = 0; index < args.length; index++) {\n    const arg = args[index];\n    if (arg === \"--publish\" || arg === \"--dry-run\") {\n      if (mode) throw new Error(usage);\n      mode = arg === \"--publish\" ? \"publish\" : \"dry-run\";\n    } else if (arg === \"--staging\") {\n      if (staging !== undefined || !args[index + 1]) throw new Error(usage);\n      staging = announcementIdSchema.parse(args[++index]);\n    } else if (arg.startsWith(\"--\") || sourceDirectory !== undefined) {\n      throw new Error(usage);\n    } else {\n      sourceDirectory = arg;\n    }\n  }\n  return { sourceDirectory: sourceDirectory ?? (staging ? \"announcements/examples/staging\" : \"announcements\"), staging, publish: mode === \"publish\" };\n}\n\nexport async function prepareAnnouncementPublish(sourceDirectory: string, staging?: string, hostPrefix?: string) {\n  const prefix = announcementPublishPrefix(staging, hostPrefix);\n  const source = path.resolve(sourceDirectory);\n  if (!(await lstat(source)).isDirectory()) throw new Error(\"Source must be a real directory\");\n  const manifestPath = path.join(source, \"current.json\");","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/publish-announcements.ts#L9-L45","documentation":"parseAnnouncementPublishArgs throws its usage string when the --publish or --dry-run flag appears more than once, or in a conflicting combination. The mode variable may hold only one value; a duplicate or second mode flag is a usage error.","triggerScenarios":"Running the CLI with both `--publish` and `--dry-run`, or repeating the same flag (e.g. `--dry-run --dry-run`), so the second occurrence finds mode already set.","commonSituations":"Script templates concatenating flag arrays so the mode flag appears twice; users passing both flags thinking one overrides the other; shell aliases appending --dry-run while the user also passes --publish.","solutions":["Pass exactly one mode flag: either --publish or --dry-run, not both.","Audit shell scripts/aliases for duplicate mode flags being appended.","Default behavior with no flag is a dry-run unless --publish is given; drop the redundant flag.","Wrap the call in argument validation that dedupes flags before parsing."],"exampleFix":"// before\npublish-announcements.ts announcements --dry-run --publish\n// after\npublish-announcements.ts announcements --publish","handlingStrategy":"validation","validationCode":"const modeFlags = args.filter(a => a === '--publish' || a === '--dry-run');\nif (modeFlags.length > 1) throw new Error('Pass at most one of --publish / --dry-run');","typeGuard":"null","tryCatchPattern":"try {\n  const parsed = parseAnnouncementPublishArgs(args);\n} catch (e) {\n  if (String(e.message).startsWith('Usage:')) {\n    console.error(e.message + ' (exactly one of --publish or --dry-run)');\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Pass only one mode flag per invocation.","Review shell scripts that concatenate flag arrays for duplicates.","Rely on the default (no flag = dry-run) when unsure.","Lint CI command lines for duplicated flags."],"tags":["cli","argument-parsing","mutually-exclusive"],"backgroundTag":"mutually-exclusive-flags","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}