neuvikon 0.2.4 → 0.2.5
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 +5 -1
- package/dist/index.mjs +62 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ pbpaste | npx neuvikon start
|
|
|
49
49
|
## Token usage
|
|
50
50
|
|
|
51
51
|
`start`/`stop` are all an agent can report about itself — no agent knows its own
|
|
52
|
-
token count. On Claude Code the transcript does, so a hook
|
|
52
|
+
token count. On Claude Code the transcript does, so `login`/`init` wire a hook
|
|
53
|
+
into `.claude/settings.json` for you, whenever that project already has one:
|
|
53
54
|
|
|
54
55
|
```jsonc
|
|
55
56
|
// .claude/settings.json
|
|
@@ -65,6 +66,9 @@ token count. On Claude Code the transcript does, so a hook can report it:
|
|
|
65
66
|
turn adds that turn rather than re-counting the conversation. It is silent and
|
|
66
67
|
always exits 0 — a token count is never worth interrupting someone's work.
|
|
67
68
|
|
|
69
|
+
If `.claude/settings.json` doesn't exist yet, or another agent is the one
|
|
70
|
+
being used, add the block above by hand.
|
|
71
|
+
|
|
68
72
|
## Configuration
|
|
69
73
|
|
|
70
74
|
| | |
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,62 @@ import { chmodSync, closeSync, existsSync, fstatSync, mkdirSync, openSync, readF
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
//#region src/agents/hooks.ts
|
|
7
|
+
const REPORT_COMMAND = "npx neuvikon report";
|
|
8
|
+
const STOP_COMMAND = "npx neuvikon stop";
|
|
9
|
+
function hasCommand(groups, command) {
|
|
10
|
+
return (groups ?? []).some((group) => group.hooks.some((hook) => hook.command === command));
|
|
11
|
+
}
|
|
12
|
+
function withCommand(groups, command) {
|
|
13
|
+
if (hasCommand(groups, command)) return groups;
|
|
14
|
+
return [...groups ?? [], { hooks: [{
|
|
15
|
+
type: "command",
|
|
16
|
+
command
|
|
17
|
+
}] }];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wires token reporting into Claude Code's own hook system — the one agent
|
|
21
|
+
* whose transcript exposes usage, per `usage.ts`. `Stop` fires after every
|
|
22
|
+
* turn, exactly when `report` should read what the transcript gained since
|
|
23
|
+
* last time; `SessionEnd` is the backstop for `stop` when nobody remembered
|
|
24
|
+
* to run it by hand. Without this, `start`/`stop` are the only events an
|
|
25
|
+
* agent ever sends and token usage never leaves zero.
|
|
26
|
+
*/
|
|
27
|
+
function installClaudeHooks(root) {
|
|
28
|
+
const relativePath = join(".claude", "settings.json");
|
|
29
|
+
const path = join(root, relativePath);
|
|
30
|
+
const raw = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
31
|
+
let settings;
|
|
32
|
+
try {
|
|
33
|
+
settings = raw.trim() ? JSON.parse(raw) : {};
|
|
34
|
+
} catch {
|
|
35
|
+
return {
|
|
36
|
+
path: relativePath,
|
|
37
|
+
action: "skipped",
|
|
38
|
+
reason: "settings.json is not valid JSON"
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const hooks = settings.hooks ?? {};
|
|
42
|
+
if (hasCommand(hooks.Stop, REPORT_COMMAND) && hasCommand(hooks.SessionEnd, STOP_COMMAND)) return {
|
|
43
|
+
path: relativePath,
|
|
44
|
+
action: "unchanged"
|
|
45
|
+
};
|
|
46
|
+
const nextSettings = {
|
|
47
|
+
...settings,
|
|
48
|
+
hooks: {
|
|
49
|
+
...hooks,
|
|
50
|
+
Stop: withCommand(hooks.Stop, REPORT_COMMAND),
|
|
51
|
+
SessionEnd: withCommand(hooks.SessionEnd, STOP_COMMAND)
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
55
|
+
writeFileSync(path, `${JSON.stringify(nextSettings, null, 2)}\n`);
|
|
56
|
+
return {
|
|
57
|
+
path: relativePath,
|
|
58
|
+
action: raw ? "updated" : "created"
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
6
62
|
//#region src/agents/instructions.ts
|
|
7
63
|
/**
|
|
8
64
|
* The bridge between "user pasted a ticket" and "Neuvikon knows about it".
|
|
@@ -123,6 +179,7 @@ function installAgentInstructions(root) {
|
|
|
123
179
|
if (existsSync(path)) results.push(writeMarkdownTarget(root, target));
|
|
124
180
|
}
|
|
125
181
|
if (existsSync(join(root, ".cursor"))) results.push(writeCursorRule(root));
|
|
182
|
+
if (existsSync(join(root, ".claude"))) results.push(installClaudeHooks(root));
|
|
126
183
|
const gitignore = ignoreSessionFile(root);
|
|
127
184
|
if (gitignore) results.push(gitignore);
|
|
128
185
|
return results;
|
|
@@ -268,6 +325,7 @@ function fail(message, hint) {
|
|
|
268
325
|
function init() {
|
|
269
326
|
const results = installAgentInstructions(process.cwd());
|
|
270
327
|
for (const result of results) if (result.action === "unchanged") info(dim(` ${result.path} — already up to date`));
|
|
328
|
+
else if (result.action === "skipped") warn(`${result.path} — skipped (${result.reason})`);
|
|
271
329
|
else success(`${result.path} — ${result.action}`);
|
|
272
330
|
if (!readCredentials()) warn("Not logged in yet — run `npx neuvikon login`.");
|
|
273
331
|
}
|
|
@@ -380,9 +438,12 @@ async function login(flags) {
|
|
|
380
438
|
info("");
|
|
381
439
|
success(`Logged in as ${bold(result.userName)} · ${result.organizationName}`);
|
|
382
440
|
info(dim(` Token stored in ${CONFIG_PATH}`));
|
|
383
|
-
const
|
|
441
|
+
const installed = installAgentInstructions(process.cwd());
|
|
442
|
+
const touched = installed.filter((entry) => entry.action === "created" || entry.action === "updated");
|
|
443
|
+
const skipped = installed.filter((entry) => entry.action === "skipped");
|
|
384
444
|
if (touched.length > 0) success(`Told your coding agent how to report work (${touched.map((e) => e.path).join(", ")})`);
|
|
385
445
|
else success("Your coding agent already knows how to report work");
|
|
446
|
+
for (const entry of skipped) if (entry.action === "skipped") warn(`${entry.path} — skipped (${entry.reason})`);
|
|
386
447
|
info("");
|
|
387
448
|
info(dim(" Paste a ticket into any coding agent and it will do the rest."));
|
|
388
449
|
return;
|