neuvikon 0.2.23 → 0.2.24
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 +25 -0
- package/dist/index.mjs +123 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,9 @@ as being worked on, by whom, with which tool.
|
|
|
42
42
|
| `neuvikon logout` | Revoke this machine's token and forget it |
|
|
43
43
|
| `neuvikon pages [--ticket EWWQ-1]` | List the ticket's project pages, flattened to plain text |
|
|
44
44
|
| `neuvikon pages update "Title" [--ticket EWWQ-1]` | Create or update a page by title, body on stdin (or `--text`) |
|
|
45
|
+
| `neuvikon create "Title" [--ticket EWWQ-1]` | File something you noticed as a new ticket, same project |
|
|
46
|
+
| `neuvikon create "Title" --sub [--ticket EWWQ-1]` | ...or as a sub-ticket of the ticket you're on |
|
|
47
|
+
| `neuvikon create "Title" --parent NS-12` | ...or of a different ticket entirely |
|
|
45
48
|
|
|
46
49
|
`start` also accepts the ticket on stdin, and pulls the reference out of it:
|
|
47
50
|
|
|
@@ -74,6 +77,28 @@ always has. Without a detected agent, or on a platform/terminal this can't
|
|
|
74
77
|
drive, it just prints the ticket ref and that command for you to paste by
|
|
75
78
|
hand.
|
|
76
79
|
|
|
80
|
+
## Filing what you notice along the way
|
|
81
|
+
|
|
82
|
+
Working a ticket turns up other things — a bug, a chunk of related work that
|
|
83
|
+
doesn't belong in the same diff. Rather than silently expanding scope or
|
|
84
|
+
silently dropping it, an agent can file it as its own ticket:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npx neuvikon create "Invite emails aren't localized" --ticket EWWQ-1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Or as a sub-ticket, if it's really part of finishing the one you're on:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
npx neuvikon create "Add French copy" --sub --ticket EWWQ-1
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`--parent NS-12` files it under a different ticket instead. Sub-tickets are
|
|
97
|
+
one level deep — a ticket that's already a sub-ticket can't take `--sub`
|
|
98
|
+
itself, same restriction the app's own "+ sub-ticket" control has. Either way
|
|
99
|
+
the ticket you were on gets an automated comment pointing at the new one, so
|
|
100
|
+
nothing gets filed invisibly.
|
|
101
|
+
|
|
77
102
|
## Project pages
|
|
78
103
|
|
|
79
104
|
An agent's most useful output often isn't the ticket, it's what it learned
|
package/dist/index.mjs
CHANGED
|
@@ -248,12 +248,12 @@ function fail(message, hint) {
|
|
|
248
248
|
}
|
|
249
249
|
//#endregion
|
|
250
250
|
//#region src/commands/board.ts
|
|
251
|
-
function requireCredentials$
|
|
251
|
+
function requireCredentials$4() {
|
|
252
252
|
const credentials = readCredentials();
|
|
253
253
|
if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
|
|
254
254
|
return credentials;
|
|
255
255
|
}
|
|
256
|
-
function resolveTicketRef$
|
|
256
|
+
function resolveTicketRef$4(flags) {
|
|
257
257
|
const ref = flags.get("ticket") ?? readSession()?.ticket;
|
|
258
258
|
if (!ref) fail("No ticket to look up a board for.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
|
|
259
259
|
return ref;
|
|
@@ -263,8 +263,8 @@ function resolveTicketRef$3(flags) {
|
|
|
263
263
|
* a column name — boards vary per project, and some have no "done" at all.
|
|
264
264
|
*/
|
|
265
265
|
async function board(flags) {
|
|
266
|
-
const credentials = requireCredentials$
|
|
267
|
-
const ticketRef = resolveTicketRef$
|
|
266
|
+
const credentials = requireCredentials$4();
|
|
267
|
+
const ticketRef = resolveTicketRef$4(flags);
|
|
268
268
|
let result;
|
|
269
269
|
try {
|
|
270
270
|
result = await post(credentials.url, "/api/agent/board", {
|
|
@@ -311,20 +311,20 @@ async function readStdin() {
|
|
|
311
311
|
}
|
|
312
312
|
//#endregion
|
|
313
313
|
//#region src/commands/comment.ts
|
|
314
|
-
function requireCredentials$
|
|
314
|
+
function requireCredentials$3() {
|
|
315
315
|
const credentials = readCredentials();
|
|
316
316
|
if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
|
|
317
317
|
return credentials;
|
|
318
318
|
}
|
|
319
|
-
function resolveTicketRef$
|
|
319
|
+
function resolveTicketRef$3(flags) {
|
|
320
320
|
const ref = flags.get("ticket") ?? readSession()?.ticket;
|
|
321
321
|
if (!ref) fail("No ticket to comment on.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
|
|
322
322
|
return ref;
|
|
323
323
|
}
|
|
324
324
|
/** A note on a ticket that doesn't move it — e.g. explaining a handoff mid-ticket. */
|
|
325
325
|
async function comment(flags) {
|
|
326
|
-
const credentials = requireCredentials$
|
|
327
|
-
const ticketRef = resolveTicketRef$
|
|
326
|
+
const credentials = requireCredentials$3();
|
|
327
|
+
const ticketRef = resolveTicketRef$3(flags);
|
|
328
328
|
const text = (flags.get("text") || await readStdin()).trim();
|
|
329
329
|
if (!text) fail("No comment text given.", "Pipe the comment in on stdin, or pass --text.");
|
|
330
330
|
try {
|
|
@@ -344,6 +344,79 @@ async function comment(flags) {
|
|
|
344
344
|
success(`Commented on ${ticketRef}`);
|
|
345
345
|
}
|
|
346
346
|
//#endregion
|
|
347
|
+
//#region src/commands/create.ts
|
|
348
|
+
const TYPES = /* @__PURE__ */ new Set(["feature", "bug"]);
|
|
349
|
+
const PRIORITIES = /* @__PURE__ */ new Set([
|
|
350
|
+
"low",
|
|
351
|
+
"medium",
|
|
352
|
+
"high",
|
|
353
|
+
"urgent"
|
|
354
|
+
]);
|
|
355
|
+
function requireCredentials$2() {
|
|
356
|
+
const credentials = readCredentials();
|
|
357
|
+
if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
|
|
358
|
+
return credentials;
|
|
359
|
+
}
|
|
360
|
+
function resolveTicketRef$2(flags) {
|
|
361
|
+
const ref = flags.get("ticket") ?? readSession()?.ticket;
|
|
362
|
+
if (!ref) fail("No ticket to file this against.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
|
|
363
|
+
return ref;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* `--sub` means "a sub-ticket of the ticket I'm already on"; `--parent`
|
|
367
|
+
* names a different one. Not both — a new ticket has exactly one parent (or
|
|
368
|
+
* none), and naming it two ways at once is more likely a mistake than intent.
|
|
369
|
+
*/
|
|
370
|
+
function resolveParentRef(flags, ticketRef) {
|
|
371
|
+
const parentFlag = flags.get("parent");
|
|
372
|
+
if (parentFlag && flags.has("sub")) fail("Pass --sub or --parent, not both.", `--sub already means "a sub-ticket of ${ticketRef}".`);
|
|
373
|
+
if (parentFlag) {
|
|
374
|
+
const [ref] = extractTicketRefs(parentFlag);
|
|
375
|
+
if (!ref) fail(`"${parentFlag}" is not a ticket reference.`, "Expected something like EWWQ-1.");
|
|
376
|
+
return ref;
|
|
377
|
+
}
|
|
378
|
+
return flags.has("sub") ? ticketRef : void 0;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* An agent filing something it noticed that isn't the ticket it's on — a bug
|
|
382
|
+
* spotted mid-fix, a chunk of follow-up work worth tracking on its own.
|
|
383
|
+
* Without `--sub`/`--parent` it's a new standalone ticket in the same
|
|
384
|
+
* project; with one of them, a sub-ticket one level deep, same as the app's
|
|
385
|
+
* own "+ sub-ticket" control.
|
|
386
|
+
*/
|
|
387
|
+
async function create(flags, positional) {
|
|
388
|
+
const credentials = requireCredentials$2();
|
|
389
|
+
const ticketRef = resolveTicketRef$2(flags);
|
|
390
|
+
const parentRef = resolveParentRef(flags, ticketRef);
|
|
391
|
+
const title = flags.get("title") ?? positional[0];
|
|
392
|
+
if (!title) fail("A ticket title is required.", `Pass --title "Invite emails aren't localized", or give it as the first argument.`);
|
|
393
|
+
const type = flags.get("type");
|
|
394
|
+
if (type && !TYPES.has(type)) fail(`Unknown type "${type}".`, "Expected feature or bug.");
|
|
395
|
+
const priority = flags.get("priority");
|
|
396
|
+
if (priority && !PRIORITIES.has(priority)) fail(`Unknown priority "${priority}".`, "Expected low, medium, high, or urgent.");
|
|
397
|
+
const description = (flags.get("description") || await readStdin()).trim() || void 0;
|
|
398
|
+
let result;
|
|
399
|
+
try {
|
|
400
|
+
result = await post(credentials.url, "/api/agent/tickets/create", {
|
|
401
|
+
token: credentials.token,
|
|
402
|
+
ticketRef,
|
|
403
|
+
title,
|
|
404
|
+
description,
|
|
405
|
+
type,
|
|
406
|
+
priority,
|
|
407
|
+
parentRef
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
|
|
411
|
+
if (error instanceof ApiError) {
|
|
412
|
+
if (error.status === 401) fail(error.message, "Run `npx neuvikon login` again.");
|
|
413
|
+
fail(error.message);
|
|
414
|
+
}
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
success(result.parentRef ? `Filed ${bold(result.ref)} as a sub-ticket of ${bold(result.parentRef)}` : `Filed ${bold(result.ref)}`);
|
|
418
|
+
}
|
|
419
|
+
//#endregion
|
|
347
420
|
//#region src/usage.ts
|
|
348
421
|
/**
|
|
349
422
|
* Sums token usage out of a JSONL agent transcript, starting from `offset`.
|
|
@@ -774,6 +847,42 @@ creating a duplicate, so reuse a title for anything you're extending.
|
|
|
774
847
|
Reference another page by writing its title in double brackets, e.g.
|
|
775
848
|
\`[[Auth Flow]]\` — it becomes a real link if that page already exists.
|
|
776
849
|
|
|
850
|
+
## Filing work you notice along the way
|
|
851
|
+
|
|
852
|
+
Working a ticket often turns up something else — a bug you tripped over, a
|
|
853
|
+
piece of real work that doesn't belong in this diff. Don't silently fix it if
|
|
854
|
+
it's unrelated, and don't silently drop it either — file it:
|
|
855
|
+
|
|
856
|
+
npx neuvikon create "Session cookie missing Secure flag" --ticket EWWQ-1
|
|
857
|
+
|
|
858
|
+
Decide sub-ticket vs. standalone ticket by whether it's part of finishing
|
|
859
|
+
*this* ticket:
|
|
860
|
+
|
|
861
|
+
- Still part of this ticket — something that has to happen before it's really
|
|
862
|
+
done, e.g. "write tests for the new endpoint" — file it as a sub-ticket of
|
|
863
|
+
the one you're on:
|
|
864
|
+
|
|
865
|
+
npx neuvikon create "Add tests for the invite endpoint" --sub --ticket EWWQ-1
|
|
866
|
+
|
|
867
|
+
- Not part of it — a bug, tech debt, an idea that has nothing to do with
|
|
868
|
+
finishing this ticket — file it as its own ticket and get back to work.
|
|
869
|
+
Fixing it now just because you're already in the file is how one ticket
|
|
870
|
+
quietly becomes three:
|
|
871
|
+
|
|
872
|
+
npx neuvikon create "Invite emails aren't localized" --ticket EWWQ-1
|
|
873
|
+
|
|
874
|
+
\`--parent OTHER-4\` instead of \`--sub\` files it under a different ticket,
|
|
875
|
+
if that's genuinely where it belongs instead of standing alone.
|
|
876
|
+
|
|
877
|
+
A sub-ticket can't itself have sub-tickets — if the ticket you're on is
|
|
878
|
+
already one, \`--sub\` will fail; file a standalone ticket instead and say in
|
|
879
|
+
its description what it's related to.
|
|
880
|
+
|
|
881
|
+
This is judgment, not a rule to apply every turn: a one-line fix in a file
|
|
882
|
+
you're already touching doesn't need a ticket, and not everything you notice
|
|
883
|
+
belongs on the board. File it when it's something the team would actually
|
|
884
|
+
want to see and decide on — not for every passing thought.
|
|
885
|
+
|
|
777
886
|
Before you change the ticket's status, look at what columns this project's
|
|
778
887
|
board actually has — they vary per project, and there may be no column
|
|
779
888
|
literally called "Done":
|
|
@@ -1707,7 +1816,11 @@ ${bold("Board")}
|
|
|
1707
1816
|
Move the ticket to a specific column, with why
|
|
1708
1817
|
npx neuvikon comment --text "Handing off, blocked on design"
|
|
1709
1818
|
Leave a note without changing the column
|
|
1710
|
-
|
|
1819
|
+
npx neuvikon create "Invite emails aren't localized"
|
|
1820
|
+
File something you noticed as its own ticket, same project
|
|
1821
|
+
npx neuvikon create "Add French copy" --sub
|
|
1822
|
+
...or as a sub-ticket (--sub for this one, --parent NS-12 for another)
|
|
1823
|
+
All four accept --ticket EWWQ-1 instead of relying on a tracked session.
|
|
1711
1824
|
|
|
1712
1825
|
${bold("Project pages")}
|
|
1713
1826
|
npx neuvikon pages List the current ticket's project pages
|
|
@@ -1768,6 +1881,7 @@ async function main() {
|
|
|
1768
1881
|
case "board": return board(flags);
|
|
1769
1882
|
case "move": return move(flags);
|
|
1770
1883
|
case "comment": return comment(flags);
|
|
1884
|
+
case "create": return create(flags, positional);
|
|
1771
1885
|
case void 0:
|
|
1772
1886
|
case "help":
|
|
1773
1887
|
case "--help":
|