@devness/useai 0.5.36 → 0.5.37

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.js +200 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -656,7 +656,7 @@ var VERSION;
656
656
  var init_version = __esm({
657
657
  "../shared/dist/constants/version.js"() {
658
658
  "use strict";
659
- VERSION = "0.5.36";
659
+ VERSION = "0.5.37";
660
660
  }
661
661
  });
662
662
 
@@ -6034,7 +6034,8 @@ var init_dist = __esm({
6034
6034
 
6035
6035
  // src/tools.ts
6036
6036
  import { createToolRegistry } from "@devness/mcp-setup/dist/registry.js";
6037
- import { readJsonFile, writeJsonFile, injectInstructions } from "@devness/mcp-setup/dist/formats.js";
6037
+ import { existsSync as existsSync6 } from "fs";
6038
+ import { hasBinary, readJsonFile, writeJsonFile, injectInstructions, removeInstructions } from "@devness/mcp-setup/dist/formats.js";
6038
6039
  import { join as join4 } from "path";
6039
6040
  import { homedir as homedir4 } from "os";
6040
6041
  function installStandardHttp(configPath) {
@@ -6053,12 +6054,83 @@ function installVscodeHttp(configPath) {
6053
6054
  config2["servers"] = servers;
6054
6055
  writeJsonFile(configPath, config2);
6055
6056
  }
6057
+ function installCrushHttp(configPath) {
6058
+ const config2 = readJsonFile(configPath);
6059
+ const servers = config2["mcp"] ?? {};
6060
+ delete servers["useai"];
6061
+ servers["UseAI"] = { type: "http", url: MCP_HTTP_URL };
6062
+ config2["mcp"] = servers;
6063
+ writeJsonFile(configPath, config2);
6064
+ }
6065
+ function createExtraTool(def) {
6066
+ const supportsUrl = true;
6067
+ const jsonKey = def.configFormat === "crush" ? "mcp" : "mcpServers";
6068
+ return {
6069
+ id: def.id,
6070
+ name: def.name,
6071
+ configFormat: def.configFormat,
6072
+ supportsUrl,
6073
+ getConfigPath: () => def.configPath,
6074
+ detect: def.detect,
6075
+ isConfigured() {
6076
+ const config2 = readJsonFile(def.configPath);
6077
+ const servers = config2[jsonKey];
6078
+ return !!servers?.["UseAI"] || !!servers?.["useai"];
6079
+ },
6080
+ install() {
6081
+ const config2 = readJsonFile(def.configPath);
6082
+ const servers = config2[jsonKey] ?? {};
6083
+ delete servers["useai"];
6084
+ servers["UseAI"] = def.configFormat === "crush" ? { type: "stdio", ...MCP_STDIO_ENTRY } : { ...MCP_STDIO_ENTRY };
6085
+ config2[jsonKey] = servers;
6086
+ writeJsonFile(def.configPath, config2);
6087
+ if (def.instructions) injectInstructions(INSTRUCTIONS, def.instructions);
6088
+ },
6089
+ installHttp() {
6090
+ if (def.configFormat === "crush") {
6091
+ installCrushHttp(def.configPath);
6092
+ } else {
6093
+ installStandardHttp(def.configPath);
6094
+ }
6095
+ if (def.instructions) injectInstructions(INSTRUCTIONS, def.instructions);
6096
+ },
6097
+ remove() {
6098
+ const config2 = readJsonFile(def.configPath);
6099
+ const servers = config2[jsonKey];
6100
+ if (servers) {
6101
+ delete servers["UseAI"];
6102
+ delete servers["useai"];
6103
+ if (Object.keys(servers).length === 0) delete config2[jsonKey];
6104
+ writeJsonFile(def.configPath, config2);
6105
+ }
6106
+ if (def.instructions) removeInstructions(INSTRUCTIONS, def.instructions);
6107
+ },
6108
+ getManualHint: () => def.instructions ? null : def.manualHint ?? null
6109
+ };
6110
+ }
6111
+ function matchesTool(tool, query) {
6112
+ const q = query.toLowerCase().replace(/[\s-_]+/g, "");
6113
+ const id = tool.id.toLowerCase().replace(/[\s-_]+/g, "");
6114
+ const name = tool.name.toLowerCase().replace(/[\s-_]+/g, "");
6115
+ return id === q || name === q || id.includes(q) || name.includes(q);
6116
+ }
6056
6117
  function resolveTools(names) {
6057
- const { matched: baseMatched, unmatched } = registry.resolveTools(names);
6118
+ const { matched: baseMatched, unmatched: registryUnmatched } = registry.resolveTools(names);
6058
6119
  const matched = baseMatched.map((bt) => AI_TOOLS.find((t) => t.id === bt.id));
6059
- return { matched, unmatched };
6120
+ const stillUnmatched = [];
6121
+ for (const name of registryUnmatched) {
6122
+ const found = extraTools.filter((t) => matchesTool(t, name));
6123
+ if (found.length > 0) {
6124
+ for (const f of found) {
6125
+ if (!matched.includes(f)) matched.push(f);
6126
+ }
6127
+ } else {
6128
+ stillUnmatched.push(name);
6129
+ }
6130
+ }
6131
+ return { matched, unmatched: stillUnmatched };
6060
6132
  }
6061
- var USEAI_INSTRUCTIONS_TEXT, MCP_HTTP_URL, MCP_HTTP_ENTRY, INSTRUCTIONS, registry, home, appSupport, toolInstructions, URL_SUPPORTED_TOOLS, AI_TOOLS;
6133
+ var USEAI_INSTRUCTIONS_TEXT, MCP_HTTP_URL, MCP_HTTP_ENTRY, INSTRUCTIONS, registry, home, appSupport, toolInstructions, URL_SUPPORTED_TOOLS, registryTools, MCP_STDIO_ENTRY, extraTools, AI_TOOLS;
6062
6134
  var init_tools = __esm({
6063
6135
  "src/tools.ts"() {
6064
6136
  "use strict";
@@ -6115,7 +6187,7 @@ var init_tools = __esm({
6115
6187
  "opencode",
6116
6188
  "crush"
6117
6189
  ]);
6118
- AI_TOOLS = registry.tools.map((baseTool) => {
6190
+ registryTools = registry.tools.map((baseTool) => {
6119
6191
  const supportsUrl = URL_SUPPORTED_TOOLS.has(baseTool.id);
6120
6192
  return {
6121
6193
  ...baseTool,
@@ -6136,6 +6208,60 @@ var init_tools = __esm({
6136
6208
  }
6137
6209
  };
6138
6210
  });
6211
+ MCP_STDIO_ENTRY = { command: "npx", args: ["-y", "@devness/useai@latest"] };
6212
+ extraTools = [
6213
+ createExtraTool({
6214
+ id: "antigravity",
6215
+ name: "Antigravity",
6216
+ configFormat: "standard",
6217
+ configPath: join4(home, ".gemini", "antigravity", "mcp_config.json"),
6218
+ detect: () => existsSync6(join4(home, ".gemini", "antigravity")),
6219
+ instructions: { method: "append", path: join4(home, ".gemini", "GEMINI.md") }
6220
+ }),
6221
+ createExtraTool({
6222
+ id: "copilot-cli",
6223
+ name: "Copilot CLI",
6224
+ configFormat: "standard",
6225
+ configPath: join4(home, ".copilot", "mcp-config.json"),
6226
+ detect: () => hasBinary("copilot") || existsSync6(join4(home, ".copilot")),
6227
+ manualHint: "No global instructions file \u2014 add UseAI instructions to your project-level agent rules."
6228
+ }),
6229
+ createExtraTool({
6230
+ id: "trae",
6231
+ name: "Trae",
6232
+ configFormat: "standard",
6233
+ configPath: join4(appSupport, "Trae", "User", "mcp.json"),
6234
+ detect: () => existsSync6(join4(appSupport, "Trae")),
6235
+ manualHint: "Open Trae Settings \u2192 Rules and paste the instructions below."
6236
+ }),
6237
+ createExtraTool({
6238
+ id: "kilo-code",
6239
+ name: "Kilo Code",
6240
+ configFormat: "standard",
6241
+ configPath: join4(
6242
+ appSupport,
6243
+ "Code",
6244
+ "User",
6245
+ "globalStorage",
6246
+ "kilocode.kilo-code",
6247
+ "settings",
6248
+ "mcp_settings.json"
6249
+ ),
6250
+ detect: () => existsSync6(
6251
+ join4(appSupport, "Code", "User", "globalStorage", "kilocode.kilo-code")
6252
+ ),
6253
+ manualHint: "Add the instructions below to .kilocode/rules/useai.md in your project root."
6254
+ }),
6255
+ createExtraTool({
6256
+ id: "crush",
6257
+ name: "Crush",
6258
+ configFormat: "crush",
6259
+ configPath: join4(home, ".config", "crush", "crush.json"),
6260
+ detect: () => hasBinary("crush") || existsSync6(join4(home, ".config", "crush")),
6261
+ manualHint: "No global instructions file \u2014 add UseAI instructions to your project-level .crush.json."
6262
+ })
6263
+ ];
6264
+ AI_TOOLS = [...registryTools, ...extraTools];
6139
6265
  }
6140
6266
  });
6141
6267
 
@@ -6481,7 +6607,8 @@ async function runSetup(args) {
6481
6607
  tools = matched;
6482
6608
  }
6483
6609
  if (isStatus) {
6484
- (await getShared()).showStatus(tools);
6610
+ const detected = tools.filter((t) => t.detect());
6611
+ (await getShared()).showStatus(detected);
6485
6612
  } else if (isRemove) {
6486
6613
  await fullRemoveFlow(tools, autoYes, explicit);
6487
6614
  } else if (isStdio) {
@@ -25417,7 +25544,7 @@ var session_state_exports = {};
25417
25544
  __export(session_state_exports, {
25418
25545
  SessionState: () => SessionState
25419
25546
  });
25420
- import { appendFileSync, existsSync as existsSync6 } from "fs";
25547
+ import { appendFileSync, existsSync as existsSync7 } from "fs";
25421
25548
  import { basename, join as join5 } from "path";
25422
25549
  var SessionState;
25423
25550
  var init_session_state = __esm({
@@ -25515,7 +25642,7 @@ var init_session_state = __esm({
25515
25642
  }
25516
25643
  initializeKeystore() {
25517
25644
  ensureDir();
25518
- if (existsSync6(KEYSTORE_FILE)) {
25645
+ if (existsSync7(KEYSTORE_FILE)) {
25519
25646
  const ks = readJson(KEYSTORE_FILE, null);
25520
25647
  if (ks) {
25521
25648
  try {
@@ -25574,7 +25701,7 @@ __export(register_tools_exports, {
25574
25701
  registerTools: () => registerTools
25575
25702
  });
25576
25703
  import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
25577
- import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
25704
+ import { existsSync as existsSync8, renameSync as renameSync2 } from "fs";
25578
25705
  import { join as join7 } from "path";
25579
25706
  function getConfig() {
25580
25707
  return readJson(CONFIG_FILE, {
@@ -25787,7 +25914,7 @@ function registerTools(server2, session2, opts) {
25787
25914
  const activePath = join7(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
25788
25915
  const sealedPath = join7(SEALED_DIR, `${session2.sessionId}.jsonl`);
25789
25916
  try {
25790
- if (existsSync7(activePath)) {
25917
+ if (existsSync8(activePath)) {
25791
25918
  renameSync2(activePath, sealedPath);
25792
25919
  }
25793
25920
  } catch {
@@ -25902,7 +26029,7 @@ var init_html = __esm({
25902
26029
  });
25903
26030
 
25904
26031
  // src/dashboard/local-api.ts
25905
- import { existsSync as existsSync8, unlinkSync as unlinkSync4 } from "fs";
26032
+ import { existsSync as existsSync9, unlinkSync as unlinkSync4 } from "fs";
25906
26033
  import { join as join8 } from "path";
25907
26034
  function json(res, status, data) {
25908
26035
  const body = JSON.stringify(data);
@@ -26136,7 +26263,7 @@ function handleDeleteSession(req, res, sessionId) {
26136
26263
  const milestonesRemoved = milestones.length - remaining.length;
26137
26264
  if (milestonesRemoved > 0) writeJson(MILESTONES_FILE, remaining);
26138
26265
  const chainPath = join8(SEALED_DIR, `${sessionId}.jsonl`);
26139
- if (existsSync8(chainPath)) unlinkSync4(chainPath);
26266
+ if (existsSync9(chainPath)) unlinkSync4(chainPath);
26140
26267
  json(res, 200, { deleted: true, session_id: sessionId, milestones_removed: milestonesRemoved });
26141
26268
  } catch (err) {
26142
26269
  json(res, 500, { error: err.message });
@@ -26159,7 +26286,7 @@ function handleDeleteConversation(req, res, conversationId) {
26159
26286
  if (milestonesRemoved > 0) writeJson(MILESTONES_FILE, remainingMilestones);
26160
26287
  for (const sid of sessionIds) {
26161
26288
  const chainPath = join8(SEALED_DIR, `${sid}.jsonl`);
26162
- if (existsSync8(chainPath)) unlinkSync4(chainPath);
26289
+ if (existsSync9(chainPath)) unlinkSync4(chainPath);
26163
26290
  }
26164
26291
  json(res, 200, { deleted: true, conversation_id: conversationId, sessions_removed: sessionIds.size, milestones_removed: milestonesRemoved });
26165
26292
  } catch (err) {
@@ -26197,7 +26324,7 @@ __export(daemon_exports, {
26197
26324
  });
26198
26325
  import { createServer } from "http";
26199
26326
  import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
26200
- import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync4, appendFileSync as appendFileSync2, renameSync as renameSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync5, statSync } from "fs";
26327
+ import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync4, appendFileSync as appendFileSync2, renameSync as renameSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync5, statSync } from "fs";
26201
26328
  import { join as join9 } from "path";
26202
26329
  function getActiveUseaiSessionIds() {
26203
26330
  const ids = /* @__PURE__ */ new Set();
@@ -26208,7 +26335,7 @@ function getActiveUseaiSessionIds() {
26208
26335
  }
26209
26336
  function sealOrphanFile(sessionId) {
26210
26337
  const filePath = join9(ACTIVE_DIR, `${sessionId}.jsonl`);
26211
- if (!existsSync9(filePath)) return;
26338
+ if (!existsSync10(filePath)) return;
26212
26339
  try {
26213
26340
  const content = readFileSync4(filePath, "utf-8").trim();
26214
26341
  if (!content) return;
@@ -26305,7 +26432,7 @@ function sealOrphanFile(sessionId) {
26305
26432
  }
26306
26433
  }
26307
26434
  function sealOrphanedSessions() {
26308
- if (!existsSync9(ACTIVE_DIR)) return;
26435
+ if (!existsSync10(ACTIVE_DIR)) return;
26309
26436
  const activeIds = getActiveUseaiSessionIds();
26310
26437
  const mcpMap = readMcpMap();
26311
26438
  const mappedUseaiIds = new Set(Object.values(mcpMap));
@@ -26334,7 +26461,7 @@ function sealOrphanedSessions() {
26334
26461
  }
26335
26462
  function isSessionAlreadySealed(session2) {
26336
26463
  const activePath = join9(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
26337
- return !existsSync9(activePath);
26464
+ return !existsSync10(activePath);
26338
26465
  }
26339
26466
  function sealRichness(s) {
26340
26467
  let score = 0;
@@ -26419,7 +26546,7 @@ function autoSealSession(active) {
26419
26546
  const activePath = join9(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
26420
26547
  const sealedPath = join9(SEALED_DIR, `${session2.sessionId}.jsonl`);
26421
26548
  try {
26422
- if (existsSync9(activePath)) {
26549
+ if (existsSync10(activePath)) {
26423
26550
  renameSync3(activePath, sealedPath);
26424
26551
  }
26425
26552
  } catch {
@@ -26550,7 +26677,7 @@ function sendJsonRpcResult(res, rpcId, text) {
26550
26677
  function readChainMetadata(useaiSessionId) {
26551
26678
  const activePath = join9(ACTIVE_DIR, `${useaiSessionId}.jsonl`);
26552
26679
  const sealedPath = join9(SEALED_DIR, `${useaiSessionId}.jsonl`);
26553
- const chainPath = existsSync9(activePath) ? activePath : existsSync9(sealedPath) ? sealedPath : null;
26680
+ const chainPath = existsSync10(activePath) ? activePath : existsSync10(sealedPath) ? sealedPath : null;
26554
26681
  if (!chainPath) return null;
26555
26682
  try {
26556
26683
  const firstLine = readFileSync4(chainPath, "utf-8").split("\n")[0];
@@ -26575,7 +26702,7 @@ function recoverStartSession(staleMcpSessionId, args, rpcId, res, req) {
26575
26702
  const map = readMcpMap();
26576
26703
  const prevSessionId = map[staleMcpSessionId];
26577
26704
  const prevActivePath = prevSessionId ? join9(ACTIVE_DIR, `${prevSessionId}.jsonl`) : null;
26578
- if (prevActivePath && existsSync9(prevActivePath)) {
26705
+ if (prevActivePath && existsSync10(prevActivePath)) {
26579
26706
  sealOrphanFile(prevSessionId);
26580
26707
  }
26581
26708
  const meta = prevSessionId ? readChainMetadata(prevSessionId) : null;
@@ -26616,7 +26743,7 @@ function recoverHeartbeat(staleMcpSessionId, rpcId, res) {
26616
26743
  const useaiSessionId = map[staleMcpSessionId];
26617
26744
  if (!useaiSessionId) return false;
26618
26745
  const chainPath = join9(ACTIVE_DIR, `${useaiSessionId}.jsonl`);
26619
- if (!existsSync9(chainPath)) {
26746
+ if (!existsSync10(chainPath)) {
26620
26747
  sendJsonRpcResult(res, rpcId, "Session already ended (recovered).");
26621
26748
  return true;
26622
26749
  }
@@ -26653,7 +26780,7 @@ function recoverEndSession(staleMcpSessionId, args, rpcId, res) {
26653
26780
  if (!useaiSessionId) return false;
26654
26781
  const activePath = join9(ACTIVE_DIR, `${useaiSessionId}.jsonl`);
26655
26782
  const sealedPath = join9(SEALED_DIR, `${useaiSessionId}.jsonl`);
26656
- const chainPath = existsSync9(activePath) ? activePath : existsSync9(sealedPath) ? sealedPath : null;
26783
+ const chainPath = existsSync10(activePath) ? activePath : existsSync10(sealedPath) ? sealedPath : null;
26657
26784
  if (!chainPath) return false;
26658
26785
  const isAlreadySealed = chainPath === sealedPath;
26659
26786
  const content = readFileSync4(chainPath, "utf-8").trim();
@@ -26958,7 +27085,7 @@ async function startDaemon(port) {
26958
27085
  const listenPort = port ?? DAEMON_PORT;
26959
27086
  ensureDir();
26960
27087
  try {
26961
- if (existsSync9(KEYSTORE_FILE)) {
27088
+ if (existsSync10(KEYSTORE_FILE)) {
26962
27089
  const ks = readJson(KEYSTORE_FILE, null);
26963
27090
  if (ks) daemonSigningKey = decryptKeystore(ks);
26964
27091
  }
@@ -27182,7 +27309,7 @@ async function startDaemon(port) {
27182
27309
  }
27183
27310
  }
27184
27311
  try {
27185
- if (existsSync9(DAEMON_PID_FILE)) {
27312
+ if (existsSync10(DAEMON_PID_FILE)) {
27186
27313
  unlinkSync5(DAEMON_PID_FILE);
27187
27314
  }
27188
27315
  } catch {
@@ -27343,6 +27470,49 @@ if (subcommand === "mcp" || subcommand?.startsWith("--") || !subcommand && proce
27343
27470
  await runSetup2(args);
27344
27471
  process.exit(0);
27345
27472
  }
27473
+ if (subcommand === "update") {
27474
+ const { fetchLatestVersion: fetchLatestVersion2, fetchDaemonHealth: fetchDaemonHealth2, killDaemon: killDaemon2, ensureDaemon: ensureDaemon2, VERSION: VERSION3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
27475
+ console.log(`Current version: ${VERSION3}`);
27476
+ console.log("Checking for updates...");
27477
+ const latest = await fetchLatestVersion2();
27478
+ if (!latest) {
27479
+ console.error("Failed to fetch latest version from npm.");
27480
+ process.exit(1);
27481
+ }
27482
+ console.log(`Latest version: ${latest}`);
27483
+ const healthBefore = await fetchDaemonHealth2();
27484
+ const runningVersion = healthBefore?.version;
27485
+ if (runningVersion) {
27486
+ console.log(`Running daemon: ${runningVersion}`);
27487
+ }
27488
+ if (runningVersion === latest && VERSION3 === latest) {
27489
+ console.log("Already up to date.");
27490
+ process.exit(0);
27491
+ }
27492
+ console.log("Stopping daemon...");
27493
+ await killDaemon2();
27494
+ console.log("Clearing npx cache...");
27495
+ const { execSync: execSync4 } = await import("child_process");
27496
+ try {
27497
+ execSync4("npm cache clean --force", { stdio: "ignore", timeout: 15e3 });
27498
+ } catch {
27499
+ }
27500
+ console.log("Restarting daemon...");
27501
+ const ok = await ensureDaemon2({ preferOnline: true });
27502
+ if (!ok) {
27503
+ console.error("Daemon failed to start after update.");
27504
+ process.exit(1);
27505
+ }
27506
+ const healthAfter = await fetchDaemonHealth2();
27507
+ const newVersion = healthAfter?.version;
27508
+ console.log(`Daemon now running: ${newVersion ?? "unknown"}`);
27509
+ if (newVersion === latest) {
27510
+ console.log("Update successful!");
27511
+ } else {
27512
+ console.log("Warning: daemon version does not match latest. You may need to retry.");
27513
+ }
27514
+ process.exit(0);
27515
+ }
27346
27516
  if (subcommand === "daemon") {
27347
27517
  const { startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon2(), daemon_exports));
27348
27518
  const portArg = process.argv.indexOf("--port");
@@ -27351,6 +27521,11 @@ if (subcommand === "daemon") {
27351
27521
  await new Promise(() => {
27352
27522
  });
27353
27523
  }
27524
+ if (subcommand) {
27525
+ console.error(`Unknown subcommand: "${subcommand}"`);
27526
+ console.error("Available commands: mcp, daemon, update");
27527
+ process.exit(1);
27528
+ }
27354
27529
  var { McpServer: McpServer2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
27355
27530
  var { StdioServerTransport: StdioServerTransport2 } = await Promise.resolve().then(() => (init_stdio2(), stdio_exports));
27356
27531
  var { VERSION: VERSION2, ensureDir: ensureDir2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devness/useai",
3
- "version": "0.5.36",
3
+ "version": "0.5.37",
4
4
  "description": "Track your AI-assisted development workflow. MCP server that records usage metrics across all your AI tools.",
5
5
  "keywords": [
6
6
  "mcp",