@brunyee-studio/onus-cli 2.28.0 → 2.30.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/README.md +36 -7
- package/dist/bin.js +116 -28
- package/package.json +1 -1
- package/skills/onus-cli/SKILL.md +53 -19
package/README.md
CHANGED
|
@@ -200,8 +200,8 @@ onus team get <id> [--json]
|
|
|
200
200
|
```bash
|
|
201
201
|
onus project list [--json]
|
|
202
202
|
onus project get <id> [--json]
|
|
203
|
-
onus project create --name <name> --team <teamId> [--status <status>] [--priority <priority>] [--lead <leadId>] [--target-date <date>] [--json]
|
|
204
|
-
onus project update <id> [--name <name>] [--status <status>] [--priority <priority>] [--lead <leadId>] [--target-date <date>] [--json]
|
|
203
|
+
onus project create --name <name> --team <teamId> [--status <status>] [--priority <priority>] [--lead <leadId>] [--target-date <date>] [--description <text>] [--description-file <path>] [--agent-context <text>] [--agent-context-file <path>] [--json]
|
|
204
|
+
onus project update <id> [--name <name>] [--status <status>] [--priority <priority>] [--lead <leadId>] [--target-date <date>] [--description <text>] [--description-file <path>] [--agent-context <text>] [--agent-context-file <path>] [--json]
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
### `onus issue`
|
|
@@ -211,8 +211,8 @@ Issues are addressed by human reference (`ENG-42`); UUIDs are also accepted.
|
|
|
211
211
|
```bash
|
|
212
212
|
onus issue list [--team <id>] [--project <id>] [--assignee <id>] [--parent <id>] [--status <status>] [--q <query>] [--limit <n>] [--cursor <cursor>] [--json]
|
|
213
213
|
onus issue get <ref> [--json]
|
|
214
|
-
onus issue create --title <title> --team <teamId> [-d <description>] [--status <status>] [--priority <priority>] [--assignee <id>] [--project <id>] [--parent <id>] [--label <labelId>] [--json]
|
|
215
|
-
onus issue update <ref> [--title <title>] [-d <description>] [--status <status>] [--priority <priority>] [--assignee <id>] [--project <id>] [--parent <id>] [--add-label <labelId>] [--remove-label <labelId>] [--json]
|
|
214
|
+
onus issue create --title <title> --team <teamId> [-d <description>] [--description-file <path>] [--status <status>] [--priority <priority>] [--assignee <id>] [--project <id>] [--parent <id>] [--label <labelId>] [--json]
|
|
215
|
+
onus issue update <ref> [--title <title>] [-d <description>] [--description-file <path>] [--status <status>] [--priority <priority>] [--assignee <id>] [--project <id>] [--parent <id>] [--add-label <labelId>] [--remove-label <labelId>] [--json]
|
|
216
216
|
onus issue delete <ref> [--yes] [--json]
|
|
217
217
|
```
|
|
218
218
|
|
|
@@ -224,9 +224,9 @@ aliased as `edit`.
|
|
|
224
224
|
#### Comment subcommands
|
|
225
225
|
|
|
226
226
|
```bash
|
|
227
|
-
onus issue comment create <ref> --body <body> [--json]
|
|
227
|
+
onus issue comment create <ref> [--body <body> | --body-file <path>] [--json]
|
|
228
228
|
onus issue comment list <ref> [--json]
|
|
229
|
-
onus issue comment edit <ref> --comment <commentId> --body <body> [--json]
|
|
229
|
+
onus issue comment edit <ref> --comment <commentId> [--body <body> | --body-file <path>] [--json]
|
|
230
230
|
onus issue comment delete <ref> --comment <commentId> [--json]
|
|
231
231
|
```
|
|
232
232
|
|
|
@@ -270,7 +270,7 @@ onus issue unlink-pr <ref> --pr-link <linkId> [--json]
|
|
|
270
270
|
|
|
271
271
|
```bash
|
|
272
272
|
onus comment list <issueId> [--json]
|
|
273
|
-
onus comment create <issueId> --body <body> [--json]
|
|
273
|
+
onus comment create <issueId> [--body <body> | --body-file <path>] [--json]
|
|
274
274
|
```
|
|
275
275
|
|
|
276
276
|
### `onus label`
|
|
@@ -352,6 +352,35 @@ export ONUS_TOKEN="<your-onus-token>"
|
|
|
352
352
|
onus me --json
|
|
353
353
|
```
|
|
354
354
|
|
|
355
|
+
### 6. Long text via per-field file input
|
|
356
|
+
|
|
357
|
+
Long, multi-line values (issue and project descriptions, comment bodies,
|
|
358
|
+
project agent context) are awkward to inline in shell commands. Each of these
|
|
359
|
+
fields has a companion file flag that reads **only that field** from a file —
|
|
360
|
+
never the whole record — following the standard `--body-file` convention:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
onus issue create --title "Fix login bug" --team ONUS --description-file ./body.md
|
|
364
|
+
onus issue update ENG-42 --description-file ./notes.md
|
|
365
|
+
onus issue comment create ENG-42 --body-file ./comment.md
|
|
366
|
+
onus comment create ENG-42 --body-file ./comment.md
|
|
367
|
+
onus project create --name "Migration" --team ONUS --description-file ./brief.md
|
|
368
|
+
onus project update <projectId> --description-file ./brief.md
|
|
369
|
+
onus project update <projectId> --agent-context-file AGENTS.md
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Pass `-` as the path to read the field value from stdin, so agents can pipe
|
|
373
|
+
content directly:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
generate-body | onus issue create --title "Fix login bug" --team ONUS --description-file -
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Passing both the inline flag and its file variant (e.g. `--body` together with
|
|
380
|
+
`--body-file`) is an error; the file never silently overrides the inline value.
|
|
381
|
+
Comment bodies must be non-empty: an empty or whitespace-only `--body` or
|
|
382
|
+
`--body-file` is rejected before the request is sent.
|
|
383
|
+
|
|
355
384
|
---
|
|
356
385
|
|
|
357
386
|
## How the CLI is generated from OpenAPI
|
package/dist/bin.js
CHANGED
|
@@ -557,15 +557,33 @@ function registerAnalyticsCommands(program2) {
|
|
|
557
557
|
);
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
// src/
|
|
560
|
+
// src/file-input.ts
|
|
561
561
|
import { readFileSync } from "fs";
|
|
562
|
-
function
|
|
563
|
-
if (file !== void 0 &&
|
|
564
|
-
throw new Error(
|
|
562
|
+
async function resolveTextValue(opts) {
|
|
563
|
+
if (opts.file !== void 0 && opts.value !== void 0) {
|
|
564
|
+
throw new Error(`${opts.inlineFlag} and ${opts.fileFlag} cannot be used together`);
|
|
565
565
|
}
|
|
566
|
-
if (file
|
|
567
|
-
return
|
|
566
|
+
if (opts.file === void 0) return opts.value;
|
|
567
|
+
if (opts.file === "-") return readStdin();
|
|
568
|
+
return readFileSync(opts.file, "utf8");
|
|
569
|
+
}
|
|
570
|
+
function readStdin() {
|
|
571
|
+
return new Promise((resolve, reject) => {
|
|
572
|
+
const chunks = [];
|
|
573
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
574
|
+
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
575
|
+
process.stdin.on("error", reject);
|
|
576
|
+
});
|
|
568
577
|
}
|
|
578
|
+
async function resolveRequiredTextValue(opts) {
|
|
579
|
+
const value = await resolveTextValue(opts);
|
|
580
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
581
|
+
throw new Error(`${opts.subject} is required (provide ${opts.inlineFlag} or ${opts.fileFlag})`);
|
|
582
|
+
}
|
|
583
|
+
return value;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/commands/project.ts
|
|
569
587
|
function previewAgentContext(value) {
|
|
570
588
|
if (!value) return null;
|
|
571
589
|
const firstLine = value.split("\n")[0].trim();
|
|
@@ -623,15 +641,29 @@ function registerProjectCommands(program2) {
|
|
|
623
641
|
await handleApiError(err, globals.json);
|
|
624
642
|
}
|
|
625
643
|
});
|
|
626
|
-
project.command("create").description("Create a new project").requiredOption("--name <name>", "Project name").requiredOption("--team <team>", "Team UUID or KEY (e.g. ONUS)").option("--status <status>", "Project status (backlog|planned|in_progress|completed|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--lead <leadId>", "Lead user UUID").option("--target-date <date>", "Target completion date (ISO)").option("--description <text>", "Project description (rendered under the title)").option(
|
|
644
|
+
project.command("create").description("Create a new project").requiredOption("--name <name>", "Project name").requiredOption("--team <team>", "Team UUID or KEY (e.g. ONUS)").option("--status <status>", "Project status (backlog|planned|in_progress|completed|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--lead <leadId>", "Lead user UUID").option("--target-date <date>", "Target completion date (ISO)").option("--description <text>", "Project description (rendered under the title)").option("--description-file <path>", 'Read the project description from a file ("-" for stdin)').option(
|
|
627
645
|
"--agent-context <text>",
|
|
628
646
|
"Agent context markdown (agents.md-style guidance; '' clears)"
|
|
629
|
-
).option(
|
|
647
|
+
).option(
|
|
648
|
+
"--agent-context-file <path>",
|
|
649
|
+
'Read agent context markdown from a file ("-" for stdin)'
|
|
650
|
+
).option("-j, --json", "Output raw JSON").action(async function(opts) {
|
|
630
651
|
const globals = this.optsWithGlobals();
|
|
631
652
|
try {
|
|
632
653
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
633
654
|
const teamId = await resolveTeamId(client, opts.team);
|
|
634
|
-
const
|
|
655
|
+
const description = await resolveTextValue({
|
|
656
|
+
value: opts.description,
|
|
657
|
+
file: opts.descriptionFile,
|
|
658
|
+
inlineFlag: "--description",
|
|
659
|
+
fileFlag: "--description-file"
|
|
660
|
+
});
|
|
661
|
+
const agentContext = await resolveTextValue({
|
|
662
|
+
value: opts.agentContext,
|
|
663
|
+
file: opts.agentContextFile,
|
|
664
|
+
inlineFlag: "--agent-context",
|
|
665
|
+
fileFlag: "--agent-context-file"
|
|
666
|
+
});
|
|
635
667
|
const project2 = await client.projectsPost({
|
|
636
668
|
projectsPostRequest: {
|
|
637
669
|
team_id: teamId,
|
|
@@ -640,7 +672,7 @@ function registerProjectCommands(program2) {
|
|
|
640
672
|
priority: opts.priority,
|
|
641
673
|
lead_id: opts.lead,
|
|
642
674
|
target_date: opts.targetDate,
|
|
643
|
-
description
|
|
675
|
+
description,
|
|
644
676
|
agent_context: agentContext
|
|
645
677
|
}
|
|
646
678
|
});
|
|
@@ -651,14 +683,28 @@ function registerProjectCommands(program2) {
|
|
|
651
683
|
await handleApiError(err, globals.json);
|
|
652
684
|
}
|
|
653
685
|
});
|
|
654
|
-
project.command("update <id>").description("Update an existing project by UUID").option("--name <name>", "Project name").option("--status <status>", "Project status (backlog|planned|in_progress|completed|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--lead <leadId>", "Lead user UUID").option("--target-date <date>", "Target completion date (ISO)").option("--description <text>", "Project description (rendered under the title)").option(
|
|
686
|
+
project.command("update <id>").description("Update an existing project by UUID").option("--name <name>", "Project name").option("--status <status>", "Project status (backlog|planned|in_progress|completed|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--lead <leadId>", "Lead user UUID").option("--target-date <date>", "Target completion date (ISO)").option("--description <text>", "Project description (rendered under the title)").option("--description-file <path>", 'Read the project description from a file ("-" for stdin)').option(
|
|
655
687
|
"--agent-context <text>",
|
|
656
688
|
"Agent context markdown (agents.md-style guidance; '' clears)"
|
|
657
|
-
).option(
|
|
689
|
+
).option(
|
|
690
|
+
"--agent-context-file <path>",
|
|
691
|
+
'Read agent context markdown from a file ("-" for stdin)'
|
|
692
|
+
).option("-j, --json", "Output raw JSON").action(async function(id, opts) {
|
|
658
693
|
const globals = this.optsWithGlobals();
|
|
659
694
|
try {
|
|
660
695
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
661
|
-
const
|
|
696
|
+
const description = await resolveTextValue({
|
|
697
|
+
value: opts.description,
|
|
698
|
+
file: opts.descriptionFile,
|
|
699
|
+
inlineFlag: "--description",
|
|
700
|
+
fileFlag: "--description-file"
|
|
701
|
+
});
|
|
702
|
+
const agentContext = await resolveTextValue({
|
|
703
|
+
value: opts.agentContext,
|
|
704
|
+
file: opts.agentContextFile,
|
|
705
|
+
inlineFlag: "--agent-context",
|
|
706
|
+
fileFlag: "--agent-context-file"
|
|
707
|
+
});
|
|
662
708
|
const project2 = await client.projectsIdPatch({
|
|
663
709
|
id,
|
|
664
710
|
projectsIdPatchRequest: {
|
|
@@ -667,7 +713,7 @@ function registerProjectCommands(program2) {
|
|
|
667
713
|
priority: opts.priority,
|
|
668
714
|
lead_id: opts.lead,
|
|
669
715
|
target_date: opts.targetDate,
|
|
670
|
-
description
|
|
716
|
+
description,
|
|
671
717
|
agent_context: agentContext
|
|
672
718
|
}
|
|
673
719
|
});
|
|
@@ -718,6 +764,7 @@ function registerProjectCommands(program2) {
|
|
|
718
764
|
[
|
|
719
765
|
'onus project create --name "Website" --team ONUS',
|
|
720
766
|
'onus project create --name "Mobile" --team ONUS --priority high --target-date <YYYY-MM-DD>',
|
|
767
|
+
'onus project create --name "Migration" --team ONUS --description-file ./brief.md',
|
|
721
768
|
'onus project create --name "Migration" --team ONUS --agent-context-file AGENTS.md'
|
|
722
769
|
]
|
|
723
770
|
);
|
|
@@ -725,6 +772,7 @@ function registerProjectCommands(program2) {
|
|
|
725
772
|
project.commands.find((c) => c.name() === "update"),
|
|
726
773
|
[
|
|
727
774
|
"onus project update <projectId> --status in_progress",
|
|
775
|
+
"onus project update <projectId> --description-file ./brief.md",
|
|
728
776
|
"onus project update <projectId> --agent-context 'Add debug logging to all new code'",
|
|
729
777
|
"onus project update <projectId> --agent-context '' # clears agent context"
|
|
730
778
|
]
|
|
@@ -883,7 +931,7 @@ Comments (${issue2.comments.length}):`);
|
|
|
883
931
|
await handleApiError(err, globals.json);
|
|
884
932
|
}
|
|
885
933
|
});
|
|
886
|
-
issue.command("create").description("Create a new issue").option("--title <title>", "Issue title").requiredOption("--team <team>", "Team UUID or KEY (e.g. ONUS)").option("-d, --description <description>", "Issue description (markdown)").option("--status <status>", "Status (backlog|todo|in_progress|done|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--assignee <assigneeId>", "Assignee UUID").option("--project <projectId>", "Project UUID").option("--parent <parentId>", "Parent issue UUID").option("--label <labelId>", "Label UUID to attach (repeatable)", collect, []).option(
|
|
934
|
+
issue.command("create").description("Create a new issue").option("--title <title>", "Issue title").requiredOption("--team <team>", "Team UUID or KEY (e.g. ONUS)").option("-d, --description <description>", "Issue description (markdown)").option("--description-file <path>", 'Read the issue description from a file ("-" for stdin)').option("--status <status>", "Status (backlog|todo|in_progress|done|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--assignee <assigneeId>", "Assignee UUID").option("--project <projectId>", "Project UUID").option("--parent <parentId>", "Parent issue UUID").option("--label <labelId>", "Label UUID to attach (repeatable)", collect, []).option(
|
|
887
935
|
"--template <nameOrId>",
|
|
888
936
|
"Issue template: UUID or exact name within the team; fills omitted fields"
|
|
889
937
|
).option("-j, --json", "Output raw JSON").action(async function(opts) {
|
|
@@ -891,8 +939,13 @@ Comments (${issue2.comments.length}):`);
|
|
|
891
939
|
try {
|
|
892
940
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
893
941
|
const teamId = await resolveTeamId(client, opts.team);
|
|
942
|
+
let description = await resolveTextValue({
|
|
943
|
+
value: opts.description,
|
|
944
|
+
file: opts.descriptionFile,
|
|
945
|
+
inlineFlag: "--description",
|
|
946
|
+
fileFlag: "--description-file"
|
|
947
|
+
});
|
|
894
948
|
let title = opts.title;
|
|
895
|
-
let description = opts.description;
|
|
896
949
|
let priority = opts.priority;
|
|
897
950
|
let labels = (opts.label ?? []).length > 0 ? opts.label : void 0;
|
|
898
951
|
if (opts.template) {
|
|
@@ -946,7 +999,7 @@ Comments (${issue2.comments.length}):`);
|
|
|
946
999
|
await handleApiError(err, globals.json);
|
|
947
1000
|
}
|
|
948
1001
|
});
|
|
949
|
-
issue.command("update <ref>").alias("edit").description("Update (or edit) an existing issue by reference").option("--title <title>", "Issue title").option("-d, --description <description>", "Issue description").option("--status <status>", "Status (backlog|todo|in_progress|done|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--assignee <assigneeId>", "Assignee UUID").option("--project <projectId>", "Project UUID").option("--parent <parentId>", 'Parent issue UUID (pass "null", "none", or "" to clear)').option("--add-label <labelId>", "Label UUID to add (repeatable)", collect, []).option(
|
|
1002
|
+
issue.command("update <ref>").alias("edit").description("Update (or edit) an existing issue by reference").option("--title <title>", "Issue title").option("-d, --description <description>", "Issue description").option("--description-file <path>", 'Read the issue description from a file ("-" for stdin)').option("--status <status>", "Status (backlog|todo|in_progress|done|canceled)").option("--priority <priority>", "Priority (none|urgent|high|medium|low)").option("--assignee <assigneeId>", "Assignee UUID").option("--project <projectId>", "Project UUID").option("--parent <parentId>", 'Parent issue UUID (pass "null", "none", or "" to clear)').option("--add-label <labelId>", "Label UUID to add (repeatable)", collect, []).option(
|
|
950
1003
|
"--remove-label <labelId>",
|
|
951
1004
|
"Label UUID to remove (repeatable)",
|
|
952
1005
|
collect,
|
|
@@ -955,9 +1008,15 @@ Comments (${issue2.comments.length}):`);
|
|
|
955
1008
|
const globals = this.optsWithGlobals();
|
|
956
1009
|
try {
|
|
957
1010
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
1011
|
+
const description = await resolveTextValue({
|
|
1012
|
+
value: opts.description,
|
|
1013
|
+
file: opts.descriptionFile,
|
|
1014
|
+
inlineFlag: "--description",
|
|
1015
|
+
fileFlag: "--description-file"
|
|
1016
|
+
});
|
|
958
1017
|
const patch = {};
|
|
959
1018
|
if (opts.title !== void 0) patch.title = opts.title;
|
|
960
|
-
if (
|
|
1019
|
+
if (description !== void 0) patch.description = description;
|
|
961
1020
|
if (opts.status !== void 0) patch.status = opts.status;
|
|
962
1021
|
if (opts.priority !== void 0) patch.priority = opts.priority;
|
|
963
1022
|
if (opts.assignee !== void 0) patch.assignee_id = opts.assignee;
|
|
@@ -1017,14 +1076,16 @@ Comments (${issue2.comments.length}):`);
|
|
|
1017
1076
|
issue.commands.find((c) => c.name() === "create"),
|
|
1018
1077
|
[
|
|
1019
1078
|
'onus issue create --title "Fix login bug" --team ONUS',
|
|
1020
|
-
'onus issue create --title "Ship v2" --team ONUS --priority high --project <projectId>'
|
|
1079
|
+
'onus issue create --title "Ship v2" --team ONUS --priority high --project <projectId>',
|
|
1080
|
+
'onus issue create --title "Refactor auth" --team ONUS --description-file ./body.md'
|
|
1021
1081
|
]
|
|
1022
1082
|
);
|
|
1023
1083
|
withExamples(
|
|
1024
1084
|
issue.commands.find((c) => c.name() === "update"),
|
|
1025
1085
|
[
|
|
1026
1086
|
"onus issue update <issueId> --status done",
|
|
1027
|
-
"onus issue update <issueId> --priority urgent --assignee <assigneeId>"
|
|
1087
|
+
"onus issue update <issueId> --priority urgent --assignee <assigneeId>",
|
|
1088
|
+
"onus issue update <issueId> --description-file ./notes.md"
|
|
1028
1089
|
]
|
|
1029
1090
|
);
|
|
1030
1091
|
withExamples(
|
|
@@ -1039,13 +1100,20 @@ Comments (${issue2.comments.length}):`);
|
|
|
1039
1100
|
}
|
|
1040
1101
|
function registerIssueCommentCommands(issue) {
|
|
1041
1102
|
const comment = issue.command("comment").description("Create, list, edit, and delete issue comments");
|
|
1042
|
-
comment.command("create <ref>").description("Post a comment on an issue").
|
|
1103
|
+
comment.command("create <ref>").description("Post a comment on an issue").option("--body <body>", "Comment body text (markdown)").option("--body-file <path>", 'Read the comment body from a file ("-" for stdin)').option("-j, --json", "Output raw JSON").action(async function(id, opts) {
|
|
1043
1104
|
const globals = this.optsWithGlobals();
|
|
1044
1105
|
try {
|
|
1045
1106
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
1107
|
+
const body = await resolveRequiredTextValue({
|
|
1108
|
+
value: opts.body,
|
|
1109
|
+
file: opts.bodyFile,
|
|
1110
|
+
inlineFlag: "--body",
|
|
1111
|
+
fileFlag: "--body-file",
|
|
1112
|
+
subject: "comment body"
|
|
1113
|
+
});
|
|
1046
1114
|
const c = await client.issuesIdCommentsPost({
|
|
1047
1115
|
id,
|
|
1048
|
-
commentsCommentIdPatchRequest: { body
|
|
1116
|
+
commentsCommentIdPatchRequest: { body }
|
|
1049
1117
|
});
|
|
1050
1118
|
printJsonOrFormatted(c, globals.json, () => formatSuccess(`Posted comment ${c.id}.`));
|
|
1051
1119
|
} catch (err) {
|
|
@@ -1078,13 +1146,20 @@ function registerIssueCommentCommands(issue) {
|
|
|
1078
1146
|
await handleApiError(err, globals.json);
|
|
1079
1147
|
}
|
|
1080
1148
|
});
|
|
1081
|
-
comment.command("edit <ref>").description("Edit an existing comment").requiredOption("--comment <commentId>", "Comment UUID").
|
|
1149
|
+
comment.command("edit <ref>").description("Edit an existing comment").requiredOption("--comment <commentId>", "Comment UUID").option("--body <body>", "New comment body text").option("--body-file <path>", 'Read the new comment body from a file ("-" for stdin)').option("-j, --json", "Output raw JSON").action(async function(_id, opts) {
|
|
1082
1150
|
const globals = this.optsWithGlobals();
|
|
1083
1151
|
try {
|
|
1084
1152
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
1153
|
+
const body = await resolveRequiredTextValue({
|
|
1154
|
+
value: opts.body,
|
|
1155
|
+
file: opts.bodyFile,
|
|
1156
|
+
inlineFlag: "--body",
|
|
1157
|
+
fileFlag: "--body-file",
|
|
1158
|
+
subject: "comment body"
|
|
1159
|
+
});
|
|
1085
1160
|
const c = await client.commentsCommentIdPatch({
|
|
1086
1161
|
commentId: opts.comment,
|
|
1087
|
-
commentsCommentIdPatchRequest: { body
|
|
1162
|
+
commentsCommentIdPatchRequest: { body }
|
|
1088
1163
|
});
|
|
1089
1164
|
printJsonOrFormatted(c, globals.json, () => formatSuccess(`Updated comment ${c.id}.`));
|
|
1090
1165
|
} catch (err) {
|
|
@@ -1107,7 +1182,10 @@ function registerIssueCommentCommands(issue) {
|
|
|
1107
1182
|
});
|
|
1108
1183
|
withExamples(
|
|
1109
1184
|
comment.commands.find((c) => c.name() === "create"),
|
|
1110
|
-
[
|
|
1185
|
+
[
|
|
1186
|
+
'onus issue comment create <issueId> --body "Thanks for the fix!"',
|
|
1187
|
+
"onus issue comment create <issueId> --body-file ./comment.md"
|
|
1188
|
+
]
|
|
1111
1189
|
);
|
|
1112
1190
|
withExamples(
|
|
1113
1191
|
comment.commands.find((c) => c.name() === "list"),
|
|
@@ -1467,13 +1545,20 @@ function registerCommentCommands(program2) {
|
|
|
1467
1545
|
await handleApiError(err, globals.json);
|
|
1468
1546
|
}
|
|
1469
1547
|
});
|
|
1470
|
-
comment.command("create <issueId>").description("Post a comment on an issue").
|
|
1548
|
+
comment.command("create <issueId>").description("Post a comment on an issue").option("--body <body>", "Comment body text (markdown)").option("--body-file <path>", 'Read the comment body from a file ("-" for stdin)').option("-j, --json", "Output raw JSON").action(async function(issueId, opts) {
|
|
1471
1549
|
const globals = this.optsWithGlobals();
|
|
1472
1550
|
try {
|
|
1473
1551
|
const client = createApiClient({ token: globals.token, apiUrl: globals.apiUrl });
|
|
1552
|
+
const body = await resolveRequiredTextValue({
|
|
1553
|
+
value: opts.body,
|
|
1554
|
+
file: opts.bodyFile,
|
|
1555
|
+
inlineFlag: "--body",
|
|
1556
|
+
fileFlag: "--body-file",
|
|
1557
|
+
subject: "comment body"
|
|
1558
|
+
});
|
|
1474
1559
|
const comment2 = await client.issuesIdCommentsPost({
|
|
1475
1560
|
id: issueId,
|
|
1476
|
-
commentsCommentIdPatchRequest: { body
|
|
1561
|
+
commentsCommentIdPatchRequest: { body }
|
|
1477
1562
|
});
|
|
1478
1563
|
printJsonOrFormatted(comment2, globals.json, () => {
|
|
1479
1564
|
formatSuccess(`Posted comment ${comment2.id}.`);
|
|
@@ -1488,7 +1573,10 @@ function registerCommentCommands(program2) {
|
|
|
1488
1573
|
);
|
|
1489
1574
|
withExamples(
|
|
1490
1575
|
comment.commands.find((c) => c.name() === "create"),
|
|
1491
|
-
[
|
|
1576
|
+
[
|
|
1577
|
+
'onus comment create <issueId> --body "Looks good to me"',
|
|
1578
|
+
"onus comment create <issueId> --body-file ./comment.md"
|
|
1579
|
+
]
|
|
1492
1580
|
);
|
|
1493
1581
|
}
|
|
1494
1582
|
|
package/package.json
CHANGED
package/skills/onus-cli/SKILL.md
CHANGED
|
@@ -22,6 +22,7 @@ onus --api-url <url> … # custom API base URL
|
|
|
22
22
|
|
|
23
23
|
- **Starting work on an issue: transition it to `in_progress` immediately.** As soon as you pick up an issue to implement it — before writing any code — run `onus issue update <ref> --status in_progress --json`. No update needed if `onus issue get <ref> --json` already shows the status is `in_progress` or `done`.
|
|
24
24
|
- Add `--json` to every command you intend to parse.
|
|
25
|
+
- Multi-line text (issue descriptions, comment bodies, agent context) goes through the field's file flag — `--description-file`, `--body-file`, `--agent-context-file` — never inline in the shell command. See "Long text via file input" below.
|
|
25
26
|
- Whenever referencing an Onus issue in any response, include its link: `https://onus.brunyeestudio.com/team/{TEAM}/issues/{NUM}` where `{TEAM}` is the team key (the tag before the dash in the ref, e.g. `ONUS`) and `{NUM}` is the issue number (the number after the dash). Example: for ref `ONUS-1`, `TEAM = ONUS`, `NUM = 1` → https://onus.brunyeestudio.com/team/ONUS/issues/1
|
|
26
27
|
- Exit codes: `0` success · `1` general error · `2` validation error · `3` auth error · `4` not found.
|
|
27
28
|
- Issue commands take a `<ref>` = human identifier (e.g. `ENG-42`) or UUID. Every `--team <team>` option accepts a team KEY (e.g. `ONUS`) or a team UUID — prefer the KEY, it's stable and readable.
|
|
@@ -65,7 +66,8 @@ reading alongside the issue itself.
|
|
|
65
66
|
(`issue create/update --project`).
|
|
66
67
|
- Editing a project (name, status, description, `agent_context`) is
|
|
67
68
|
permission-gated; never guess project UUIDs — `onus project list` first.
|
|
68
|
-
Set context from a file: `onus project update <id> --agent-context-file AGENTS.md
|
|
69
|
+
Set context from a file: `onus project update <id> --agent-context-file AGENTS.md`
|
|
70
|
+
(or `-` to read stdin).
|
|
69
71
|
|
|
70
72
|
## Create and update issues
|
|
71
73
|
|
|
@@ -80,6 +82,28 @@ onus issue update ENG-42 --remove-label <labelId> --assignee <userId> --json
|
|
|
80
82
|
onus issue delete ENG-42 --yes # skip confirmation
|
|
81
83
|
```
|
|
82
84
|
|
|
85
|
+
## Long text via file input
|
|
86
|
+
|
|
87
|
+
Multi-line values (issue and project descriptions, comment bodies, project
|
|
88
|
+
agent context) must not be inlined in the shell command — quoting, escaping,
|
|
89
|
+
and shell length limits make the transcript fragile. Each long-text field has
|
|
90
|
+
a companion file flag that reads exactly that one field from a file; pass `-`
|
|
91
|
+
to read the value from stdin:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
onus issue create --title "Refactor auth" --team ONUS --description-file ./body.md --json
|
|
95
|
+
onus issue update ENG-42 --description-file ./notes.md --json
|
|
96
|
+
onus issue comment create ENG-42 --body-file ./comment.md --json
|
|
97
|
+
onus issue comment edit ENG-42 --comment <commentId> --body-file ./comment.md --json
|
|
98
|
+
onus project update <projectId> --description-file ./brief.md --json
|
|
99
|
+
onus project update <projectId> --agent-context-file AGENTS.md --json
|
|
100
|
+
generate-body | onus comment create ENG-42 --body-file - --json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- The file flag covers exactly one field — never a whole-record file.
|
|
104
|
+
- Inline value and file flag are mutually exclusive: passing both (e.g.
|
|
105
|
+
`--body` together with `--body-file`) is an error.
|
|
106
|
+
|
|
83
107
|
## Comments
|
|
84
108
|
|
|
85
109
|
```bash
|
|
@@ -121,24 +145,34 @@ onus dsn create --team ONUS --label ci # prints the full secret exactly once
|
|
|
121
145
|
## Pull requests and issues
|
|
122
146
|
|
|
123
147
|
Linking a PR to an issue means **the issue is auto-closed when the PR merges** —
|
|
124
|
-
there is no "related" link type.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
there is no "related" link type.
|
|
149
|
+
|
|
150
|
+
**Always link manually with `onus issue link-pr`. Never rely on the fix
|
|
151
|
+
keyword.** The integration also scans the PR title, body, and branch name for
|
|
152
|
+
`KEY-123` refs introduced by a closing keyword (`Fixes` / `Closes` /
|
|
153
|
+
`Resolves` — case-insensitive, optional colon, comma/`and` lists), but agents
|
|
154
|
+
must never depend on that scan and must never write a closing keyword before
|
|
155
|
+
an Onus issue ref in PR text or a branch name. Reference issues in PR text by
|
|
156
|
+
name or their Onus URL, never as bare issue IDs.
|
|
157
|
+
|
|
158
|
+
Required PR flow, in order:
|
|
159
|
+
|
|
160
|
+
1. **Create the PR** (push the branch, open the PR with `gh`).
|
|
161
|
+
2. **Determine whether the PR closes the issue.** It closes the issue only if
|
|
162
|
+
the PR fully resolves it — you implemented the whole fix in that PR. A PR
|
|
163
|
+
that merely touches related code, discusses the issue, or implements part
|
|
164
|
+
of it without completing it does not close it.
|
|
165
|
+
3. **Link it.** If (and only if) step 2 says yes, run:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
onus issue link-pr <ref> --repo owner/repo --pr <number> --json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The bot link survives PR-body edits and closes the issue on merge. If the
|
|
172
|
+
PR does not close the issue, leave it unlinked — use a relation
|
|
173
|
+
(`onus issue relation add … --type related`) or a comment mention instead.
|
|
174
|
+
|
|
175
|
+
- Never guess or assume: link only issues you know the PR fully resolves.
|
|
142
176
|
- Link at most once per PR per issue; don't spam links across partially-related
|
|
143
177
|
issues.
|
|
144
178
|
|