@tmcayden/youtrack-axi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cayden Hunsaker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # youtrack-axi
2
+
3
+ A YouTrack CLI built for agents rather than people. It follows the
4
+ [AXI](https://skills.sh/kunchenguid/axi) standards - the copy of the spec this was
5
+ built against is committed at `.pi/skills/axi/SKILL.md`.
6
+
7
+ - **TOON on stdout**, roughly 40% cheaper than the equivalent JSON. The REST layer
8
+ stays on JSON; conversion happens only at the output boundary.
9
+ - **Content first.** No arguments prints your open issues, not a manual.
10
+ - **Errors are data.** They go to stdout, in TOON, with the command that fixes them.
11
+ - **No prompts, ever.** Every operation is completable with flags alone.
12
+ - **Time tracking**, which is the reason this exists: `work <ID> --log 1h30m`.
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install -g @tmcayden/youtrack-axi
18
+ youtrack-axi --version
19
+ ```
20
+
21
+ Or run it without installing:
22
+
23
+ ```sh
24
+ npx -y @tmcayden/youtrack-axi
25
+ ```
26
+
27
+ The bare name `youtrack-axi` is taken on npm by an unrelated author, so this is
28
+ published under the `@tmcayden` scope. The binary is `youtrack-axi` either way.
29
+
30
+ To work from a checkout instead:
31
+
32
+ ```sh
33
+ npm install -g /path/to/youtrack-axi
34
+ ```
35
+
36
+ ## Configuration
37
+
38
+ Credentials are read in this order:
39
+
40
+ 1. `YOUTRACK_URL` / `YOUTRACK_TOKEN` environment variables
41
+ 2. `$HARNESS_STATE_ROOT/.timesheet.json` (`youtrack_url`, `youtrack_token`)
42
+ 3. `~/.config/youtrack-axi/config.json`
43
+
44
+ `setup` writes the dedicated file only when the shared one is absent, so it never
45
+ shadows an existing setup:
46
+
47
+ ```sh
48
+ youtrack-axi setup --url "https://example.youtrack.cloud" --token "<permanent-token>"
49
+ youtrack-axi setup --verify
50
+ ```
51
+
52
+ The token is never written to stdout, a log, or a commit. `setup --verify`
53
+ confirms it works by naming the authenticated login instead.
54
+
55
+ ## Commands
56
+
57
+ ```sh
58
+ youtrack-axi # your open issues
59
+ youtrack-axi issues "project: GSE #Unresolved" --limit 20
60
+ youtrack-axi issues --assignee me --fields Priority,Type
61
+ youtrack-axi view GSE-123 [--full]
62
+ youtrack-axi create --project GSE --summary "..." [--description "..."]
63
+ youtrack-axi comment GSE-123 "..."
64
+ youtrack-axi cmd GSE-123 --command "in progress"
65
+ youtrack-axi work GSE-123 # read logged time
66
+ youtrack-axi work GSE-123 --log 1h30m --date 2026-09-05 --type Development --text "..."
67
+ youtrack-axi projects
68
+ youtrack-axi fields GSE [--full]
69
+ ```
70
+
71
+ Every subcommand takes `--help`. Exit codes are 0 for success including no-ops,
72
+ 1 for errors, 2 for usage errors.
73
+
74
+ `cmd` is idempotent: applying a state an issue is already in reports a no-op and
75
+ exits 0, and any real change is reported as a field-level diff.
76
+
77
+ `work --log` needs time tracking enabled on the project and the create-work-item
78
+ permission. When either is missing the CLI names which one it was, rather than
79
+ passing along a bare 403.
80
+
81
+ ## Agent integration
82
+
83
+ Two ways to give an agent this tool. You only need one.
84
+
85
+ - **Session hook** - ambient, shows live state at every session start:
86
+ ```sh
87
+ youtrack-axi setup --hooks
88
+ ```
89
+ Installs a `SessionStart` hook for Claude Code, Codex, and OpenCode, pointing at
90
+ the binary on PATH when it resolves to this executable. Repeat runs are silent
91
+ no-ops and stale paths are repaired.
92
+ - **Skill** - loads on demand, no per-session token cost:
93
+ `skills/youtrack-axi/SKILL.md`, generated from the CLI's own command specs.
94
+ `npm run skill:check` fails if it has drifted.
95
+ ```sh
96
+ npx skills add tmcayden/youtrack-axi --skill youtrack-axi
97
+ ```
98
+
99
+ ## Development
100
+
101
+ ```sh
102
+ npm install
103
+ npm test # build, unit + end-to-end tests, skill freshness
104
+ ```
105
+
106
+ The end-to-end suite runs the real binary against a stub YouTrack over HTTP and
107
+ asserts on stdout, stderr, and exit codes - the same surface an agent sees.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { tryFastPath } from "axi-sdk-js/fast-path";
3
+ import { VERSION } from "../dist/version.js";
4
+
5
+ if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
6
+ const { main } = await import("../dist/cli.js");
7
+ await main();
8
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type CommandSpec } from "./flags.js";
2
+ export declare const PACKAGE_NAME = "@tmcayden/youtrack-axi";
3
+ export declare const DESCRIPTION = "Query, update, and log time against YouTrack issues";
4
+ export declare const SPECS: CommandSpec[];
5
+ export declare function main(): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,91 @@
1
+ import { runAxiCli } from "axi-sdk-js";
2
+ import { renderError, renderOutput } from "./render.js";
3
+ import { resolveConfig } from "./config.js";
4
+ import { renderCommandHelp } from "./flags.js";
5
+ import { ISSUE_LIST_FIELDS, YouTrackClient } from "./youtrack.js";
6
+ import { VERSION } from "./version.js";
7
+ import { DEFAULT_QUERY, issueListBody, issuesSpec, runIssues, runView, viewSpec } from "./commands/issues.js";
8
+ import { cmdSpec, commentSpec, createSpec, runCmd, runComment, runCreate } from "./commands/mutate.js";
9
+ import { runWork, workSpec } from "./commands/work.js";
10
+ import { fieldsSpec, projectsSpec, runFields, runProjects } from "./commands/admin.js";
11
+ import { runSetup, setupSpec } from "./commands/setup.js";
12
+ export const PACKAGE_NAME = "@tmcayden/youtrack-axi";
13
+ export const DESCRIPTION = "Query, update, and log time against YouTrack issues";
14
+ const HOME_LIMIT = 15;
15
+ export const SPECS = [
16
+ issuesSpec,
17
+ viewSpec,
18
+ createSpec,
19
+ commentSpec,
20
+ cmdSpec,
21
+ workSpec,
22
+ projectsSpec,
23
+ fieldsSpec,
24
+ setupSpec,
25
+ ];
26
+ async function home(_args, client) {
27
+ const active = client;
28
+ const [issues, total] = await Promise.all([
29
+ active.get("issues", { query: DEFAULT_QUERY, fields: ISSUE_LIST_FIELDS, $top: HOME_LIMIT }),
30
+ active.countIssues(DEFAULT_QUERY),
31
+ ]);
32
+ return {
33
+ query: DEFAULT_QUERY,
34
+ ...issueListBody(issues, total, HOME_LIMIT),
35
+ };
36
+ }
37
+ function withClient(handler) {
38
+ return (args, client) => handler(args, client);
39
+ }
40
+ function topLevelHelp() {
41
+ return `${renderOutput({
42
+ tool: "youtrack-axi",
43
+ description: DESCRIPTION,
44
+ version: VERSION,
45
+ commands: SPECS.map((spec) => ({ command: spec.name, usage: spec.usage, description: spec.description })),
46
+ config: [
47
+ "Credentials come from $HARNESS_STATE_ROOT/.timesheet.json, then a dedicated config file",
48
+ "YOUTRACK_URL and YOUTRACK_TOKEN override both",
49
+ ],
50
+ help: [
51
+ "Run `youtrack-axi` with no arguments to see your open issues",
52
+ "Run `youtrack-axi <command> --help` for a command's flags and examples",
53
+ ],
54
+ })}\n`;
55
+ }
56
+ export async function main() {
57
+ await runAxiCli({
58
+ description: DESCRIPTION,
59
+ version: VERSION,
60
+ packageName: PACKAGE_NAME,
61
+ topLevelHelp: topLevelHelp(),
62
+ home,
63
+ commands: {
64
+ issues: withClient(runIssues),
65
+ view: withClient(runView),
66
+ create: withClient(runCreate),
67
+ comment: withClient(runComment),
68
+ cmd: withClient(runCmd),
69
+ work: withClient(runWork),
70
+ projects: withClient(runProjects),
71
+ fields: withClient(runFields),
72
+ setup: (args) => runSetup(args),
73
+ },
74
+ getCommandHelp: (command) => {
75
+ const spec = SPECS.find((candidate) => candidate.name === command);
76
+ return spec ? `${renderOutput(renderCommandHelp(spec))}\n` : null;
77
+ },
78
+ // `setup` must run before credentials exist, so context resolution - which
79
+ // requires them - is skipped for it and for pure help lookups.
80
+ resolveContext: ({ command, args }) => {
81
+ if (command === "setup" || args.includes("--help"))
82
+ return undefined;
83
+ return new YouTrackClient(resolveConfig());
84
+ },
85
+ renderUnknownCommand: (command) => `${renderError(`Unknown command: ${command}`, "VALIDATION_ERROR", [
86
+ `Valid commands: ${SPECS.map((spec) => spec.name).join(", ")}`,
87
+ "Run `youtrack-axi` with no arguments to see your open issues",
88
+ ])}\n`,
89
+ });
90
+ }
91
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { StructuredOutput } from "../render.js";
2
+ import { type CommandSpec } from "../flags.js";
3
+ import { YouTrackClient } from "../youtrack.js";
4
+ export declare const projectsSpec: CommandSpec;
5
+ export declare function runProjects(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
6
+ export declare const fieldsSpec: CommandSpec;
7
+ export declare function runFields(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
@@ -0,0 +1,96 @@
1
+ import { AxiError } from "axi-sdk-js";
2
+ import { boolFlag, parseArgs, positiveInt, rejectExtraPositionals, requirePositional, } from "../flags.js";
3
+ const DEFAULT_LIMIT = 100;
4
+ const VALUE_PREVIEW_COUNT = 8;
5
+ export const projectsSpec = {
6
+ name: "projects",
7
+ usage: "projects [--limit <n>]",
8
+ description: "List the projects this account can see",
9
+ flags: {
10
+ "--help": { kind: "boolean", description: "Show this help" },
11
+ "--limit": { kind: "string", description: "Maximum projects to return", default: String(DEFAULT_LIMIT) },
12
+ },
13
+ examples: ["youtrack-axi projects"],
14
+ };
15
+ export async function runProjects(args, client) {
16
+ const parsed = parseArgs(projectsSpec, args);
17
+ rejectExtraPositionals(parsed, projectsSpec, 0);
18
+ const limit = positiveInt(parsed, projectsSpec, "--limit", DEFAULT_LIMIT);
19
+ const projects = await client.get("admin/projects", {
20
+ fields: "shortName,name,archived",
21
+ $top: limit,
22
+ });
23
+ const active = projects.filter((p) => !p.archived);
24
+ if (active.length === 0) {
25
+ return {
26
+ projects: "0 active projects visible to this account",
27
+ help: ["Ask a YouTrack administrator to grant this account project access"],
28
+ };
29
+ }
30
+ return {
31
+ count: active.length,
32
+ projects: active.map((p) => ({ short: p.shortName ?? "", name: p.name ?? "" })),
33
+ help: [
34
+ 'Run `youtrack-axi issues "project: <SHORT> #Unresolved"` to list a project\'s open issues',
35
+ "Run `youtrack-axi fields <SHORT>` to see a project's custom fields",
36
+ ],
37
+ };
38
+ }
39
+ export const fieldsSpec = {
40
+ name: "fields",
41
+ usage: "fields <SHORT> [--full]",
42
+ description: "List a project's custom fields and their allowed values",
43
+ flags: {
44
+ "--help": { kind: "boolean", description: "Show this help" },
45
+ "--full": { kind: "boolean", description: "List every allowed value instead of the first few" },
46
+ },
47
+ examples: ["youtrack-axi fields GSE", "youtrack-axi fields GSE --full"],
48
+ };
49
+ export async function runFields(args, client) {
50
+ const parsed = parseArgs(fieldsSpec, args);
51
+ const shortName = requirePositional(parsed, fieldsSpec, 0, "<SHORT>");
52
+ rejectExtraPositionals(parsed, fieldsSpec, 1);
53
+ const full = boolFlag(parsed, "--full");
54
+ const projects = await client.get("admin/projects", {
55
+ fields: "id,shortName",
56
+ $top: 500,
57
+ });
58
+ const project = projects.find((p) => p.shortName?.toLowerCase() === shortName.toLowerCase());
59
+ if (!project?.id) {
60
+ throw new AxiError(`No project with short name ${shortName}`, "NOT_FOUND", [
61
+ "Run `youtrack-axi projects` to list valid short names",
62
+ ]);
63
+ }
64
+ const fields = await client.get(`admin/projects/${project.id}/customFields`, {
65
+ fields: "canBeEmpty,field(name,fieldType(valueType)),bundle(values(name))",
66
+ $top: 100,
67
+ });
68
+ if (fields.length === 0) {
69
+ return { fields: `0 custom fields configured on ${shortName}` };
70
+ }
71
+ let anyTruncated = false;
72
+ const rows = fields.map((f) => {
73
+ const values = (f.bundle?.values ?? []).map((v) => v.name ?? "").filter(Boolean);
74
+ const shown = full ? values : values.slice(0, VALUE_PREVIEW_COUNT);
75
+ if (shown.length < values.length)
76
+ anyTruncated = true;
77
+ return {
78
+ name: f.field?.name ?? "",
79
+ type: f.field?.fieldType?.valueType ?? "",
80
+ required: f.canBeEmpty === false ? "yes" : "no",
81
+ values: shown.join(" | "),
82
+ value_count: values.length,
83
+ };
84
+ });
85
+ return {
86
+ project: shortName,
87
+ count: fields.length,
88
+ fields: rows,
89
+ help: [
90
+ ...(anyTruncated ? [`Run \`youtrack-axi fields ${shortName} --full\` to see every allowed value`] : []),
91
+ `Run \`youtrack-axi issues "project: ${shortName} State: {<value>}"\` to filter by a field value`,
92
+ `Run \`youtrack-axi cmd <ID> --command "<field> <value>"\` to set one`,
93
+ ],
94
+ };
95
+ }
96
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { StructuredOutput } from "../render.js";
2
+ import { parseArgs, type CommandSpec } from "../flags.js";
3
+ import { type Issue, YouTrackClient } from "../youtrack.js";
4
+ export declare const DEFAULT_QUERY = "for:me #Unresolved";
5
+ export declare const issuesSpec: CommandSpec;
6
+ export declare function buildQuery(parsed: ReturnType<typeof parseArgs>): string;
7
+ export declare function runIssues(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
8
+ export declare function issueListBody(issues: Issue[], total: number | undefined, limit: number, extraFields?: string[]): StructuredOutput;
9
+ export declare const viewSpec: CommandSpec;
10
+ export declare function runView(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
@@ -0,0 +1,117 @@
1
+ import { boolFlag, optionalString, parseArgs, positiveInt, rejectExtraPositionals, requirePositional, } from "../flags.js";
2
+ import { DESCRIPTION_PREVIEW_CHARS, flattenCustomFields, formatDate, issueRow, issueRowWithFields, splitFieldList, truncate, } from "../format.js";
3
+ import { ISSUE_DETAIL_FIELDS, ISSUE_LIST_FIELDS } from "../youtrack.js";
4
+ export const DEFAULT_QUERY = "for:me #Unresolved";
5
+ const DEFAULT_LIMIT = 50;
6
+ export const issuesSpec = {
7
+ name: "issues",
8
+ usage: 'issues ["<query>"] [--project <SHORT>] [--state <state>] [--assignee <login>] [--fields <a,b>] [--limit <n>]',
9
+ description: "List issues matching a YouTrack query",
10
+ flags: {
11
+ "--help": { kind: "boolean", description: "Show this help" },
12
+ "--project": { kind: "string", description: "Restrict to a project short name" },
13
+ "--state": { kind: "string", description: "Restrict to a State value" },
14
+ "--assignee": { kind: "string", description: "Restrict to an assignee login, or `me`" },
15
+ "--fields": { kind: "string", description: "Extra custom fields to add as columns, comma separated" },
16
+ "--limit": { kind: "string", description: "Maximum rows to return", default: String(DEFAULT_LIMIT) },
17
+ },
18
+ examples: [
19
+ "youtrack-axi issues",
20
+ 'youtrack-axi issues "project: GSE #Unresolved" --limit 20',
21
+ "youtrack-axi issues --assignee me --state Open --fields Priority,Type",
22
+ ],
23
+ };
24
+ export function buildQuery(parsed) {
25
+ const clauses = [];
26
+ const positional = parsed.positionals[0];
27
+ if (positional)
28
+ clauses.push(positional);
29
+ const project = optionalString(parsed, "--project");
30
+ if (project)
31
+ clauses.push(`project: ${project}`);
32
+ const state = optionalString(parsed, "--state");
33
+ if (state)
34
+ clauses.push(`State: {${state}}`);
35
+ const assignee = optionalString(parsed, "--assignee");
36
+ if (assignee)
37
+ clauses.push(assignee === "me" ? "for: me" : `Assignee: ${assignee}`);
38
+ return clauses.length > 0 ? clauses.join(" ") : DEFAULT_QUERY;
39
+ }
40
+ export async function runIssues(args, client) {
41
+ const parsed = parseArgs(issuesSpec, args);
42
+ rejectExtraPositionals(parsed, issuesSpec, 1);
43
+ const limit = positiveInt(parsed, issuesSpec, "--limit", DEFAULT_LIMIT);
44
+ const extraFields = splitFieldList(optionalString(parsed, "--fields"));
45
+ const query = buildQuery(parsed);
46
+ const fields = extraFields.length > 0 ? `${ISSUE_LIST_FIELDS},project(shortName),reporter(login),created,updated` : ISSUE_LIST_FIELDS;
47
+ const [issues, total] = await Promise.all([
48
+ client.get("issues", { query, fields, $top: limit }),
49
+ client.countIssues(query),
50
+ ]);
51
+ return {
52
+ query,
53
+ ...issueListBody(issues, total, limit, extraFields),
54
+ };
55
+ }
56
+ export function issueListBody(issues, total, limit, extraFields = []) {
57
+ if (issues.length === 0) {
58
+ return {
59
+ issues: "0 issues match this query",
60
+ help: [
61
+ 'Widen the query, e.g. `youtrack-axi issues "#Unresolved"`',
62
+ "Run `youtrack-axi projects` to see the projects you can search",
63
+ ],
64
+ };
65
+ }
66
+ const rows = extraFields.length > 0 ? issues.map((i) => issueRowWithFields(i, extraFields)) : issues.map(issueRow);
67
+ const help = ["Run `youtrack-axi view <ID>` for full detail", "Run `youtrack-axi work <ID> --log 1h` to log time"];
68
+ if (total !== undefined && total > issues.length) {
69
+ help.unshift(`Showing ${issues.length} of ${total}; re-run with \`--limit ${Math.min(total, 500)}\` to see them all`);
70
+ }
71
+ return {
72
+ count: total === undefined ? `${issues.length}` : `${issues.length} of ${total} total`,
73
+ issues: rows,
74
+ help,
75
+ };
76
+ }
77
+ export const viewSpec = {
78
+ name: "view",
79
+ usage: "view <ID> [--full]",
80
+ description: "Show one issue in full detail",
81
+ flags: {
82
+ "--help": { kind: "boolean", description: "Show this help" },
83
+ "--full": { kind: "boolean", description: "Print the complete description instead of a preview" },
84
+ },
85
+ examples: ["youtrack-axi view GSE-123", "youtrack-axi view GSE-123 --full"],
86
+ };
87
+ export async function runView(args, client) {
88
+ const parsed = parseArgs(viewSpec, args);
89
+ const id = requirePositional(parsed, viewSpec, 0, "<ID>");
90
+ rejectExtraPositionals(parsed, viewSpec, 1);
91
+ const full = boolFlag(parsed, "--full");
92
+ const issue = await client.get(`issues/${encodeURIComponent(id)}`, { fields: ISSUE_DETAIL_FIELDS });
93
+ const fields = flattenCustomFields(issue.customFields);
94
+ const description = truncate(issue.description, full ? Number.MAX_SAFE_INTEGER : DESCRIPTION_PREVIEW_CHARS);
95
+ const populated = Object.fromEntries(Object.entries(fields).filter(([, value]) => value.length > 0));
96
+ return {
97
+ issue: {
98
+ id: issue.idReadable ?? id,
99
+ summary: issue.summary ?? "",
100
+ project: issue.project?.shortName ?? "",
101
+ reporter: issue.reporter?.login ?? "",
102
+ created: issue.created ? formatDate(issue.created) : "",
103
+ updated: issue.updated ? formatDate(issue.updated) : "",
104
+ resolved: issue.resolved ? formatDate(issue.resolved) : "no",
105
+ comments: issue.comments?.length ?? 0,
106
+ ...populated,
107
+ description: description.text.length > 0 ? description.text : "(empty)",
108
+ ...(description.truncated
109
+ ? { description_note: `truncated, ${description.totalChars} chars total` }
110
+ : {}),
111
+ },
112
+ ...(description.truncated
113
+ ? { help: [`Run \`youtrack-axi view ${issue.idReadable ?? id} --full\` to see the complete description`] }
114
+ : {}),
115
+ };
116
+ }
117
+ //# sourceMappingURL=issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/commands/issues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { StructuredOutput } from "../render.js";
2
+ import { type CommandSpec } from "../flags.js";
3
+ import { YouTrackClient } from "../youtrack.js";
4
+ export declare const createSpec: CommandSpec;
5
+ export declare function runCreate(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
6
+ export declare const commentSpec: CommandSpec;
7
+ export declare function runComment(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
8
+ export declare const cmdSpec: CommandSpec;
9
+ export declare function runCmd(args: string[], client: YouTrackClient): Promise<StructuredOutput>;
@@ -0,0 +1,115 @@
1
+ import { optionalString, parseArgs, rejectExtraPositionals, requirePositional, requireString, } from "../flags.js";
2
+ import { flattenCustomFields, truncate } from "../format.js";
3
+ export const createSpec = {
4
+ name: "create",
5
+ usage: 'create --project <SHORT> --summary "<text>" [--description "<text>"]',
6
+ description: "Create a new issue in a project",
7
+ flags: {
8
+ "--help": { kind: "boolean", description: "Show this help" },
9
+ "--project": { kind: "string", description: "Project short name, e.g. GSE" },
10
+ "--summary": { kind: "string", description: "Issue summary line" },
11
+ "--description": { kind: "string", description: "Issue description body" },
12
+ },
13
+ examples: [
14
+ 'youtrack-axi create --project GSE --summary "Fix map zoom regression"',
15
+ 'youtrack-axi create --project GSE --summary "Add audit log" --description "Track who changed what."',
16
+ ],
17
+ };
18
+ export async function runCreate(args, client) {
19
+ const parsed = parseArgs(createSpec, args);
20
+ rejectExtraPositionals(parsed, createSpec, 0);
21
+ const project = requireString(parsed, createSpec, "--project");
22
+ const summary = requireString(parsed, createSpec, "--summary");
23
+ const description = optionalString(parsed, "--description");
24
+ const created = await client.post("issues", { project: { shortName: project }, summary, ...(description ? { description } : {}) }, { fields: "idReadable,summary,project(shortName)" });
25
+ const id = created.idReadable ?? "";
26
+ return {
27
+ created: {
28
+ id,
29
+ summary: created.summary ?? summary,
30
+ project: created.project?.shortName ?? project,
31
+ url: `${client.baseUrl}/issue/${id}`,
32
+ },
33
+ help: [
34
+ `Run \`youtrack-axi cmd ${id} --command "<command>"\` to set state or assignee`,
35
+ `Run \`youtrack-axi work ${id} --log <duration>\` to log time against it`,
36
+ ],
37
+ };
38
+ }
39
+ export const commentSpec = {
40
+ name: "comment",
41
+ usage: 'comment <ID> "<text>"',
42
+ description: "Add a comment to an issue",
43
+ flags: {
44
+ "--help": { kind: "boolean", description: "Show this help" },
45
+ },
46
+ examples: [
47
+ 'youtrack-axi comment GSE-123 "Deployed to staging, ready for QA."',
48
+ ],
49
+ };
50
+ export async function runComment(args, client) {
51
+ const parsed = parseArgs(commentSpec, args);
52
+ const id = requirePositional(parsed, commentSpec, 0, "<ID>");
53
+ const text = requirePositional(parsed, commentSpec, 1, "comment text");
54
+ rejectExtraPositionals(parsed, commentSpec, 2);
55
+ const comment = await client.post(`issues/${encodeURIComponent(id)}/comments`, { text }, { fields: "id" });
56
+ return {
57
+ commented: {
58
+ issue: id,
59
+ id: comment.id ?? "",
60
+ preview: truncate(text, 120).text,
61
+ },
62
+ help: [`Run \`youtrack-axi view ${id}\` to see the issue's current state`],
63
+ };
64
+ }
65
+ export const cmdSpec = {
66
+ name: "cmd",
67
+ usage: 'cmd <ID> --command "<youtrack command>"',
68
+ description: "Apply a YouTrack command to an issue, such as a state or assignee change",
69
+ flags: {
70
+ "--help": { kind: "boolean", description: "Show this help" },
71
+ "--command": { kind: "string", description: "YouTrack command string" },
72
+ "--comment": { kind: "string", description: "Comment to add alongside the command" },
73
+ },
74
+ examples: [
75
+ 'youtrack-axi cmd GSE-123 --command "in progress"',
76
+ 'youtrack-axi cmd GSE-123 --command "for me"',
77
+ 'youtrack-axi cmd GSE-123 --command "State Fixed" --comment "Shipped in 2.4.1"',
78
+ ],
79
+ };
80
+ const CMD_STATE_FIELDS = "customFields(name,value(name,login))";
81
+ export async function runCmd(args, client) {
82
+ const parsed = parseArgs(cmdSpec, args);
83
+ const id = requirePositional(parsed, cmdSpec, 0, "<ID>");
84
+ rejectExtraPositionals(parsed, cmdSpec, 1);
85
+ const command = requireString(parsed, cmdSpec, "--command");
86
+ const comment = optionalString(parsed, "--comment");
87
+ const before = await readFields(client, id);
88
+ await client.post("commands", {
89
+ query: command,
90
+ issues: [{ idReadable: id }],
91
+ ...(comment ? { comment } : {}),
92
+ });
93
+ const after = await readFields(client, id);
94
+ const changes = Object.keys({ ...before, ...after })
95
+ .filter((name) => (before[name] ?? "") !== (after[name] ?? ""))
96
+ .map((name) => ({ field: name, from: before[name] ?? "", to: after[name] ?? "" }));
97
+ if (changes.length === 0) {
98
+ return {
99
+ applied: {
100
+ issue: id,
101
+ command,
102
+ result: comment ? "no field change; comment added" : "no-op, issue already in that state",
103
+ },
104
+ };
105
+ }
106
+ return {
107
+ applied: { issue: id, command },
108
+ changes,
109
+ };
110
+ }
111
+ async function readFields(client, id) {
112
+ const issue = await client.get(`issues/${encodeURIComponent(id)}`, { fields: CMD_STATE_FIELDS });
113
+ return flattenCustomFields(issue.customFields);
114
+ }
115
+ //# sourceMappingURL=mutate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutate.js","sourceRoot":"","sources":["../../src/commands/mutate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type StructuredOutput } from "../render.js";
2
+ import { type CommandSpec } from "../flags.js";
3
+ export declare const setupSpec: CommandSpec;
4
+ export declare function runSetup(args: string[]): Promise<StructuredOutput>;