neuvikon 0.2.13 → 0.2.14

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 CHANGED
@@ -39,6 +39,8 @@ as being worked on, by whom, with which tool.
39
39
  | `neuvikon stop` | Stop tracking |
40
40
  | `neuvikon status` | Login, project setup, current ticket |
41
41
  | `neuvikon logout` | Revoke this machine's token and forget it |
42
+ | `neuvikon pages [--ticket EWWQ-1]` | List the ticket's project pages, flattened to plain text |
43
+ | `neuvikon pages update "Title" [--ticket EWWQ-1]` | Create or update a page by title, body on stdin (or `--text`) |
42
44
 
43
45
  `start` also accepts the ticket on stdin, and pulls the reference out of it:
44
46
 
@@ -46,6 +48,32 @@ as being worked on, by whom, with which tool.
46
48
  pbpaste | npx neuvikon start
47
49
  ```
48
50
 
51
+ ## Project pages
52
+
53
+ An agent's most useful output often isn't the ticket, it's what it learned
54
+ doing it. `pages` reads and writes the same project pages a person sees in the
55
+ app — the running knowledge base for that project, not a per-ticket scratchpad:
56
+
57
+ ```sh
58
+ npx neuvikon pages --ticket EWWQ-1
59
+ ```
60
+
61
+ ```sh
62
+ npx neuvikon pages update "Auth flow" --ticket EWWQ-1 <<'EOF'
63
+ Sessions are issued by convex/auth.ts and expire after 30 days.
64
+ See [[Session storage]] for where they're persisted.
65
+ EOF
66
+ ```
67
+
68
+ An update matches an existing page by title (case-insensitive) and overwrites
69
+ it, so writing to the same title again refines that page instead of piling up
70
+ duplicates. `[[Another Title]]` inside the text becomes a real link to that
71
+ page if a page with that title already exists in the project, same as typing
72
+ it in the editor.
73
+
74
+ Both commands fall back to whichever ticket `npx neuvikon start` is already
75
+ tracking in this directory when `--ticket` is omitted.
76
+
49
77
  ## Token usage
50
78
 
51
79
  `start`/`stop` are all an agent can report about itself — no agent knows its own
@@ -97,4 +125,3 @@ pnpm typecheck
97
125
  ```
98
126
 
99
127
  No runtime dependencies, so `npx neuvikon` stays a fast cold start.
100
- # neuvikon-cli
package/dist/index.mjs CHANGED
@@ -640,6 +640,25 @@ When the user hands you a Neuvikon ticket — text beginning with a reference li
640
640
 
641
641
  npx neuvikon start --ticket EWWQ-1
642
642
 
