From d2ac958b1025b5b47d75eb296c6732b5281d4b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Ke=C3=9Fler?= Date: Mon, 23 Feb 2026 12:49:58 +0100 Subject: [PATCH 1/2] feat(jira): add --field flag for custom fields on issue create and update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for setting arbitrary Jira custom fields via a repeatable --field = flag on both `jira issue create` and `jira issue update`. Values are auto-coerced by type: - numeric strings → number - "null" → null - valid JSON → parsed (enables option objects and arrays) - everything else → string Example usage: atlcli jira issue create \ --project PROJ --type Story --summary "My story" \ --field customfield_10028=5 \ --field customfield_10077={"value":"Feature"} atlcli jira issue update --key PROJ-123 \ --field customfield_10028=8 \ --field customfield_10079=3 --- apps/cli/src/commands/jira.ts | 50 ++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/apps/cli/src/commands/jira.ts b/apps/cli/src/commands/jira.ts index deef358..5fb286a 100644 --- a/apps/cli/src/commands/jira.ts +++ b/apps/cli/src/commands/jira.ts @@ -6,6 +6,7 @@ import { fail, getActiveProfile, getFlag, + getFlags, hasFlag, loadConfig, output, @@ -328,6 +329,43 @@ Options: // ============ Issue Operations ============ +/** + * Parses repeated --field key=value flags into a fields object suitable for the Jira API. + * + * Type coercion rules: + * - "null" → null + * - numeric strings → number + * - valid JSON → parsed value (enables objects like {"value":"High"} and arrays) + * - everything else → string + * + * Examples: + * --field customfield_10028=5 → { customfield_10028: 5 } + * --field customfield_10077={"value":"Feature"} → { customfield_10077: { value: "Feature" } } + * --field customfield_10194=Some text → { customfield_10194: "Some text" } + */ +function parseCustomFields(rawFields: string[]): Record { + const result: Record = {}; + for (const raw of rawFields) { + const eqIdx = raw.indexOf("="); + if (eqIdx === -1) continue; + const key = raw.slice(0, eqIdx).trim(); + const value = raw.slice(eqIdx + 1); + if (!key) continue; + if (value === "null") { + result[key] = null; + } else if (/^-?\d+(\.\d+)?$/.test(value)) { + result[key] = parseFloat(value); + } else { + try { + result[key] = JSON.parse(value); + } catch { + result[key] = value; + } + } + } + return result; +} + async function handleIssue( args: string[], flags: Record, @@ -412,6 +450,7 @@ async function handleIssueCreate( const assignee = getFlag(flags, "assignee"); const labels = getFlag(flags, "labels"); const parent = getFlag(flags, "parent"); // For subtasks or epic children + const customFields = parseCustomFields(getFlags(flags, "field")); if (!project || !type || !summary) { fail(opts, 1, ERROR_CODES.USAGE, "--project, --type, and --summary are required (or set defaults.project in config)."); @@ -426,6 +465,7 @@ async function handleIssueCreate( assignee: assignee ? { accountId: assignee } : undefined, labels: labels ? labels.split(",").map((l) => l.trim()) : undefined, parent: parent ? { key: parent } : undefined, + ...customFields, }, }); @@ -447,10 +487,11 @@ async function handleIssueUpdate( const assignee = getFlag(flags, "assignee"); const addLabels = getFlag(flags, "add-labels"); const removeLabels = getFlag(flags, "remove-labels"); + const customFields = parseCustomFields(getFlags(flags, "field")); const client = await getClient(flags, opts); - const fields: Record = {}; + const fields: Record = { ...customFields }; if (summary) fields.summary = summary; if (description) fields.description = client.textToAdf(description); if (priority) fields.priority = { name: priority }; @@ -722,8 +763,8 @@ function issueHelp(): string { Commands: get --key [--expand ] - create --project --type --summary [--description ] [--priority ] [--assignee ] [--labels ] [--parent ] - update --key [--summary ] [--description ] [--priority ] [--assignee |none] [--add-labels ] [--remove-labels ] + create --project --type --summary [--description ] [--priority ] [--assignee ] [--labels ] [--parent ] [--field = ...] + update --key [--summary ] [--description ] [--priority ] [--assignee |none] [--add-labels ] [--remove-labels ] [--field = ...] delete --key --confirm [--delete-subtasks] transition --key --to transitions --key List available transitions @@ -740,6 +781,9 @@ Options: --profile Use a specific auth profile --json JSON output --comment Add comment when linking (link-page only) + --field = Set a custom field (repeatable). Value is auto-coerced: + numbers → number, JSON strings → parsed, plain text → string. + Example: --field customfield_10028=5 --field customfield_10077={"value":"Bug"} `; } From 20a698692b0145a75b22a09c54c076223e00ef04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Ke=C3=9Fler?= Date: Mon, 23 Feb 2026 12:55:14 +0100 Subject: [PATCH 2/2] docs(jira): document --field flag for custom fields - issues.md: add --field to Create/Update options tables, new "Custom Fields" section with type coercion table, value syntax for option/multi-select/user fields, field discovery workflow, and a note on field availability; remove stale bulk-edit tip - fields.md: replace non-existent --set syntax with correct --field flag and cross-reference to the new custom fields section --- src/content/docs/jira/fields.md | 13 +++++-- src/content/docs/jira/issues.md | 60 ++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/content/docs/jira/fields.md b/src/content/docs/jira/fields.md index 0ff6397..1216719 100644 --- a/src/content/docs/jira/fields.md +++ b/src/content/docs/jira/fields.md @@ -94,17 +94,24 @@ ID VALUE ### Set Custom Fields -When creating or updating issues: +Use the `--field =` flag on `issue create` and `issue update`. The flag is repeatable for multiple fields: ```bash # Create with custom field atlcli jira issue create --project PROJ --type Story --summary "Feature" \ - --set "customfield_10001=5" + --field customfield_10001=5 # Update custom field -atlcli jira issue update --key PROJ-123 --set "customfield_10001=8" +atlcli jira issue update --key PROJ-123 --field customfield_10001=8 + +# Set multiple fields at once +atlcli jira issue update --key PROJ-123 \ + --field customfield_10001=8 \ + --field customfield_10002='{"value":"Backend"}' ``` +See [Issues → Custom Fields](issues.md#custom-fields) for full type coercion rules and examples. + ### Query by Custom Fields Use JQL to search by custom field values: diff --git a/src/content/docs/jira/issues.md b/src/content/docs/jira/issues.md index a691e33..437bdef 100644 --- a/src/content/docs/jira/issues.md +++ b/src/content/docs/jira/issues.md @@ -53,6 +53,7 @@ Options: | `--priority` | Priority name | | `--labels` | Comma-separated labels | | `--parent` | Parent issue key (for subtasks) | +| `--field` | Set a custom field — `=`, repeatable | ### Examples @@ -75,6 +76,59 @@ Use `atlcli jira epic add` or `atlcli jira sprint add` after creating the issue. ::: +## Custom Fields + +Use `--field =` to set custom fields when creating or updating issues. The flag can be repeated for multiple fields. + +```bash +atlcli jira issue create --project PROJ --type Story --summary "My story" \ + --field customfield_10028=5 \ + --field customfield_10077='{"value":"Feature"}' \ + --field customfield_10194="As a user I want to..." + +atlcli jira issue update --key PROJ-123 \ + --field customfield_10028=8 \ + --field customfield_10079=3 +``` + +### Value Type Coercion + +Values are automatically coerced to the correct type: + +| Input | Resulting type | Example | +|-------|----------------|---------| +| Numeric string | `number` | `--field customfield_10028=5` | +| `null` | `null` | `--field customfield_10028=null` | +| Valid JSON | Parsed value | `--field customfield_10077='{"value":"Bug"}'` | +| Everything else | `string` | `--field customfield_10194="Some text"` | + +Use JSON syntax for Jira option, multi-select, and array fields: + +```bash +# Single select (option) +--field customfield_10077='{"value":"Feature"}' + +# Multi-select (array of options) +--field customfield_10195='[{"value":"Goal A"},{"value":"Goal B"}]' + +# User field +--field customfield_10050='{"accountId":"557058:abc123"}' +``` + +### Finding Field IDs + +Use `atlcli jira field search` to look up the ID for a custom field: + +```bash +atlcli jira field search "story points" +atlcli jira field options customfield_10077 # list allowed option values +``` + +:::note[Field availability] +Custom fields must be on the issue's create or edit screen in Jira. If you receive a "field cannot be set" error, the field is not configured for that issue type or project screen. + +::: + ## Update Issue ```bash @@ -92,6 +146,7 @@ Options: | `--add-labels` | Add labels (comma-separated) | | `--remove-labels` | Remove labels (comma-separated) | | `--assignee` | New assignee (account ID or `none` to unassign) | +| `--field` | Set a custom field — `=`, repeatable | ### Examples @@ -108,11 +163,6 @@ atlcli jira issue update --key PROJ-123 --add-labels reviewed,verified atlcli jira issue update --key PROJ-123 --assignee none ``` -:::tip[Setting Custom Fields] -Use `atlcli jira bulk edit --issues PROJ-123 --set field=value` to set custom fields on a single issue. - -::: - ## Delete Issue ```bash