neuvikon 0.2.31 → 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 +84 -16
- 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
|
|
@@ -750,7 +753,7 @@ function parsePayload(raw) {
|
|
|
750
753
|
* in front of the model at the one moment it's actually relevant instead of
|
|
751
754
|
* buried in a rules file next to a dozen other instructions.
|
|
752
755
|
*/
|
|
753
|
-
const MODEL_TRIAGE_CONTEXT = "A Neuvikon ticket just landed in this prompt. Before reading any file or running `start`: judge how difficult it actually is. If it's a small, mechanical change (button placement, a copy/label edit, a trivial styling tweak, a one-line config change), send `/model haiku` and `/effort low` now, as their own messages, before touching the ticket — there's no reason to spend a frontier model thinking hard about something this small. If it needs real design judgment, touches multiple systems, or you're unsure, stay on the current model and effort level instead.";
|
|
756
|
+
const MODEL_TRIAGE_CONTEXT = "A Neuvikon ticket just landed in this prompt. Before reading any file or running `start`: judge how difficult it actually is. If it's a small, mechanical change (button placement, a copy/label edit, a trivial styling tweak, a one-line config change), send `/model haiku` and `/effort low` now, as their own messages, before touching the ticket — there's no reason to spend a frontier model thinking hard about something this small. If it needs real design judgment, touches multiple systems, or you're unsure, stay on the current model and effort level instead. Either way, don't stop and wait after sending those commands — keep going immediately into the ticket itself in the same turn, exactly as if the model/effort switch were one more step in starting the ticket, not the end of your response.";
|
|
754
757
|
function hookResponse(additionalContext) {
|
|
755
758
|
return JSON.stringify({ hookSpecificOutput: {
|
|
756
759
|
hookEventName: "UserPromptSubmit",
|
|
@@ -898,6 +901,10 @@ on its own, before you touch the ticket:
|
|
|
898
901
|
- Your CLI doesn't expose a way to change model or effort mid-session: skip
|
|
899
902
|
this and go straight to the ticket.
|
|
900
903
|
|
|
904
|
+
Don't stop and wait after sending a \`/model\`/\`/effort\` command — continue
|
|
905
|
+
immediately into the ticket in the same turn, as one more step in starting
|
|
906
|
+
it, not as the end of your response.
|
|
907
|
+
|
|
901
908
|
Only after that's decided, run this before you start working on the ticket:
|
|
902
909
|
|
|
903
910
|
npx neuvikon start --ticket EWWQ-1
|
|
@@ -957,6 +964,35 @@ you're already touching doesn't need a ticket, and not everything you notice
|
|
|
957
964
|
belongs on the board. File it when it's something the team would actually
|
|
958
965
|
want to see and decide on — not for every passing thought.
|
|
959
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
|
+
|
|
960
996
|
## Auditing the project on request
|
|
961
997
|
|
|
962
998
|
Sometimes the ask isn't "work this ticket" but "find what's wrong with this
|
|
@@ -1915,14 +1951,17 @@ async function stop(flags) {
|
|
|
1915
1951
|
}
|
|
1916
1952
|
//#endregion
|
|
1917
1953
|
//#region src/commands/update.ts
|
|
1954
|
+
const TYPES = /* @__PURE__ */ new Set(["feature", "bug"]);
|
|
1918
1955
|
const PRIORITIES = /* @__PURE__ */ new Set([
|
|
1919
1956
|
"low",
|
|
1920
1957
|
"medium",
|
|
1921
1958
|
"high",
|
|
1922
1959
|
"urgent"
|
|
1923
1960
|
]);
|
|
1924
|
-
/** What `--assignee none` / `--sprint none` means: clear the field. */
|
|
1961
|
+
/** What `--assignee none` / `--sprint none` / `--team none` means: clear the field. */
|
|
1925
1962
|
const NONE = "none";
|
|
1963
|
+
/** `--description -` reads stdin, so a heredoc can carry a long one. */
|
|
1964
|
+
const STDIN = "-";
|
|
1926
1965
|
function requireCredentials() {
|
|
1927
1966
|
const credentials = readCredentials();
|
|
1928
1967
|
if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
|
|
@@ -1937,23 +1976,46 @@ function resolveTicketRef(flags) {
|
|
|
1937
1976
|
function resolveClearable(flags, name) {
|
|
1938
1977
|
const value = flags.get(name);
|
|
1939
1978
|
if (value === void 0) return void 0;
|
|
1979
|
+
if (!value) fail(`--${name} needs a value.`, `Pass a name, or "${NONE}" to clear it.`);
|
|
1940
1980
|
return value.toLowerCase() === NONE ? null : value;
|
|
1941
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
|
+
}
|
|
1942
1996
|
/**
|
|
1943
|
-
*
|
|
1944
|
-
* `npx neuvikon update --ticket NS-91 --assignee Ogulcan --
|
|
1945
|
-
*
|
|
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.
|
|
1946
2004
|
*/
|
|
1947
2005
|
async function update(flags) {
|
|
1948
2006
|
const credentials = requireCredentials();
|
|
1949
2007
|
const ticketRef = resolveTicketRef(flags);
|
|
1950
2008
|
const assignee = resolveClearable(flags, "assignee");
|
|
1951
2009
|
const sprint = resolveClearable(flags, "sprint");
|
|
1952
|
-
const
|
|
1953
|
-
|
|
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");
|
|
1954
2013
|
const labelsFlag = flags.get("labels");
|
|
1955
2014
|
const labels = labelsFlag !== void 0 ? labelsFlag.split(",").map((label) => label.trim()).filter(Boolean) : void 0;
|
|
1956
|
-
|
|
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.");
|
|
1957
2019
|
let result;
|
|
1958
2020
|
try {
|
|
1959
2021
|
result = await post(credentials.url, "/api/agent/tickets/update", {
|
|
@@ -1961,8 +2023,12 @@ async function update(flags) {
|
|
|
1961
2023
|
ticketRef,
|
|
1962
2024
|
assignee,
|
|
1963
2025
|
sprint,
|
|
2026
|
+
team,
|
|
2027
|
+
type,
|
|
1964
2028
|
priority,
|
|
1965
|
-
labels
|
|
2029
|
+
labels,
|
|
2030
|
+
title,
|
|
2031
|
+
description
|
|
1966
2032
|
});
|
|
1967
2033
|
} catch (error) {
|
|
1968
2034
|
if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
|
|
@@ -1998,11 +2064,13 @@ ${bold("Board")}
|
|
|
1998
2064
|
File something you noticed as its own ticket, same project
|
|
1999
2065
|
npx neuvikon create "Add French copy" --sub
|
|
2000
2066
|
...or as a sub-ticket (--sub for this one, --parent NS-12 for another)
|
|
2001
|
-
npx neuvikon create "..." --assignee Ogulcan --sprint "Sprint 1"
|
|
2002
|
-
File it already assigned
|
|
2003
|
-
npx neuvikon update --assignee Ogulcan --
|
|
2004
|
-
Change
|
|
2005
|
-
|
|
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)
|
|
2006
2074
|
All five accept --ticket EWWQ-1 instead of relying on a tracked session.
|
|
2007
2075
|
|
|
2008
2076
|
${bold("Project pages")}
|