neuvikon 0.2.32 → 0.2.33
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 +37 -0
- package/dist/index.mjs +79 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,10 @@ as being worked on, by whom, with which tool.
|
|
|
45
45
|
| `neuvikon create "Title" [--ticket EWWQ-1]` | File something you noticed as a new ticket, same project |
|
|
46
46
|
| `neuvikon create "Title" --sub [--ticket EWWQ-1]` | ...or as a sub-ticket of the ticket you're on |
|
|
47
47
|
| `neuvikon create "Title" --parent NS-12` | ...or of a different ticket entirely |
|
|
48
|
+
| `neuvikon update --assignee Ogulcan [--ticket EWWQ-1]` | Change a ticket's fields: assignee, team, sprint, type, priority, labels, title, description |
|
|
49
|
+
| `neuvikon board [--ticket EWWQ-1]` | This project's columns, and where the ticket sits |
|
|
50
|
+
| `neuvikon move --column "Code Review" --comment "why"` | Move the ticket to a column, with a reason |
|
|
51
|
+
| `neuvikon comment --text "..."` | Leave a note without moving the ticket |
|
|
48
52
|
|
|
49
53
|
`start` also accepts the ticket on stdin, and pulls the reference out of it:
|
|
50
54
|
|
|
@@ -99,6 +103,39 @@ itself, same restriction the app's own "+ sub-ticket" control has. Either way
|
|
|
99
103
|
the ticket you were on gets an automated comment pointing at the new one, so
|
|
100
104
|
nothing gets filed invisibly.
|
|
101
105
|
|
|
106
|
+
## Changing a ticket
|
|
107
|
+
|
|
108
|
+
Anything the board's ticket dialog can change, `update` can change from a
|
|
109
|
+
terminal — so an agent can carry out "assign this to Ogulcan and bump it to
|
|
110
|
+
high" without anyone opening the app:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
npx neuvikon update --ticket EWWQ-1 --assignee Ogulcan --priority high
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
One flag per field, any number at once: `--assignee`, `--team`, `--sprint`,
|
|
117
|
+
`--type` (`feature`/`bug`), `--priority` (`low`/`medium`/`high`/`urgent`),
|
|
118
|
+
`--labels` (comma-separated, replaces the whole set), `--title`,
|
|
119
|
+
`--description`.
|
|
120
|
+
|
|
121
|
+
People, teams and sprints are named the way you'd say them out loud — a first
|
|
122
|
+
name, an email, `"Sprint 1"` — and resolved on the server, which refuses an
|
|
123
|
+
ambiguous name instead of guessing at one. `none` clears one of those three,
|
|
124
|
+
an empty value clears labels or the description, and `--description -` takes
|
|
125
|
+
it from stdin:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
npx neuvikon update --ticket EWWQ-1 --assignee none --labels ""
|
|
129
|
+
|
|
130
|
+
npx neuvikon update --ticket EWWQ-1 --description - <<'EOF'
|
|
131
|
+
What this ticket actually turned out to be about.
|
|
132
|
+
EOF
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Being assigned a ticket this way notifies the assignee exactly as the board
|
|
136
|
+
does. Two things `update` won't do: move the ticket to another column (that's
|
|
137
|
+
`move`, which requires a reason) or delete it.
|
|
138
|
+
|
|
102
139
|
## Project pages
|
|
103
140
|
|
|
104
141
|
An agent's most useful output often isn't the ticket, it's what it learned
|
package/dist/index.mjs
CHANGED
|
@@ -373,7 +373,7 @@ async function comment(flags) {
|
|
|
373
373
|
}
|
|
374
374
|
//#endregion
|
|
375
375
|
//#region src/commands/create.ts
|
|
376
|
-
const TYPES = /* @__PURE__ */ new Set(["feature", "bug"]);
|
|
376
|
+
const TYPES$1 = /* @__PURE__ */ new Set(["feature", "bug"]);
|
|
377
377
|
const PRIORITIES$1 = /* @__PURE__ */ new Set([
|
|
378
378
|
"low",
|
|
379
379
|
"medium",
|
|
@@ -419,12 +419,14 @@ async function create(flags, positional) {
|
|
|
419
419
|
const title = flags.get("title") ?? positional[0];
|
|
420
420
|
if (!title) fail("A ticket title is required.", `Pass --title "Invite emails aren't localized", or give it as the first argument.`);
|
|
421
421
|
const type = flags.get("type");
|
|
422
|
-
if (type && !TYPES.has(type)) fail(`Unknown type "${type}".`, "Expected feature or bug.");
|
|
422
|
+
if (type && !TYPES$1.has(type)) fail(`Unknown type "${type}".`, "Expected feature or bug.");
|
|
423
423
|
const priority = flags.get("priority");
|
|
424
424
|
if (priority && !PRIORITIES$1.has(priority)) fail(`Unknown priority "${priority}".`, "Expected low, medium, high, or urgent.");
|
|
425
425
|
const description = (flags.get("description") || await readStdin()).trim() || void 0;
|
|
426
426
|
const assignee = flags.get("assignee");
|
|
427
427
|
const sprint = flags.get("sprint");
|
|
428
|
+
const labelsFlag = flags.get("labels");
|
|
429
|
+
const labels = labelsFlag ? labelsFlag.split(",").map((label) => label.trim()).filter(Boolean) : void 0;
|
|
428
430
|
let result;
|
|
429
431
|
try {
|
|
430
432
|
result = await post(credentials.url, "/api/agent/tickets/create", {
|
|
@@ -434,6 +436,7 @@ async function create(flags, positional) {
|
|
|
434
436
|
description,
|
|
435
437
|
type,
|
|
436
438
|
priority,
|
|
439
|
+
labels,
|
|
437
440
|
parentRef,
|
|
438
441
|
assignee,
|
|
439
442
|
sprint
|
|
@@ -961,6 +964,35 @@ you're already touching doesn't need a ticket, and not everything you notice
|
|
|
961
964
|
belongs on the board. File it when it's something the team would actually
|
|
962
965
|
want to see and decide on — not for every passing thought.
|
|
963
966
|
|
|
967
|
+
## Changing a ticket
|
|
968
|
+
|
|
969
|
+
Every field the board's own ticket dialog can change, you can change from
|
|
970
|
+
here — when the user asks for it, or when what you learned working the ticket
|
|
971
|
+
makes its own fields wrong:
|
|
972
|
+
|
|
973
|
+
npx neuvikon update --ticket EWWQ-1 --assignee Ogulcan --priority high
|
|
974
|
+
|
|
975
|
+
One flag per field, as many at once as you like: \`--assignee\`, \`--team\`,
|
|
976
|
+
\`--sprint\`, \`--type\` (feature or bug), \`--priority\` (low, medium, high,
|
|
977
|
+
urgent), \`--labels\` (comma-separated, replaces the whole set), \`--title\`,
|
|
978
|
+
\`--description\`. People, teams and sprints are named the way a person says
|
|
979
|
+
them and resolved on the server, which refuses an ambiguous name instead of
|
|
980
|
+
guessing — so pass through what the user actually said rather than an id.
|
|
981
|
+
|
|
982
|
+
\`none\` clears a reference, an empty value clears free text, and
|
|
983
|
+
\`--description -\` reads stdin, for one too long to put on the command line:
|
|
984
|
+
|
|
985
|
+
npx neuvikon update --ticket EWWQ-1 --assignee none --labels ""
|
|
986
|
+
|
|
987
|
+
npx neuvikon update --ticket EWWQ-1 --description - <<'EOF'
|
|
988
|
+
What this ticket actually turned out to be about.
|
|
989
|
+
EOF
|
|
990
|
+
|
|
991
|
+
Two changes \`update\` deliberately won't make: moving the ticket to another
|
|
992
|
+
column (that's \`move\`, which makes you say why) and deleting it. And don't
|
|
993
|
+
retitle or rewrite a ticket just to tidy it up — do that when it's actually
|
|
994
|
+
wrong or the user asked, and say what you changed.
|
|
995
|
+
|
|
964
996
|
## Auditing the project on request
|
|
965
997
|
|
|
966
998
|
Sometimes the ask isn't "work this ticket" but "find what's wrong with this
|
|
@@ -1919,14 +1951,17 @@ async function stop(flags) {
|
|
|
1919
1951
|
}
|
|
1920
1952
|
//#endregion
|
|
1921
1953
|
//#region src/commands/update.ts
|
|
1954
|
+
const TYPES = /* @__PURE__ */ new Set(["feature", "bug"]);
|
|
1922
1955
|
const PRIORITIES = /* @__PURE__ */ new Set([
|
|
1923
1956
|
"low",
|
|
1924
1957
|
"medium",
|
|
1925
1958
|
"high",
|
|
1926
1959
|
"urgent"
|
|
1927
1960
|
]);
|
|
1928
|
-
/** What `--assignee none` / `--sprint none` means: clear the field. */
|
|
1961
|
+
/** What `--assignee none` / `--sprint none` / `--team none` means: clear the field. */
|
|
1929
1962
|
const NONE = "none";
|
|
1963
|
+
/** `--description -` reads stdin, so a heredoc can carry a long one. */
|
|
1964
|
+
const STDIN = "-";
|
|
1930
1965
|
function requireCredentials() {
|
|
1931
1966
|
const credentials = readCredentials();
|
|
1932
1967
|
if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
|
|
@@ -1941,23 +1976,46 @@ function resolveTicketRef(flags) {
|
|
|
1941
1976
|
function resolveClearable(flags, name) {
|
|
1942
1977
|
const value = flags.get(name);
|
|
1943
1978
|
if (value === void 0) return void 0;
|
|
1979
|
+
if (!value) fail(`--${name} needs a value.`, `Pass a name, or "${NONE}" to clear it.`);
|
|
1944
1980
|
return value.toLowerCase() === NONE ? null : value;
|
|
1945
1981
|
}
|
|
1982
|
+
/** A flag with a fixed set of values — caught here so the error names the choices. */
|
|
1983
|
+
function resolveChoice(flags, name, allowed, expected) {
|
|
1984
|
+
const value = flags.get(name);
|
|
1985
|
+
if (value === void 0) return void 0;
|
|
1986
|
+
if (!allowed.has(value)) fail(`Unknown ${name} "${value}".`, `Expected ${expected}.`);
|
|
1987
|
+
return value;
|
|
1988
|
+
}
|
|
1989
|
+
async function resolveDescription(flags) {
|
|
1990
|
+
const value = flags.get("description");
|
|
1991
|
+
if (value !== STDIN) return value;
|
|
1992
|
+
const piped = await readStdin();
|
|
1993
|
+
if (!piped.trim()) fail("Nothing arrived on stdin for --description.", `Pipe the text in, or pass --description "the text" directly.`);
|
|
1994
|
+
return piped;
|
|
1995
|
+
}
|
|
1946
1996
|
/**
|
|
1947
|
-
*
|
|
1948
|
-
* `npx neuvikon update --ticket NS-91 --assignee Ogulcan --
|
|
1949
|
-
*
|
|
1997
|
+
* Everything the board's ticket dialog can change, from a terminal, e.g.
|
|
1998
|
+
* `npx neuvikon update --ticket NS-91 --assignee Ogulcan --priority high`.
|
|
1999
|
+
* People, sprints and teams are named the way a person would say them and
|
|
2000
|
+
* resolved server-side; `none` clears one of those three, and an empty value
|
|
2001
|
+
* clears labels or the description.
|
|
2002
|
+
*
|
|
2003
|
+
* Which column the ticket sits in is `move`'s job — that one needs a reason.
|
|
1950
2004
|
*/
|
|
1951
2005
|
async function update(flags) {
|
|
1952
2006
|
const credentials = requireCredentials();
|
|
1953
2007
|
const ticketRef = resolveTicketRef(flags);
|
|
1954
2008
|
const assignee = resolveClearable(flags, "assignee");
|
|
1955
2009
|
const sprint = resolveClearable(flags, "sprint");
|
|
1956
|
-
const
|
|
1957
|
-
|
|
2010
|
+
const team = resolveClearable(flags, "team");
|
|
2011
|
+
const type = resolveChoice(flags, "type", TYPES, "feature or bug");
|
|
2012
|
+
const priority = resolveChoice(flags, "priority", PRIORITIES, "low, medium, high, or urgent");
|
|
1958
2013
|
const labelsFlag = flags.get("labels");
|
|
1959
2014
|
const labels = labelsFlag !== void 0 ? labelsFlag.split(",").map((label) => label.trim()).filter(Boolean) : void 0;
|
|
1960
|
-
|
|
2015
|
+
const title = flags.get("title");
|
|
2016
|
+
if (title !== void 0 && !title.trim()) fail("A ticket can't have an empty title.", `Pass --title "What it should say instead".`);
|
|
2017
|
+
const description = await resolveDescription(flags);
|
|
2018
|
+
if (assignee === void 0 && sprint === void 0 && team === void 0 && type === void 0 && priority === void 0 && labels === void 0 && title === void 0 && description === void 0) fail("Nothing to update.", "Pass at least one of --assignee, --sprint, --team, --type, --priority, --labels, --title, --description.");
|
|
1961
2019
|
let result;
|
|
1962
2020
|
try {
|
|
1963
2021
|
result = await post(credentials.url, "/api/agent/tickets/update", {
|
|
@@ -1965,8 +2023,12 @@ async function update(flags) {
|
|
|
1965
2023
|
ticketRef,
|
|
1966
2024
|
assignee,
|
|
1967
2025
|
sprint,
|
|
2026
|
+
team,
|
|
2027
|
+
type,
|
|
1968
2028
|
priority,
|
|
1969
|
-
labels
|
|
2029
|
+
labels,
|
|
2030
|
+
title,
|
|
2031
|
+
description
|
|
1970
2032
|
});
|
|
1971
2033
|
} catch (error) {
|
|
1972
2034
|
if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
|
|
@@ -2002,11 +2064,13 @@ ${bold("Board")}
|
|
|
2002
2064
|
File something you noticed as its own ticket, same project
|
|
2003
2065
|
npx neuvikon create "Add French copy" --sub
|
|
2004
2066
|
...or as a sub-ticket (--sub for this one, --parent NS-12 for another)
|
|
2005
|
-
npx neuvikon create "..." --assignee Ogulcan --sprint "Sprint 1"
|
|
2006
|
-
File it already assigned
|
|
2007
|
-
npx neuvikon update --assignee Ogulcan --
|
|
2008
|
-
Change
|
|
2009
|
-
|
|
2067
|
+
npx neuvikon create "..." --assignee Ogulcan --sprint "Sprint 1" --labels "api,auth"
|
|
2068
|
+
File it already assigned, planned and labelled
|
|
2069
|
+
npx neuvikon update --assignee Ogulcan --priority high
|
|
2070
|
+
Change any field on an existing ticket: --assignee, --team,
|
|
2071
|
+
--sprint, --type, --priority, --labels, --title, --description
|
|
2072
|
+
("none" clears assignee/team/sprint, "" clears labels/description,
|
|
2073
|
+
--description - reads stdin)
|
|
2010
2074
|
All five accept --ticket EWWQ-1 instead of relying on a tracked session.
|
|
2011
2075
|
|
|
2012
2076
|
${bold("Project pages")}
|