neuvikon 0.2.30 → 0.2.32

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 (2) hide show
  1. package/dist/index.mjs +104 -29
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -276,12 +276,12 @@ function fail(message, hint) {
276
276
  }
277
277
  //#endregion
278
278
  //#region src/commands/board.ts
279
- function requireCredentials$4() {
279
+ function requireCredentials$5() {
280
280
  const credentials = readCredentials();
281
281
  if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
282
282
  return credentials;
283
283
  }
284
- function resolveTicketRef$4(flags) {
284
+ function resolveTicketRef$5(flags) {
285
285
  const ref = flags.get("ticket") ?? readSession()?.ticket;
286
286
  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.");
287
287
  return ref;
@@ -291,8 +291,8 @@ function resolveTicketRef$4(flags) {
291
291
  * a column name — boards vary per project, and some have no "done" at all.
292
292
  */
293
293
  async function board(flags) {
294
- const credentials = requireCredentials$4();
295
- const ticketRef = resolveTicketRef$4(flags);
294
+ const credentials = requireCredentials$5();
295
+ const ticketRef = resolveTicketRef$5(flags);
296
296
  let result;
297
297
  try {
298
298
  result = await post(credentials.url, "/api/agent/board", {
@@ -339,20 +339,20 @@ async function readStdin() {
339
339
  }
340
340
  //#endregion
341
341
  //#region src/commands/comment.ts
342
- function requireCredentials$3() {
342
+ function requireCredentials$4() {
343
343
  const credentials = readCredentials();
344
344
  if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
345
345
  return credentials;
346
346
  }
347
- function resolveTicketRef$3(flags) {
347
+ function resolveTicketRef$4(flags) {
348
348
  const ref = flags.get("ticket") ?? readSession()?.ticket;
349
349
  if (!ref) fail("No ticket to comment on.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
350
350
  return ref;
351
351
  }
352
352
  /** A note on a ticket that doesn't move it — e.g. explaining a handoff mid-ticket. */
353
353
  async function comment(flags) {
354
- const credentials = requireCredentials$3();
355
- const ticketRef = resolveTicketRef$3(flags);
354
+ const credentials = requireCredentials$4();
355
+ const ticketRef = resolveTicketRef$4(flags);
356
356
  const text = (flags.get("text") || await readStdin()).trim();
357
357
  if (!text) fail("No comment text given.", "Pipe the comment in on stdin, or pass --text.");
358
358
  try {
@@ -374,18 +374,18 @@ async function comment(flags) {
374
374
  //#endregion
375
375
  //#region src/commands/create.ts
376
376
  const TYPES = /* @__PURE__ */ new Set(["feature", "bug"]);
377
- const PRIORITIES = /* @__PURE__ */ new Set([
377
+ const PRIORITIES$1 = /* @__PURE__ */ new Set([
378
378
  "low",
379
379
  "medium",
380
380
  "high",
381
381
  "urgent"
382
382
  ]);
383
- function requireCredentials$2() {
383
+ function requireCredentials$3() {
384
384
  const credentials = readCredentials();
385
385
  if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
386
386
  return credentials;
387
387
  }
388
- function resolveTicketRef$2(flags) {
388
+ function resolveTicketRef$3(flags) {
389
389
  const ref = flags.get("ticket") ?? readSession()?.ticket;
390
390
  if (!ref) fail("No ticket to file this against.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
391
391
  return ref;
@@ -413,16 +413,18 @@ function resolveParentRef(flags, ticketRef) {
413
413
  * own "+ sub-ticket" control.
414
414
  */
415
415
  async function create(flags, positional) {
416
- const credentials = requireCredentials$2();
417
- const ticketRef = resolveTicketRef$2(flags);
416
+ const credentials = requireCredentials$3();
417
+ const ticketRef = resolveTicketRef$3(flags);
418
418
  const parentRef = resolveParentRef(flags, ticketRef);
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
422
  if (type && !TYPES.has(type)) fail(`Unknown type "${type}".`, "Expected feature or bug.");
423
423
  const priority = flags.get("priority");
424
- if (priority && !PRIORITIES.has(priority)) fail(`Unknown priority "${priority}".`, "Expected low, medium, high, or urgent.");
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
+ const assignee = flags.get("assignee");
427
+ const sprint = flags.get("sprint");
426
428
  let result;
427
429
  try {
428
430
  result = await post(credentials.url, "/api/agent/tickets/create", {
@@ -432,7 +434,9 @@ async function create(flags, positional) {
432
434
  description,
433
435
  type,
434
436
  priority,
435
- parentRef
437
+ parentRef,
438
+ assignee,
439
+ sprint
436
440
  });
437
441
  } catch (error) {
438
442
  if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
@@ -746,7 +750,7 @@ function parsePayload(raw) {
746
750
  * in front of the model at the one moment it's actually relevant instead of
747
751
  * buried in a rules file next to a dozen other instructions.
748
752
  */
749
- 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.";
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. 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.";
750
754
  function hookResponse(additionalContext) {
751
755
  return JSON.stringify({ hookSpecificOutput: {
752
756
  hookEventName: "UserPromptSubmit",
@@ -894,6 +898,10 @@ on its own, before you touch the ticket:
894
898
  - Your CLI doesn't expose a way to change model or effort mid-session: skip
895
899
  this and go straight to the ticket.
896
900
 
901
+ Don't stop and wait after sending a \`/model\`/\`/effort\` command — continue
902
+ immediately into the ticket in the same turn, as one more step in starting
903
+ it, not as the end of your response.
904
+
897
905
  Only after that's decided, run this before you start working on the ticket:
898
906
 
899
907
  npx neuvikon start --ticket EWWQ-1
@@ -1294,12 +1302,12 @@ async function logout() {
1294
1302
  }
1295
1303
  //#endregion
1296
1304
  //#region src/commands/move.ts
1297
- function requireCredentials$1() {
1305
+ function requireCredentials$2() {
1298
1306
  const credentials = readCredentials();
1299
1307
  if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
1300
1308
  return credentials;
1301
1309
  }
1302
- function resolveTicketRef$1(flags) {
1310
+ function resolveTicketRef$2(flags) {
1303
1311
  const ref = flags.get("ticket") ?? readSession()?.ticket;
1304
1312
  if (!ref) fail("No ticket to move.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
1305
1313
  return ref;
@@ -1311,8 +1319,8 @@ function resolveTicketRef$1(flags) {
1311
1319
  * that silently.
1312
1320
  */
1313
1321
  async function move(flags) {
1314
- const credentials = requireCredentials$1();
1315
- const ticketRef = resolveTicketRef$1(flags);
1322
+ const credentials = requireCredentials$2();
1323
+ const ticketRef = resolveTicketRef$2(flags);
1316
1324
  const column = flags.get("column");
1317
1325
  if (!column) fail("A target column is required.", `Pass --column "Code Review" — run \`npx neuvikon board\` first if you're not sure what this project calls it.`);
1318
1326
  const comment = (flags.get("comment") || await readStdin()).trim();
@@ -1337,13 +1345,13 @@ async function move(flags) {
1337
1345
  }
1338
1346
  //#endregion
1339
1347
  //#region src/commands/pages.ts
1340
- function requireCredentials() {
1348
+ function requireCredentials$1() {
1341
1349
  const credentials = readCredentials();
1342
1350
  if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
1343
1351
  return credentials;
1344
1352
  }
1345
1353
  /** `--ticket` wins; otherwise fall back to whatever this directory is already tracking. */
1346
- function resolveTicketRef(flags) {
1354
+ function resolveTicketRef$1(flags) {
1347
1355
  const ref = flags.get("ticket") ?? readSession()?.ticket;
1348
1356
  if (!ref) fail("No ticket to look up pages for.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
1349
1357
  return ref;
@@ -1362,8 +1370,8 @@ async function request(credentials, path, body) {
1362
1370
  }
1363
1371
  }
1364
1372
  async function list(flags) {
1365
- const credentials = requireCredentials();
1366
- const ticketRef = resolveTicketRef(flags);
1373
+ const credentials = requireCredentials$1();
1374
+ const ticketRef = resolveTicketRef$1(flags);
1367
1375
  const { pages } = await request(credentials, "/api/agent/pages", {
1368
1376
  token: credentials.token,
1369
1377
  ticketRef
@@ -1375,9 +1383,9 @@ async function list(flags) {
1375
1383
  const body = pages.map((page) => `# ${page.title}\n\n${page.text}`).join("\n\n---\n\n");
1376
1384
  process.stdout.write(`${body}\n`);
1377
1385
  }
1378
- async function update(flags, positional) {
1379
- const credentials = requireCredentials();
1380
- const ticketRef = resolveTicketRef(flags);
1386
+ async function update$1(flags, positional) {
1387
+ const credentials = requireCredentials$1();
1388
+ const ticketRef = resolveTicketRef$1(flags);
1381
1389
  const title = flags.get("title") ?? positional[0];
1382
1390
  if (!title) fail("A page title is required.", `Pass --title "Auth flow notes", or give it as the first argument.`);
1383
1391
  const text = (flags.get("text") || await readStdin()).trim();
@@ -1394,7 +1402,7 @@ async function pages(flags, positional) {
1394
1402
  switch (subcommand) {
1395
1403
  case void 0:
1396
1404
  case "list": return list(flags);
1397
- case "update": return update(flags, rest);
1405
+ case "update": return update$1(flags, rest);
1398
1406
  default: fail(`Unknown "pages ${subcommand}".`, "Try `pages` or `pages update`.");
1399
1407
  }
1400
1408
  }
@@ -1910,6 +1918,67 @@ async function stop(flags) {
1910
1918
  success(`Stopped tracking ${session.ticket}`);
1911
1919
  }
1912
1920
  //#endregion
1921
+ //#region src/commands/update.ts
1922
+ const PRIORITIES = /* @__PURE__ */ new Set([
1923
+ "low",
1924
+ "medium",
1925
+ "high",
1926
+ "urgent"
1927
+ ]);
1928
+ /** What `--assignee none` / `--sprint none` means: clear the field. */
1929
+ const NONE = "none";
1930
+ function requireCredentials() {
1931
+ const credentials = readCredentials();
1932
+ if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
1933
+ return credentials;
1934
+ }
1935
+ function resolveTicketRef(flags) {
1936
+ const ref = flags.get("ticket") ?? readSession()?.ticket;
1937
+ if (!ref) fail("No ticket to update.", "Pass --ticket EWWQ-1, or run this where `npx neuvikon start` is already tracking one.");
1938
+ return ref;
1939
+ }
1940
+ /** `undefined` means "leave it alone", `null` means "clear it", a string means "set it to this". */
1941
+ function resolveClearable(flags, name) {
1942
+ const value = flags.get(name);
1943
+ if (value === void 0) return void 0;
1944
+ return value.toLowerCase() === NONE ? null : value;
1945
+ }
1946
+ /**
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.
1950
+ */
1951
+ async function update(flags) {
1952
+ const credentials = requireCredentials();
1953
+ const ticketRef = resolveTicketRef(flags);
1954
+ const assignee = resolveClearable(flags, "assignee");
1955
+ 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.");
1958
+ const labelsFlag = flags.get("labels");
1959
+ 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.");
1961
+ let result;
1962
+ try {
1963
+ result = await post(credentials.url, "/api/agent/tickets/update", {
1964
+ token: credentials.token,
1965
+ ticketRef,
1966
+ assignee,
1967
+ sprint,
1968
+ priority,
1969
+ labels
1970
+ });
1971
+ } catch (error) {
1972
+ if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
1973
+ if (error instanceof ApiError) {
1974
+ if (error.status === 401) fail(error.message, "Run `npx neuvikon login` again.");
1975
+ fail(error.message);
1976
+ }
1977
+ throw error;
1978
+ }
1979
+ success(`Updated ${bold(result.ref)}: ${result.changed.join(", ")}`);
1980
+ }
1981
+ //#endregion
1913
1982
  //#region src/index.ts
1914
1983
  const HELP = `${bold("neuvikon")} — tell Neuvikon which ticket your coding agent is on
1915
1984
 
@@ -1933,7 +2002,12 @@ ${bold("Board")}
1933
2002
  File something you noticed as its own ticket, same project
1934
2003
  npx neuvikon create "Add French copy" --sub
1935
2004
  ...or as a sub-ticket (--sub for this one, --parent NS-12 for another)
1936
- All four accept --ticket EWWQ-1 instead of relying on a tracked session.
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)
2010
+ All five accept --ticket EWWQ-1 instead of relying on a tracked session.
1937
2011
 
1938
2012
  ${bold("Project pages")}
1939
2013
  npx neuvikon pages List the current ticket's project pages
@@ -1995,6 +2069,7 @@ async function main() {
1995
2069
  case "move": return move(flags);
1996
2070
  case "comment": return comment(flags);
1997
2071
  case "create": return create(flags, positional);
2072
+ case "update": return update(flags);
1998
2073
  case void 0:
1999
2074
  case "help":
2000
2075
  case "--help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuvikon",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
4
4
  "description": "Tell Neuvikon which ticket your coding agent is working on.",
5
5
  "keywords": [
6
6
  "neuvikon",