643
+ Before you start, and any time you need more context, read the ticket's
644
+ project pages — this is the project's accumulated knowledge, not just this
645
+ ticket's:
646
+
647
+ npx neuvikon pages --ticket EWWQ-1
648
+
649
+ When you learn something worth keeping — how a system works, a decision and
650
+ why, anything future work on this project would benefit from — write it back
651
+ so the next agent or teammate has it:
652
+
653
+ npx neuvikon pages update "Some Title" --ticket EWWQ-1 <<'EOF'
654
+ What you learned.
655
+ EOF
656
+
657
+ An update with a title that already exists overwrites that page instead of
658
+ creating a duplicate, so reuse a title for anything you're extending.
659
+ Reference another page by writing its title in double brackets, e.g.
660
+ \`[[Auth Flow]]\` — it becomes a real link if that page already exists.
661
+
643
662
  When you finish that ticket, or the user moves you to a different one, run:
644
663
 
645
664
  npx neuvikon stop
@@ -907,6 +926,69 @@ async function logout() {
907
926
  info(dim(" Could not reach Neuvikon to revoke the token — revoke it in settings if it leaked."));
908
927
  }
909
928
  //#endregion
929
+ //#region src/commands/pages.ts
930
+ function requireCredentials() {
931
+ const credentials = readCredentials();
932
+ if (!credentials) fail("Not logged in.", "Run `npx neuvikon login` first.");
933
+ return credentials;
934
+ }
935
+ /** `--ticket` wins; otherwise fall back to whatever this directory is already tracking. */
936
+ function resolveTicketRef(flags) {
937
+ const ref = flags.get("ticket") ?? readSession()?.ticket;
938
+ 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.");
939
+ return ref;
940
+ }
941
+ /** Both pages subcommands fail the same two ways `start` does; shared so they say so identically. */
942
+ async function request(credentials, path, body) {
943
+ try {
944
+ return await post(credentials.url, path, body);
945
+ } catch (error) {
946
+ if (error instanceof NetworkError) fail("Could not reach Neuvikon.");
947
+ if (error instanceof ApiError) {
948
+ if (error.status === 401) fail(error.message, "Run `npx neuvikon login` again.");
949
+ fail(error.message);
950
+ }
951
+ throw error;
952
+ }
953
+ }
954
+ async function list(flags) {
955
+ const credentials = requireCredentials();
956
+ const ticketRef = resolveTicketRef(flags);
957
+ const { pages } = await request(credentials, "/api/agent/pages", {
958
+ token: credentials.token,
959
+ ticketRef
960
+ });
961
+ if (pages.length === 0) {
962
+ info(dim(`No pages in ${ticketRef}'s project yet.`));
963
+ return;
964
+ }
965
+ const body = pages.map((page) => `# ${page.title}\n\n${page.text}`).join("\n\n---\n\n");
966
+ process.stdout.write(`${body}\n`);
967
+ }
968
+ async function update(flags, positional) {
969
+ const credentials = requireCredentials();
970
+ const ticketRef = resolveTicketRef(flags);
971
+ const title = flags.get("title") ?? positional[0];
972
+ if (!title) fail("A page title is required.", `Pass --title "Auth flow notes", or give it as the first argument.`);
973
+ const text = (flags.get("text") || await readStdin()).trim();
974
+ if (!text) fail("No page text given.", "Pipe the page's body in on stdin, or pass --text.");
975
+ success(`${(await request(credentials, "/api/agent/pages/upsert", {
976
+ token: credentials.token,
977
+ ticketRef,
978
+ title,
979
+ text
980
+ })).created ? "Created" : "Updated"} page ${bold(title)} in ${ticketRef}'s project`);
981
+ }
982
+ async function pages(flags, positional) {
983
+ const [subcommand, ...rest] = positional;
984
+ switch (subcommand) {
985
+ case void 0:
986
+ case "list": return list(flags);
987
+ case "update": return update(flags, rest);
988
+ default: fail(`Unknown "pages ${subcommand}".`, "Try `pages` or `pages update`.");
989
+ }
990
+ }
991
+ //#endregion
910
992
  //#region src/commands/start.ts
911
993
  const TOOLS = /* @__PURE__ */ new Set([
912
994
  "claude-code",
@@ -1012,6 +1094,13 @@ ${bold("Usage")}
1012
1094
  npx neuvikon status Show login, project setup and current ticket
1013
1095
  npx neuvikon logout
1014
1096
 
1097
+ ${bold("Project pages")}
1098
+ npx neuvikon pages List the current ticket's project pages
1099
+ npx neuvikon pages update "Title" < notes.txt
1100
+ Create or update a page by title
1101
+ Reference another page with [[Its Title]], same as in the editor.
1102
+ Both accept --ticket EWWQ-1 instead of relying on a tracked session.
1103
+
1015
1104
  ${bold("Options")}
1016
1105
  --tool <name> claude-code | codex | cursor | other
1017
1106
  --transcript <path> Read token usage from an agent transcript
@@ -1059,6 +1148,7 @@ async function main() {
1059
1148
  case "report": return report(flags);
1060
1149
  case "stop": return stop(flags);
1061
1150
  case "status": return status();
1151
+ case "pages": return pages(flags, positional);
1062
1152
  case void 0:
1063
1153
  case "help":
1064
1154
  case "--help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuvikon",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Tell Neuvikon which ticket your coding agent is working on.",
5
5
  "keywords": [
6
6
  "neuvikon",