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.
Files changed (3) hide show
  1. package/README.md +37 -0
  2. package/dist/index.mjs +79 -15
  3. 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
- * Changes fields on a ticket that already exists, e.g.
1948
- * `npx neuvikon update --ticket NS-91 --assignee Ogulcan --sprint "Sprint 1"`.
1949
- * `--assignee none` / `--sprint none` clears that field instead of setting it.
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 priority = flags.get("priority");
1957
- if (priority && !PRIORITIES.has(priority)) fail(`Unknown priority "${priority}".`, "Expected low, medium, high, or urgent.");
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
- if (assignee === void 0 && sprint === void 0 && priority === void 0 && labels === void 0) fail("Nothing to update.", "Pass at least one of --assignee, --sprint, --priority, --labels.");
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 and/or planned in a sprint
2007
- npx neuvikon update --assignee Ogulcan --sprint "Sprint 1" --priority high
2008
- Change assignee, sprint, priority, or labels on an existing ticket
2009
- ("none" clears --assignee/--sprint)
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")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuvikon",
3
- "version": "0.2.32",
3
+ "version": "0.2.33",
4
4
  "description": "Tell Neuvikon which ticket your coding agent is working on.",
5
5
  "keywords": [
6
6
  "neuvikon",