@infinitedusky/indusk-mcp 1.11.8 → 1.12.0

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.
@@ -48,6 +48,12 @@ export async function evalSummary(projectRoot, opts) {
48
48
  console.info(` ${id.padEnd(20)} ${bar} ${(rate * 100).toFixed(0)}%`);
49
49
  }
50
50
  console.info(`\nGraphiti writes: ${summary.totalGraphitiWrites}`);
51
+ if (summary.totalCostUsd > 0) {
52
+ console.info(`\nCost:`);
53
+ console.info(` total: $${summary.totalCostUsd.toFixed(2)}`);
54
+ console.info(` per eval: $${(summary.totalCostUsd / scorecards.length).toFixed(2)}`);
55
+ console.info(` tokens: ${summary.totalInputTokens.toLocaleString()} in / ${summary.totalOutputTokens.toLocaleString()} out`);
56
+ }
51
57
  if (summary.evalCount >= 10) {
52
58
  console.info(`\nTrend (last 10 vs previous 10):`);
53
59
  for (const [id, delta] of Object.entries(summary.trend)) {
@@ -94,6 +100,9 @@ function computeSummary(scorecards) {
94
100
  baselineCount: baselineCards.length,
95
101
  passRates,
96
102
  totalGraphitiWrites: scorecards.reduce((sum, s) => sum + s.graphitiWrites, 0),
103
+ totalCostUsd: scorecards.reduce((sum, s) => sum + (s.usage?.costUsd ?? 0), 0),
104
+ totalInputTokens: scorecards.reduce((sum, s) => sum + (s.usage?.inputTokens ?? 0) + (s.usage?.cacheReadTokens ?? 0), 0),
105
+ totalOutputTokens: scorecards.reduce((sum, s) => sum + (s.usage?.outputTokens ?? 0), 0),
97
106
  trend,
98
107
  };
99
108
  }
@@ -91,12 +91,24 @@ export function runJudgeBackground(opts) {
91
91
  if (code !== 0) {
92
92
  throw new Error(`claude exited with code ${code}: ${stderr.slice(0, 500)}`);
93
93
  }
94
- // --output-format json wraps the result; extract the text content
94
+ // --output-format json wraps the result; extract the text content and usage
95
95
  let scorecardText = stdout;
96
+ let usage;
96
97
  try {
97
98
  const jsonOutput = JSON.parse(stdout);
98
- // claude --print --output-format json returns { result: string } or similar
99
99
  scorecardText = jsonOutput.result ?? jsonOutput.text ?? jsonOutput.content ?? stdout;
100
+ // Capture usage data from claude --print output
101
+ if (jsonOutput.total_cost_usd !== undefined || jsonOutput.usage) {
102
+ const u = jsonOutput.usage ?? {};
103
+ usage = {
104
+ costUsd: jsonOutput.total_cost_usd ?? 0,
105
+ inputTokens: u.input_tokens ?? 0,
106
+ outputTokens: u.output_tokens ?? 0,
107
+ cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
108
+ cacheReadTokens: u.cache_read_input_tokens ?? 0,
109
+ durationMs: jsonOutput.duration_ms ?? 0,
110
+ };
111
+ }
100
112
  }
101
113
  catch {
102
114
  // stdout might be raw JSON scorecard already
@@ -107,6 +119,8 @@ export function runJudgeBackground(opts) {
107
119
  scorecardText = jsonMatch[1];
108
120
  }
109
121
  const scorecard = JSON.parse(scorecardText.trim());
122
+ if (usage)
123
+ scorecard.usage = usage;
110
124
  scorecard.telemetryPosted = false;
111
125
  if (opts.evalEndpoint) {
112
126
  await postTelemetry(opts.evalEndpoint, scorecard);
@@ -184,9 +198,21 @@ export async function runJudgeSync(opts) {
184
198
  throw new Error(`claude exited with code ${code}: ${stderr.slice(0, 500)}`);
185
199
  }
186
200
  let scorecardText = stdout;
201
+ let syncUsage;
187
202
  try {
188
203
  const jsonOutput = JSON.parse(stdout);
189
204
  scorecardText = jsonOutput.result ?? jsonOutput.text ?? jsonOutput.content ?? stdout;
205
+ if (jsonOutput.total_cost_usd !== undefined || jsonOutput.usage) {
206
+ const u = jsonOutput.usage ?? {};
207
+ syncUsage = {
208
+ costUsd: jsonOutput.total_cost_usd ?? 0,
209
+ inputTokens: u.input_tokens ?? 0,
210
+ outputTokens: u.output_tokens ?? 0,
211
+ cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
212
+ cacheReadTokens: u.cache_read_input_tokens ?? 0,
213
+ durationMs: jsonOutput.duration_ms ?? 0,
214
+ };
215
+ }
190
216
  }
191
217
  catch {
192
218
  // raw JSON
@@ -196,6 +222,8 @@ export async function runJudgeSync(opts) {
196
222
  scorecardText = jsonMatch[1];
197
223
  }
198
224
  const scorecard = JSON.parse(scorecardText.trim());
225
+ if (syncUsage)
226
+ scorecard.usage = syncUsage;
199
227
  scorecard.telemetryPosted = false;
200
228
  if (opts.evalEndpoint) {
201
229
  await postTelemetry(opts.evalEndpoint, scorecard);
@@ -17,6 +17,14 @@ export interface EvalQuestion {
17
17
  evidence: string;
18
18
  finding: string;
19
19
  }
20
+ export interface EvalUsage {
21
+ costUsd: number;
22
+ inputTokens: number;
23
+ outputTokens: number;
24
+ cacheCreationTokens: number;
25
+ cacheReadTokens: number;
26
+ durationMs: number;
27
+ }
20
28
  export interface EvalScorecard {
21
29
  version: 1;
22
30
  timestamp: string;
@@ -27,6 +35,7 @@ export interface EvalScorecard {
27
35
  summary: string;
28
36
  graphitiWrites: number;
29
37
  telemetryPosted: boolean;
38
+ usage?: EvalUsage;
30
39
  }
31
40
  export interface EvalErrorEntry {
32
41
  version: 1;
@@ -1,3 +1,13 @@
1
+ process.on("uncaughtException", (err) => {
2
+ console.error(`[indusk] uncaught exception: ${err.message}`);
3
+ if (err.stack)
4
+ console.error(err.stack);
5
+ process.exit(1);
6
+ });
7
+ process.on("unhandledRejection", (reason) => {
8
+ console.error(`[indusk] unhandled rejection: ${reason}`);
9
+ process.exit(1);
10
+ });
1
11
  import { readFileSync } from "node:fs";
2
12
  import { dirname, join, resolve } from "node:path";
3
13
  import { fileURLToPath } from "node:url";
@@ -32,19 +42,32 @@ function checkForUpdates(currentVersion) {
32
42
  export async function startServer() {
33
43
  const projectRoot = resolve(process.env.PROJECT_ROOT ?? ".");
34
44
  const version = getLocalVersion();
45
+ console.error(`[indusk] v${version} starting (project: ${projectRoot})`);
35
46
  // Non-blocking version check
36
47
  checkForUpdates(version);
37
- const server = new McpServer({
38
- name: "indusk",
39
- version,
40
- });
41
- registerPlanTools(server, projectRoot);
42
- registerContextTools(server, projectRoot);
43
- registerQualityTools(server, projectRoot);
44
- registerDocumentTools(server, projectRoot);
45
- registerSystemTools(server, projectRoot);
46
- registerGraphTools(server, projectRoot);
47
- registerLessonTools(server, projectRoot);
48
- const transport = new StdioServerTransport();
49
- await server.connect(transport);
48
+ try {
49
+ const server = new McpServer({
50
+ name: "indusk",
51
+ version,
52
+ });
53
+ console.error("[indusk] registering tools...");
54
+ registerPlanTools(server, projectRoot);
55
+ registerContextTools(server, projectRoot);
56
+ registerQualityTools(server, projectRoot);
57
+ registerDocumentTools(server, projectRoot);
58
+ registerSystemTools(server, projectRoot);
59
+ registerGraphTools(server, projectRoot);
60
+ registerLessonTools(server, projectRoot);
61
+ console.error("[indusk] tools registered");
62
+ const transport = new StdioServerTransport();
63
+ await server.connect(transport);
64
+ console.error("[indusk] connected via stdio");
65
+ }
66
+ catch (err) {
67
+ console.error(`[indusk] FATAL: ${err instanceof Error ? err.message : String(err)}`);
68
+ if (err instanceof Error && err.stack) {
69
+ console.error(err.stack);
70
+ }
71
+ process.exit(1);
72
+ }
50
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitedusky/indusk-mcp",
3
- "version": "1.11.8",
3
+ "version": "1.12.0",
4
4
  "description": "InDusk development system — skills, MCP tools, and CLI for structured AI-assisted development",
5
5
  "type": "module",
6
6
  "files": [