neuvikon 0.2.26 → 0.2.28

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 +63 -20
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -22,11 +22,21 @@ var NetworkError = class extends Error {
22
22
  };
23
23
  const TIMEOUT_MS = 15e3;
24
24
  /**
25
- * The one place an HTTP failure becomes a typed error. Callers decide what a
26
- * failure means — a hook wrapping the agent's turn wants to swallow it, `login`
27
- * wants to shout about it.
25
+ * A long-running ticket's `stop`/`report` calls matter more than a quick
26
+ * command's — losing one of those to a blip on a flaky connection is what
27
+ * leaves a session "active" server-side but silently stale on the board,
28
+ * since nothing else will ever tell the server this session ended. Three
29
+ * tries with a short, fixed backoff absorbs a transient blip (DNS hiccup,
30
+ * one dropped packet) without retrying forever: a genuinely dead connection
31
+ * still fails after three `TIMEOUT_MS` waits plus two short delays, same as
32
+ * it always would have, just with two extra chances to succeed first.
28
33
  */
29
- async function post(url, path, body) {
34
+ const RETRY_ATTEMPTS = 3;
35
+ const RETRY_DELAY_MS = 1e3;
36
+ function sleep$1(ms) {
37
+ return new Promise((resolve) => setTimeout(resolve, ms));
38
+ }
39
+ async function postOnce(url, path, body) {
30
40
  let response;
31
41
  try {
32
42
  response = await fetch(`${url}${path}`, {
@@ -43,6 +53,24 @@ async function post(url, path, body) {
43
53
  if (!response.ok) throw new ApiError(typeof error === "string" ? error : `Request failed with status ${response.status}`, response.status);
44
54
  return payload;
45
55
  }
56
+ /**
57
+ * The one place an HTTP failure becomes a typed error. Callers decide what a
58
+ * failure means — a hook wrapping the agent's turn wants to swallow it, `login`
59
+ * wants to shout about it.
60
+ *
61
+ * Retries only on `NetworkError` — the request never reached the server, so
62
+ * trying again can only help. An `ApiError` means the server answered (a bad
63
+ * token, an unknown ticket, a validation failure); retrying that gets the
64
+ * same answer every time, so it still fails on the first try.
65
+ */
66
+ async function post(url, path, body) {
67
+ for (let attempt = 1;; attempt++) try {
68
+ return await postOnce(url, path, body);
69
+ } catch (error) {
70
+ if (!(error instanceof NetworkError) || attempt >= RETRY_ATTEMPTS) throw error;
71
+ await sleep$1(RETRY_DELAY_MS);
72
+ }
73
+ }
46
74
  function normalizeUrl(url) {
47
75
  return url.trim().replace(/\/+$/, "");
48
76
  }
@@ -828,14 +856,22 @@ When the user hands you a Neuvikon ticket — text beginning with a reference li
828
856
 
829
857
  npx neuvikon start --ticket EWWQ-1
830
858
 
831
- Before doing anything else, judge how difficult the ticket actually is. If
832
- it's a small, mechanical change — swapping button placement, a copy/label
833
- edit, a trivial styling tweak, a one-line config change — switch to a
834
- cheaper/faster model for it if your CLI supports doing so mid-session (e.g.
835
- \`/model haiku\`): there's no reason to spend a frontier model's tokens on
836
- something that small. If it needs real design judgment, touches multiple
837
- systems, or you're unsure, stay on your current model — don't downgrade
838
- just to save cost on something that actually needs the stronger model.
859
+ Before doing anything else, judge how difficult the ticket actually is, and
860
+ match both your model and your reasoning effort to it instead of defaulting
861
+ to maximum for everything:
862
+
863
+ - Small, mechanical change — swapping button placement, a copy/label edit, a
864
+ trivial styling tweak, a one-line config change: switch to a cheaper/faster
865
+ model if your CLI supports doing so mid-session (e.g. \`/model haiku\`), and
866
+ drop reasoning effort to its lowest setting (e.g. \`/effort low\` in Claude
867
+ Code, \`/reasoning low\` in Codex). There's no reason to spend a frontier
868
+ model thinking hard about something this small.
869
+ - Needs real design judgment, touches multiple systems, or you're unsure:
870
+ stay on your current model and effort level — don't downgrade either just
871
+ to save cost on something that actually needs it.
872
+
873
+ If your CLI doesn't expose a way to change model or effort mid-session,
874
+ skip this and just work the ticket.
839
875
 
840
876
  Before you start, and any time you need more context, read the ticket's
841
877
  project pages — this is the project's accumulated knowledge, not just this
@@ -955,16 +991,23 @@ Then decide, based on what's actually true, not on finishing your turn:
955
991
  npx neuvikon comment --ticket EWWQ-1 --text "Handing off — blocked on design"
956
992
 
957
993
  A ticket that isn't actually finished should never land on a terminal column
958
- just because you're done for now. When you finish that ticket, or the user
959
- moves you to a different one, also run:
994
+ just because you're done for now.
995
+
996
+ **Whenever you stop working a ticket — finished or not — \`move\`/\`comment\`
997
+ always comes first, \`stop\` always comes last.** \`stop\` only tells the team
998
+ you're no longer working on it; it does not move the ticket, and running it
999
+ before the move leaves the board showing stale status for a ticket nobody's
1000
+ touching anymore. So the last two things you do on a ticket, in this order,
1001
+ are:
960
1002
 
961
- npx neuvikon stop
1003
+ 1. \`npx neuvikon move ...\` (finished) or \`npx neuvikon comment ...\`
1004
+ (not finished) — reflect the real status first.
1005
+ 2. \`npx neuvikon stop\` — only after step 1, whether you're done or just
1006
+ handing off.
962
1007
 
963
- \`stop\` only tells the team you're no longer working on it — it does not move
964
- the ticket, since stopping and finishing are not the same thing. This is
965
- bookkeeping that tells the user's team which ticket you are on. You do not
966
- need to narrate these commands, and if one fails, carry on with the actual
967
- work — never let it block you.`;
1008
+ This is bookkeeping that tells the user's team which ticket you are on. You
1009
+ do not need to narrate these commands, and if one fails, carry on with the
1010
+ actual work — never let it block you.`;
968
1011
  function instructionBlock() {
969
1012
  return `${START_MARKER}\n${INSTRUCTION_BODY}\n${END_MARKER}`;
970
1013
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuvikon",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Tell Neuvikon which ticket your coding agent is working on.",
5
5
  "keywords": [
6
6
  "neuvikon",