@devness/useai 0.3.4 → 0.3.6

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 +102 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ var __export = (target, all) => {
11
11
 
12
12
  // src/tools.ts
13
13
  import { execSync } from "child_process";
14
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
14
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync } from "fs";
15
15
  import { dirname, join as join3 } from "path";
16
16
  import { homedir as homedir2 } from "os";
17
17
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
@@ -200,6 +200,54 @@ function removeYaml(configPath) {
200
200
  writeYamlFile(configPath, config);
201
201
  }
202
202
  }
203
+ function hasInstructionsBlock(filePath) {
204
+ if (!existsSync3(filePath)) return false;
205
+ try {
206
+ return readFileSync2(filePath, "utf-8").includes(INSTRUCTIONS_START);
207
+ } catch {
208
+ return false;
209
+ }
210
+ }
211
+ function injectInstructions(config) {
212
+ mkdirSync2(dirname(config.path), { recursive: true });
213
+ if (config.method === "create") {
214
+ writeFileSync2(config.path, USEAI_INSTRUCTIONS + "\n");
215
+ return;
216
+ }
217
+ if (hasInstructionsBlock(config.path)) return;
218
+ let existing = "";
219
+ if (existsSync3(config.path)) {
220
+ existing = readFileSync2(config.path, "utf-8");
221
+ }
222
+ const separator = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
223
+ writeFileSync2(config.path, existing + separator + USEAI_INSTRUCTIONS_BLOCK + "\n");
224
+ }
225
+ function removeInstructions(config) {
226
+ if (config.method === "create") {
227
+ if (existsSync3(config.path)) {
228
+ try {
229
+ unlinkSync(config.path);
230
+ } catch {
231
+ }
232
+ }
233
+ return;
234
+ }
235
+ if (!existsSync3(config.path)) return;
236
+ try {
237
+ const content = readFileSync2(config.path, "utf-8");
238
+ const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
239
+ const regex = new RegExp(
240
+ `\\n?${escaped(INSTRUCTIONS_START)}[\\s\\S]*?${escaped(INSTRUCTIONS_END)}\\n?`
241
+ );
242
+ const cleaned = content.replace(regex, "").trim();
243
+ if (cleaned) {
244
+ writeFileSync2(config.path, cleaned + "\n");
245
+ } else {
246
+ unlinkSync(config.path);
247
+ }
248
+ } catch {
249
+ }
250
+ }
203
251
  function createTool(def) {
204
252
  const handler = formatHandlers[def.configFormat];
205
253
  return {
@@ -209,8 +257,14 @@ function createTool(def) {
209
257
  getConfigPath: () => def.configPath,
210
258
  detect: def.detect,
211
259
  isConfigured: () => handler.isConfigured(def.configPath),
212
- install: () => handler.install(def.configPath),
213
- remove: () => handler.remove(def.configPath)
260
+ install: () => {
261
+ handler.install(def.configPath);
262
+ if (def.instructions) injectInstructions(def.instructions);
263
+ },
264
+ remove: () => {
265
+ handler.remove(def.configPath);
266
+ if (def.instructions) removeInstructions(def.instructions);
267
+ }
214
268
  };
215
269
  }
216
270
  function matchesTool(tool, query) {
@@ -234,7 +288,7 @@ function resolveTools(names) {
234
288
  }
235
289
  return { matched, unmatched };
236
290
  }
237
- var MCP_ENTRY, home, formatHandlers, appSupport, AI_TOOLS;
291
+ var MCP_ENTRY, home, INSTRUCTIONS_START, INSTRUCTIONS_END, USEAI_INSTRUCTIONS, USEAI_INSTRUCTIONS_BLOCK, formatHandlers, appSupport, AI_TOOLS;
238
292
  var init_tools = __esm({
239
293
  "src/tools.ts"() {
240
294
  "use strict";
@@ -243,6 +297,17 @@ var init_tools = __esm({
243
297
  args: ["-y", "@devness/useai"]
244
298
  };
245
299
  home = homedir2();
300
+ INSTRUCTIONS_START = "<!-- useai:start -->";
301
+ INSTRUCTIONS_END = "<!-- useai:end -->";
302
+ USEAI_INSTRUCTIONS = [
303
+ "## UseAI Session Tracking",
304
+ "- At the START of every conversation, call `useai_session_start` with the appropriate task_type.",
305
+ "- For LONG sessions (15+ min), call `useai_heartbeat` periodically.",
306
+ "- At the END of every conversation, call `useai_session_end` with languages used, files_touched_count, and milestones (generic descriptions only \u2014 no project names, file paths, or company names)."
307
+ ].join("\n");
308
+ USEAI_INSTRUCTIONS_BLOCK = `${INSTRUCTIONS_START}
309
+ ${USEAI_INSTRUCTIONS}
310
+ ${INSTRUCTIONS_END}`;
246
311
  formatHandlers = {
247
312
  standard: { isConfigured: isConfiguredStandard, install: installStandard, remove: removeStandard },
248
313
  vscode: { isConfigured: isConfiguredVscode, install: installVscode, remove: removeVscode },
@@ -257,14 +322,8 @@ var init_tools = __esm({
257
322
  name: "Claude Code",
258
323
  configFormat: "standard",
259
324
  configPath: join3(home, ".claude.json"),
260
- detect: () => hasBinary("claude") || existsSync3(join3(home, ".claude.json"))
261
- }),
262
- createTool({
263
- id: "claude-desktop",
264
- name: "Claude Desktop",
265
- configFormat: "standard",
266
- configPath: join3(appSupport, "Claude", "claude_desktop_config.json"),
267
- detect: () => existsSync3(join3(appSupport, "Claude")) || existsSync3("/Applications/Claude.app")
325
+ detect: () => hasBinary("claude") || existsSync3(join3(home, ".claude.json")),
326
+ instructions: { method: "append", path: join3(home, ".claude", "CLAUDE.md") }
268
327
  }),
269
328
  createTool({
270
329
  id: "cursor",
@@ -278,28 +337,32 @@ var init_tools = __esm({
278
337
  name: "Windsurf",
279
338
  configFormat: "standard",
280
339
  configPath: join3(home, ".codeium", "windsurf", "mcp_config.json"),
281
- detect: () => existsSync3(join3(home, ".codeium", "windsurf"))
340
+ detect: () => existsSync3(join3(home, ".codeium", "windsurf")),
341
+ instructions: { method: "append", path: join3(home, ".codeium", "windsurf", "memories", "global_rules.md") }
282
342
  }),
283
343
  createTool({
284
344
  id: "vscode",
285
345
  name: "VS Code",
286
346
  configFormat: "vscode",
287
347
  configPath: join3(appSupport, "Code", "User", "mcp.json"),
288
- detect: () => existsSync3(join3(appSupport, "Code"))
348
+ detect: () => existsSync3(join3(appSupport, "Code")),
349
+ instructions: { method: "create", path: join3(appSupport, "Code", "User", "prompts", "useai.instructions.md") }
289
350
  }),
290
351
  createTool({
291
352
  id: "vscode-insiders",
292
353
  name: "VS Code Insiders",
293
354
  configFormat: "vscode",
294
355
  configPath: join3(appSupport, "Code - Insiders", "User", "mcp.json"),
295
- detect: () => existsSync3(join3(appSupport, "Code - Insiders"))
356
+ detect: () => existsSync3(join3(appSupport, "Code - Insiders")),
357
+ instructions: { method: "create", path: join3(appSupport, "Code - Insiders", "User", "prompts", "useai.instructions.md") }
296
358
  }),
297
359
  createTool({
298
360
  id: "gemini-cli",
299
361
  name: "Gemini CLI",
300
362
  configFormat: "standard",
301
363
  configPath: join3(home, ".gemini", "settings.json"),
302
- detect: () => hasBinary("gemini")
364
+ detect: () => hasBinary("gemini"),
365
+ instructions: { method: "append", path: join3(home, ".gemini", "GEMINI.md") }
303
366
  }),
304
367
  createTool({
305
368
  id: "zed",
@@ -323,7 +386,8 @@ var init_tools = __esm({
323
386
  ),
324
387
  detect: () => existsSync3(
325
388
  join3(appSupport, "Code", "User", "globalStorage", "saoudrizwan.claude-dev")
326
- )
389
+ ),
390
+ instructions: { method: "create", path: join3(home, "Documents", "Cline", "Rules", "useai.md") }
327
391
  }),
328
392
  createTool({
329
393
  id: "roo-code",
@@ -340,7 +404,8 @@ var init_tools = __esm({
340
404
  ),
341
405
  detect: () => existsSync3(
342
406
  join3(appSupport, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline")
343
- )
407
+ ),
408
+ instructions: { method: "create", path: join3(home, ".roo", "rules", "useai.md") }
344
409
  }),
345
410
  createTool({
346
411
  id: "amazon-q-cli",
@@ -361,21 +426,24 @@ var init_tools = __esm({
361
426
  name: "Codex",
362
427
  configFormat: "toml",
363
428
  configPath: join3(home, ".codex", "config.toml"),
364
- detect: () => hasBinary("codex") || existsSync3(join3(home, ".codex")) || existsSync3("/Applications/Codex.app")
429
+ detect: () => hasBinary("codex") || existsSync3(join3(home, ".codex")) || existsSync3("/Applications/Codex.app"),
430
+ instructions: { method: "append", path: join3(home, ".codex", "AGENTS.md") }
365
431
  }),
366
432
  createTool({
367
433
  id: "goose",
368
434
  name: "Goose",
369
435
  configFormat: "yaml",
370
436
  configPath: join3(home, ".config", "goose", "config.yaml"),
371
- detect: () => existsSync3(join3(home, ".config", "goose"))
437
+ detect: () => existsSync3(join3(home, ".config", "goose")),
438
+ instructions: { method: "append", path: join3(home, ".config", "goose", ".goosehints") }
372
439
  }),
373
440
  createTool({
374
441
  id: "opencode",
375
442
  name: "OpenCode",
376
443
  configFormat: "standard",
377
444
  configPath: join3(home, ".config", "opencode", "opencode.json"),
378
- detect: () => hasBinary("opencode") || existsSync3(join3(home, ".config", "opencode"))
445
+ detect: () => hasBinary("opencode") || existsSync3(join3(home, ".config", "opencode")),
446
+ instructions: { method: "append", path: join3(home, ".config", "opencode", "AGENTS.md") }
379
447
  }),
380
448
  createTool({
381
449
  id: "junie",
@@ -438,17 +506,16 @@ function showStatus(tools) {
438
506
  }
439
507
  async function installFlow(tools, autoYes, explicit) {
440
508
  if (explicit) {
441
- const toInstall2 = tools.filter((t) => !t.isConfigured());
442
- const alreadyDone = tools.filter((t) => t.isConfigured());
443
- for (const tool of alreadyDone) {
444
- console.log(dim(`${tool.name} is already configured.`));
445
- }
446
- if (toInstall2.length === 0) return;
447
509
  console.log();
448
- for (const tool of toInstall2) {
510
+ for (const tool of tools) {
449
511
  try {
512
+ const wasConfigured = tool.isConfigured();
450
513
  tool.install();
451
- console.log(ok(`\u2713 ${tool.name.padEnd(18)} \u2192 ${chalk.dim(shortenPath(tool.getConfigPath()))}`));
514
+ if (wasConfigured) {
515
+ console.log(ok(`\u2713 ${tool.name.padEnd(18)} ${chalk.dim("(updated)")}`));
516
+ } else {
517
+ console.log(ok(`\u2713 ${tool.name.padEnd(18)} \u2192 ${chalk.dim(shortenPath(tool.getConfigPath()))}`));
518
+ }
452
519
  } catch (e) {
453
520
  console.log(err(`\u2717 ${tool.name.padEnd(18)} \u2014 ${e.message}`));
454
521
  }
@@ -512,6 +579,12 @@ async function installFlow(tools, autoYes, explicit) {
512
579
  console.log(err(`\u2717 ${tool.name.padEnd(18)} \u2014 ${e.message}`));
513
580
  }
514
581
  }
582
+ for (const tool of alreadyConfigured) {
583
+ try {
584
+ tool.install();
585
+ } catch {
586
+ }
587
+ }
515
588
  console.log(`
516
589
  Done! UseAI MCP server configured in ${chalk.bold(String(toInstall.length))} tool${toInstall.length === 1 ? "" : "s"}.
517
590
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devness/useai",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
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",