@devness/useai-cli 0.4.4 → 0.4.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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +201 -72
  3. package/package.json +11 -12
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 useai.dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var CONFIG_FILE = join(USEAI_DIR, "config.json");
20
20
  var SESSIONS_FILE = join(DATA_DIR, "sessions.json");
21
21
  var MILESTONES_FILE = join(DATA_DIR, "milestones.json");
22
22
  var DAEMON_PID_FILE = join(USEAI_DIR, "daemon.pid");
23
+ var USEAI_HOOKS_DIR = join(USEAI_DIR, "hooks");
23
24
  var DAEMON_PORT = 19200;
24
25
  var DAEMON_LOG_FILE = join(USEAI_DIR, "daemon.log");
25
26
  var DAEMON_MCP_URL = `http://127.0.0.1:${DAEMON_PORT}/mcp`;
@@ -29,7 +30,7 @@ var SYSTEMD_SERVICE_PATH = join(homedir(), ".config", "systemd", "user", "useai-
29
30
  var WINDOWS_STARTUP_SCRIPT_PATH = join(process.env["APPDATA"] ?? join(homedir(), "AppData", "Roaming"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup", "useai-daemon.vbs");
30
31
 
31
32
  // ../shared/dist/constants/version.js
32
- var VERSION = "0.4.4";
33
+ var VERSION = "0.4.5";
33
34
 
34
35
  // ../shared/dist/constants/defaults.js
35
36
  var DEFAULT_CONFIG = {
@@ -4875,11 +4876,126 @@ function isAutostartInstalled() {
4875
4876
  }
4876
4877
  }
4877
4878
 
4879
+ // ../shared/dist/hooks/claude-code.js
4880
+ import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, chmodSync } from "fs";
4881
+ import { join as join3 } from "path";
4882
+ import { homedir as homedir3 } from "os";
4883
+ var STOP_GUARD_PATH = join3(USEAI_HOOKS_DIR, "stop-guard.js");
4884
+ var CLAUDE_SETTINGS_PATH = join3(homedir3(), ".claude", "settings.json");
4885
+ var STOP_GUARD_SCRIPT = `#!/usr/bin/env node
4886
+ 'use strict';
4887
+ const { readFileSync } = require('node:fs');
4888
+ let input = '';
4889
+ process.stdin.setEncoding('utf-8');
4890
+ process.stdin.on('data', chunk => input += chunk);
4891
+ process.stdin.on('end', () => {
4892
+ try {
4893
+ const hook = JSON.parse(input);
4894
+ if (hook.stop_hook_active) process.exit(0);
4895
+ if (!hook.transcript_path) process.exit(0);
4896
+ let transcript;
4897
+ try { transcript = readFileSync(hook.transcript_path, 'utf-8'); } catch { process.exit(0); }
4898
+ if (!transcript.includes('useai_session_start')) process.exit(0);
4899
+ if (transcript.includes('useai_session_end')) process.exit(0);
4900
+ process.stdout.write(JSON.stringify({
4901
+ decision: 'block',
4902
+ reason: 'Before ending, call useai_session_end with milestones (generic descriptions of accomplishments), languages used, and files_touched_count.'
4903
+ }));
4904
+ } catch { process.exit(0); }
4905
+ });
4906
+ `;
4907
+ function readSettings() {
4908
+ if (!existsSync7(CLAUDE_SETTINGS_PATH))
4909
+ return {};
4910
+ try {
4911
+ return JSON.parse(readFileSync3(CLAUDE_SETTINGS_PATH, "utf-8"));
4912
+ } catch {
4913
+ return {};
4914
+ }
4915
+ }
4916
+ function writeSettings(settings) {
4917
+ mkdirSync3(join3(homedir3(), ".claude"), { recursive: true });
4918
+ writeFileSync3(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
4919
+ }
4920
+ function installClaudeCodeHooks() {
4921
+ mkdirSync3(USEAI_HOOKS_DIR, { recursive: true });
4922
+ writeFileSync3(STOP_GUARD_PATH, STOP_GUARD_SCRIPT);
4923
+ try {
4924
+ chmodSync(STOP_GUARD_PATH, "755");
4925
+ } catch {
4926
+ }
4927
+ const settings = readSettings();
4928
+ const hooks = settings["hooks"] ?? {};
4929
+ const stopCmd = `node "${STOP_GUARD_PATH}"`;
4930
+ const sealCmd = `curl -sf -X POST http://127.0.0.1:${DAEMON_PORT}/api/seal-active --max-time 3 2>/dev/null || true`;
4931
+ let changed = false;
4932
+ if (!hooks["Stop"])
4933
+ hooks["Stop"] = [];
4934
+ const stopArr = hooks["Stop"];
4935
+ const hasStop = stopArr.some((g) => {
4936
+ const inner = g["hooks"];
4937
+ return inner?.some((h) => h["command"]?.includes("stop-guard"));
4938
+ });
4939
+ if (!hasStop) {
4940
+ stopArr.push({ hooks: [{ type: "command", command: stopCmd, timeout: 10 }] });
4941
+ changed = true;
4942
+ }
4943
+ if (!hooks["SessionEnd"])
4944
+ hooks["SessionEnd"] = [];
4945
+ const endArr = hooks["SessionEnd"];
4946
+ const hasEnd = endArr.some((g) => {
4947
+ const inner = g["hooks"];
4948
+ return inner?.some((h) => h["command"]?.includes("seal-active"));
4949
+ });
4950
+ if (!hasEnd) {
4951
+ endArr.push({ hooks: [{ type: "command", command: sealCmd, timeout: 5 }] });
4952
+ changed = true;
4953
+ }
4954
+ settings["hooks"] = hooks;
4955
+ writeSettings(settings);
4956
+ return changed;
4957
+ }
4958
+ function removeClaudeCodeHooks() {
4959
+ if (existsSync7(CLAUDE_SETTINGS_PATH)) {
4960
+ try {
4961
+ const settings = readSettings();
4962
+ const hooks = settings["hooks"];
4963
+ if (hooks) {
4964
+ if (hooks["Stop"]) {
4965
+ hooks["Stop"] = hooks["Stop"].filter((g) => {
4966
+ const inner = g["hooks"];
4967
+ return !inner?.some((h) => h["command"]?.includes("stop-guard"));
4968
+ });
4969
+ if (hooks["Stop"].length === 0)
4970
+ delete hooks["Stop"];
4971
+ }
4972
+ if (hooks["SessionEnd"]) {
4973
+ hooks["SessionEnd"] = hooks["SessionEnd"].filter((g) => {
4974
+ const inner = g["hooks"];
4975
+ return !inner?.some((h) => h["command"]?.includes("seal-active"));
4976
+ });
4977
+ if (hooks["SessionEnd"].length === 0)
4978
+ delete hooks["SessionEnd"];
4979
+ }
4980
+ if (Object.keys(hooks).length === 0)
4981
+ delete settings["hooks"];
4982
+ }
4983
+ writeSettings(settings);
4984
+ } catch {
4985
+ }
4986
+ }
4987
+ try {
4988
+ if (existsSync7(STOP_GUARD_PATH))
4989
+ unlinkSync3(STOP_GUARD_PATH);
4990
+ } catch {
4991
+ }
4992
+ }
4993
+
4878
4994
  // src/services/tools.ts
4879
4995
  import { execSync as execSync3 } from "child_process";
4880
- import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3 } from "fs";
4881
- import { dirname as dirname2, join as join3 } from "path";
4882
- import { homedir as homedir3 } from "os";
4996
+ import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
4997
+ import { dirname as dirname2, join as join4 } from "path";
4998
+ import { homedir as homedir4 } from "os";
4883
4999
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
4884
5000
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4885
5001
  var USEAI_INSTRUCTIONS_TEXT = [
@@ -4895,7 +5011,7 @@ var MCP_ENTRY = {
4895
5011
  };
4896
5012
  var MCP_HTTP_URL = DAEMON_MCP_URL;
4897
5013
  var MCP_HTTP_ENTRY = { type: "http", url: MCP_HTTP_URL };
4898
- var home = homedir3();
5014
+ var home = homedir4();
4899
5015
  function installStandardHttp(configPath) {
4900
5016
  const config = readJsonFile(configPath);
4901
5017
  const servers = config["mcpServers"] ?? {};
@@ -4921,9 +5037,9 @@ function hasBinary(name) {
4921
5037
  }
4922
5038
  }
4923
5039
  function readJsonFile(path) {
4924
- if (!existsSync7(path)) return {};
5040
+ if (!existsSync8(path)) return {};
4925
5041
  try {
4926
- const raw = readFileSync3(path, "utf-8").trim();
5042
+ const raw = readFileSync4(path, "utf-8").trim();
4927
5043
  if (!raw) return {};
4928
5044
  return JSON.parse(raw);
4929
5045
  } catch {
@@ -4931,8 +5047,8 @@ function readJsonFile(path) {
4931
5047
  }
4932
5048
  }
4933
5049
  function writeJsonFile(path, data) {
4934
- mkdirSync3(dirname2(path), { recursive: true });
4935
- writeFileSync3(path, JSON.stringify(data, null, 2) + "\n");
5050
+ mkdirSync4(dirname2(path), { recursive: true });
5051
+ writeFileSync4(path, JSON.stringify(data, null, 2) + "\n");
4936
5052
  }
4937
5053
  function isConfiguredStandard(configPath) {
4938
5054
  const config = readJsonFile(configPath);
@@ -5013,9 +5129,9 @@ function removeZed(configPath) {
5013
5129
  }
5014
5130
  }
5015
5131
  function readTomlFile(path) {
5016
- if (!existsSync7(path)) return {};
5132
+ if (!existsSync8(path)) return {};
5017
5133
  try {
5018
- const raw = readFileSync3(path, "utf-8").trim();
5134
+ const raw = readFileSync4(path, "utf-8").trim();
5019
5135
  if (!raw) return {};
5020
5136
  return parseToml(raw);
5021
5137
  } catch {
@@ -5023,8 +5139,8 @@ function readTomlFile(path) {
5023
5139
  }
5024
5140
  }
5025
5141
  function writeTomlFile(path, data) {
5026
- mkdirSync3(dirname2(path), { recursive: true });
5027
- writeFileSync3(path, stringifyToml(data) + "\n");
5142
+ mkdirSync4(dirname2(path), { recursive: true });
5143
+ writeFileSync4(path, stringifyToml(data) + "\n");
5028
5144
  }
5029
5145
  function isConfiguredToml(configPath) {
5030
5146
  const config = readTomlFile(configPath);
@@ -5052,9 +5168,9 @@ function removeToml(configPath) {
5052
5168
  }
5053
5169
  }
5054
5170
  function readYamlFile(path) {
5055
- if (!existsSync7(path)) return {};
5171
+ if (!existsSync8(path)) return {};
5056
5172
  try {
5057
- const raw = readFileSync3(path, "utf-8").trim();
5173
+ const raw = readFileSync4(path, "utf-8").trim();
5058
5174
  if (!raw) return {};
5059
5175
  return parseYaml(raw) ?? {};
5060
5176
  } catch {
@@ -5062,8 +5178,8 @@ function readYamlFile(path) {
5062
5178
  }
5063
5179
  }
5064
5180
  function writeYamlFile(path, data) {
5065
- mkdirSync3(dirname2(path), { recursive: true });
5066
- writeFileSync3(path, stringifyYaml(data));
5181
+ mkdirSync4(dirname2(path), { recursive: true });
5182
+ writeFileSync4(path, stringifyYaml(data));
5067
5183
  }
5068
5184
  function isConfiguredYaml(configPath) {
5069
5185
  const config = readYamlFile(configPath);
@@ -5109,49 +5225,49 @@ var USEAI_INSTRUCTIONS_BLOCK = `${INSTRUCTIONS_START}
5109
5225
  ${USEAI_INSTRUCTIONS}
5110
5226
  ${INSTRUCTIONS_END}`;
5111
5227
  function hasInstructionsBlock(filePath) {
5112
- if (!existsSync7(filePath)) return false;
5228
+ if (!existsSync8(filePath)) return false;
5113
5229
  try {
5114
- return readFileSync3(filePath, "utf-8").includes(INSTRUCTIONS_START);
5230
+ return readFileSync4(filePath, "utf-8").includes(INSTRUCTIONS_START);
5115
5231
  } catch {
5116
5232
  return false;
5117
5233
  }
5118
5234
  }
5119
5235
  function injectInstructions(config) {
5120
- mkdirSync3(dirname2(config.path), { recursive: true });
5236
+ mkdirSync4(dirname2(config.path), { recursive: true });
5121
5237
  if (config.method === "create") {
5122
- writeFileSync3(config.path, USEAI_INSTRUCTIONS + "\n");
5238
+ writeFileSync4(config.path, USEAI_INSTRUCTIONS + "\n");
5123
5239
  return;
5124
5240
  }
5125
5241
  if (hasInstructionsBlock(config.path)) return;
5126
5242
  let existing = "";
5127
- if (existsSync7(config.path)) {
5128
- existing = readFileSync3(config.path, "utf-8");
5243
+ if (existsSync8(config.path)) {
5244
+ existing = readFileSync4(config.path, "utf-8");
5129
5245
  }
5130
5246
  const separator = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
5131
- writeFileSync3(config.path, existing + separator + USEAI_INSTRUCTIONS_BLOCK + "\n");
5247
+ writeFileSync4(config.path, existing + separator + USEAI_INSTRUCTIONS_BLOCK + "\n");
5132
5248
  }
5133
5249
  function removeInstructions(config) {
5134
5250
  if (config.method === "create") {
5135
- if (existsSync7(config.path)) {
5251
+ if (existsSync8(config.path)) {
5136
5252
  try {
5137
- unlinkSync3(config.path);
5253
+ unlinkSync4(config.path);
5138
5254
  } catch {
5139
5255
  }
5140
5256
  }
5141
5257
  return;
5142
5258
  }
5143
- if (!existsSync7(config.path)) return;
5259
+ if (!existsSync8(config.path)) return;
5144
5260
  try {
5145
- const content = readFileSync3(config.path, "utf-8");
5261
+ const content = readFileSync4(config.path, "utf-8");
5146
5262
  const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5147
5263
  const regex = new RegExp(
5148
5264
  `\\n?${escaped(INSTRUCTIONS_START)}[\\s\\S]*?${escaped(INSTRUCTIONS_END)}\\n?`
5149
5265
  );
5150
5266
  const cleaned = content.replace(regex, "").trim();
5151
5267
  if (cleaned) {
5152
- writeFileSync3(config.path, cleaned + "\n");
5268
+ writeFileSync4(config.path, cleaned + "\n");
5153
5269
  } else {
5154
- unlinkSync3(config.path);
5270
+ unlinkSync4(config.path);
5155
5271
  }
5156
5272
  } catch {
5157
5273
  }
@@ -5195,7 +5311,7 @@ function createTool(def) {
5195
5311
  getManualHint: () => def.instructions ? null : def.manualHint ?? null
5196
5312
  };
5197
5313
  }
5198
- var appSupport = join3(home, "Library", "Application Support");
5314
+ var appSupport = join4(home, "Library", "Application Support");
5199
5315
  function matchesTool(tool, query) {
5200
5316
  const q = query.toLowerCase().replace(/[\s-_]+/g, "");
5201
5317
  const id = tool.id.toLowerCase().replace(/[\s-_]+/g, "");
@@ -5222,17 +5338,17 @@ var AI_TOOLS = [
5222
5338
  id: "claude-code",
5223
5339
  name: "Claude Code",
5224
5340
  configFormat: "standard",
5225
- configPath: join3(home, ".claude.json"),
5226
- detect: () => hasBinary("claude") || existsSync7(join3(home, ".claude.json")),
5227
- instructions: { method: "append", path: join3(home, ".claude", "CLAUDE.md") },
5341
+ configPath: join4(home, ".claude.json"),
5342
+ detect: () => hasBinary("claude") || existsSync8(join4(home, ".claude.json")),
5343
+ instructions: { method: "append", path: join4(home, ".claude", "CLAUDE.md") },
5228
5344
  supportsUrl: true
5229
5345
  }),
5230
5346
  createTool({
5231
5347
  id: "cursor",
5232
5348
  name: "Cursor",
5233
5349
  configFormat: "standard",
5234
- configPath: join3(home, ".cursor", "mcp.json"),
5235
- detect: () => existsSync7(join3(home, ".cursor")),
5350
+ configPath: join4(home, ".cursor", "mcp.json"),
5351
+ detect: () => existsSync8(join4(home, ".cursor")),
5236
5352
  manualHint: "Open Cursor Settings \u2192 Rules \u2192 User Rules and paste the instructions below.",
5237
5353
  supportsUrl: true
5238
5354
  }),
@@ -5240,51 +5356,51 @@ var AI_TOOLS = [
5240
5356
  id: "windsurf",
5241
5357
  name: "Windsurf",
5242
5358
  configFormat: "standard",
5243
- configPath: join3(home, ".codeium", "windsurf", "mcp_config.json"),
5244
- detect: () => existsSync7(join3(home, ".codeium", "windsurf")),
5245
- instructions: { method: "append", path: join3(home, ".codeium", "windsurf", "memories", "global_rules.md") },
5359
+ configPath: join4(home, ".codeium", "windsurf", "mcp_config.json"),
5360
+ detect: () => existsSync8(join4(home, ".codeium", "windsurf")),
5361
+ instructions: { method: "append", path: join4(home, ".codeium", "windsurf", "memories", "global_rules.md") },
5246
5362
  supportsUrl: true
5247
5363
  }),
5248
5364
  createTool({
5249
5365
  id: "vscode",
5250
5366
  name: "VS Code",
5251
5367
  configFormat: "vscode",
5252
- configPath: join3(appSupport, "Code", "User", "mcp.json"),
5253
- detect: () => existsSync7(join3(appSupport, "Code")),
5254
- instructions: { method: "create", path: join3(appSupport, "Code", "User", "prompts", "useai.instructions.md") },
5368
+ configPath: join4(appSupport, "Code", "User", "mcp.json"),
5369
+ detect: () => existsSync8(join4(appSupport, "Code")),
5370
+ instructions: { method: "create", path: join4(appSupport, "Code", "User", "prompts", "useai.instructions.md") },
5255
5371
  supportsUrl: true
5256
5372
  }),
5257
5373
  createTool({
5258
5374
  id: "vscode-insiders",
5259
5375
  name: "VS Code Insiders",
5260
5376
  configFormat: "vscode",
5261
- configPath: join3(appSupport, "Code - Insiders", "User", "mcp.json"),
5262
- detect: () => existsSync7(join3(appSupport, "Code - Insiders")),
5263
- instructions: { method: "create", path: join3(appSupport, "Code - Insiders", "User", "prompts", "useai.instructions.md") },
5377
+ configPath: join4(appSupport, "Code - Insiders", "User", "mcp.json"),
5378
+ detect: () => existsSync8(join4(appSupport, "Code - Insiders")),
5379
+ instructions: { method: "create", path: join4(appSupport, "Code - Insiders", "User", "prompts", "useai.instructions.md") },
5264
5380
  supportsUrl: true
5265
5381
  }),
5266
5382
  createTool({
5267
5383
  id: "gemini-cli",
5268
5384
  name: "Gemini CLI",
5269
5385
  configFormat: "standard",
5270
- configPath: join3(home, ".gemini", "settings.json"),
5386
+ configPath: join4(home, ".gemini", "settings.json"),
5271
5387
  detect: () => hasBinary("gemini"),
5272
- instructions: { method: "append", path: join3(home, ".gemini", "GEMINI.md") },
5388
+ instructions: { method: "append", path: join4(home, ".gemini", "GEMINI.md") },
5273
5389
  supportsUrl: true
5274
5390
  }),
5275
5391
  createTool({
5276
5392
  id: "zed",
5277
5393
  name: "Zed",
5278
5394
  configFormat: "zed",
5279
- configPath: join3(appSupport, "Zed", "settings.json"),
5280
- detect: () => existsSync7(join3(appSupport, "Zed")),
5395
+ configPath: join4(appSupport, "Zed", "settings.json"),
5396
+ detect: () => existsSync8(join4(appSupport, "Zed")),
5281
5397
  manualHint: "Open Rules Library (\u2318\u2325L) \u2192 click + \u2192 paste the instructions below."
5282
5398
  }),
5283
5399
  createTool({
5284
5400
  id: "cline",
5285
5401
  name: "Cline",
5286
5402
  configFormat: "standard",
5287
- configPath: join3(
5403
+ configPath: join4(
5288
5404
  appSupport,
5289
5405
  "Code",
5290
5406
  "User",
@@ -5293,17 +5409,17 @@ var AI_TOOLS = [
5293
5409
  "settings",
5294
5410
  "cline_mcp_settings.json"
5295
5411
  ),
5296
- detect: () => existsSync7(
5297
- join3(appSupport, "Code", "User", "globalStorage", "saoudrizwan.claude-dev")
5412
+ detect: () => existsSync8(
5413
+ join4(appSupport, "Code", "User", "globalStorage", "saoudrizwan.claude-dev")
5298
5414
  ),
5299
- instructions: { method: "create", path: join3(home, "Documents", "Cline", "Rules", "useai.md") },
5415
+ instructions: { method: "create", path: join4(home, "Documents", "Cline", "Rules", "useai.md") },
5300
5416
  supportsUrl: true
5301
5417
  }),
5302
5418
  createTool({
5303
5419
  id: "roo-code",
5304
5420
  name: "Roo Code",
5305
5421
  configFormat: "standard",
5306
- configPath: join3(
5422
+ configPath: join4(
5307
5423
  appSupport,
5308
5424
  "Code",
5309
5425
  "User",
@@ -5312,59 +5428,59 @@ var AI_TOOLS = [
5312
5428
  "settings",
5313
5429
  "cline_mcp_settings.json"
5314
5430
  ),
5315
- detect: () => existsSync7(
5316
- join3(appSupport, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline")
5431
+ detect: () => existsSync8(
5432
+ join4(appSupport, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline")
5317
5433
  ),
5318
- instructions: { method: "create", path: join3(home, ".roo", "rules", "useai.md") },
5434
+ instructions: { method: "create", path: join4(home, ".roo", "rules", "useai.md") },
5319
5435
  supportsUrl: true
5320
5436
  }),
5321
5437
  createTool({
5322
5438
  id: "amazon-q-cli",
5323
5439
  name: "Amazon Q CLI",
5324
5440
  configFormat: "standard",
5325
- configPath: join3(home, ".aws", "amazonq", "mcp.json"),
5326
- detect: () => hasBinary("q") || existsSync7(join3(home, ".aws", "amazonq")),
5441
+ configPath: join4(home, ".aws", "amazonq", "mcp.json"),
5442
+ detect: () => hasBinary("q") || existsSync8(join4(home, ".aws", "amazonq")),
5327
5443
  manualHint: "Create .amazonq/rules/useai.md in your project root with the instructions below."
5328
5444
  }),
5329
5445
  createTool({
5330
5446
  id: "amazon-q-ide",
5331
5447
  name: "Amazon Q IDE",
5332
5448
  configFormat: "standard",
5333
- configPath: join3(home, ".aws", "amazonq", "default.json"),
5334
- detect: () => existsSync7(join3(home, ".amazonq")) || existsSync7(join3(home, ".aws", "amazonq")),
5449
+ configPath: join4(home, ".aws", "amazonq", "default.json"),
5450
+ detect: () => existsSync8(join4(home, ".amazonq")) || existsSync8(join4(home, ".aws", "amazonq")),
5335
5451
  manualHint: "Create .amazonq/rules/useai.md in your project root with the instructions below."
5336
5452
  }),
5337
5453
  createTool({
5338
5454
  id: "codex",
5339
5455
  name: "Codex",
5340
5456
  configFormat: "toml",
5341
- configPath: join3(home, ".codex", "config.toml"),
5342
- detect: () => hasBinary("codex") || existsSync7(join3(home, ".codex")) || existsSync7("/Applications/Codex.app"),
5343
- instructions: { method: "append", path: join3(home, ".codex", "AGENTS.md") }
5457
+ configPath: join4(home, ".codex", "config.toml"),
5458
+ detect: () => hasBinary("codex") || existsSync8(join4(home, ".codex")) || existsSync8("/Applications/Codex.app"),
5459
+ instructions: { method: "append", path: join4(home, ".codex", "AGENTS.md") }
5344
5460
  }),
5345
5461
  createTool({
5346
5462
  id: "goose",
5347
5463
  name: "Goose",
5348
5464
  configFormat: "yaml",
5349
- configPath: join3(home, ".config", "goose", "config.yaml"),
5350
- detect: () => existsSync7(join3(home, ".config", "goose")),
5351
- instructions: { method: "append", path: join3(home, ".config", "goose", ".goosehints") }
5465
+ configPath: join4(home, ".config", "goose", "config.yaml"),
5466
+ detect: () => existsSync8(join4(home, ".config", "goose")),
5467
+ instructions: { method: "append", path: join4(home, ".config", "goose", ".goosehints") }
5352
5468
  }),
5353
5469
  createTool({
5354
5470
  id: "opencode",
5355
5471
  name: "OpenCode",
5356
5472
  configFormat: "standard",
5357
- configPath: join3(home, ".config", "opencode", "opencode.json"),
5358
- detect: () => hasBinary("opencode") || existsSync7(join3(home, ".config", "opencode")),
5359
- instructions: { method: "append", path: join3(home, ".config", "opencode", "AGENTS.md") },
5473
+ configPath: join4(home, ".config", "opencode", "opencode.json"),
5474
+ detect: () => hasBinary("opencode") || existsSync8(join4(home, ".config", "opencode")),
5475
+ instructions: { method: "append", path: join4(home, ".config", "opencode", "AGENTS.md") },
5360
5476
  supportsUrl: true
5361
5477
  }),
5362
5478
  createTool({
5363
5479
  id: "junie",
5364
5480
  name: "Junie",
5365
5481
  configFormat: "standard",
5366
- configPath: join3(home, ".junie", "mcp", "mcp.json"),
5367
- detect: () => existsSync7(join3(home, ".junie")),
5482
+ configPath: join4(home, ".junie", "mcp", "mcp.json"),
5483
+ detect: () => existsSync8(join4(home, ".junie")),
5368
5484
  manualHint: "Add the instructions below to .junie/guidelines.md in your project root."
5369
5485
  })
5370
5486
  ];
@@ -5458,6 +5574,14 @@ async function daemonInstallFlow(tools, explicit) {
5458
5574
  console.log(error(`\u2717 ${tool.name.padEnd(18)} \u2014 ${e.message}`));
5459
5575
  }
5460
5576
  }
5577
+ try {
5578
+ const hooksInstalled = installClaudeCodeHooks();
5579
+ if (hooksInstalled) {
5580
+ console.log(success("\u2713 Claude Code hooks installed (Stop + SessionEnd)"));
5581
+ }
5582
+ } catch {
5583
+ console.log(chalk6.yellow(" \u26A0 Could not install Claude Code hooks"));
5584
+ }
5461
5585
  showManualHints(targetTools);
5462
5586
  const mode = useDaemon ? "daemon mode" : "stdio mode";
5463
5587
  console.log(`
@@ -5632,6 +5756,11 @@ async function fullRemoveFlow(tools, autoYes, explicit) {
5632
5756
  }
5633
5757
  }
5634
5758
  }
5759
+ try {
5760
+ removeClaudeCodeHooks();
5761
+ console.log(success("\u2713 Claude Code hooks removed"));
5762
+ } catch {
5763
+ }
5635
5764
  console.log();
5636
5765
  try {
5637
5766
  await killDaemon();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devness/useai-cli",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "CLI tool for useai.dev — stats, sync, publish your AI development workflow",
5
5
  "author": "nabeelkausari",
6
6
  "license": "MIT",
@@ -14,14 +14,6 @@
14
14
  "README.md",
15
15
  "LICENSE"
16
16
  ],
17
- "scripts": {
18
- "build": "tsc -p tsconfig.build.json",
19
- "dev": "tsc --watch",
20
- "bundle": "tsup src/index.ts --format esm --target node18 --no-splitting",
21
- "prepublishOnly": "pnpm run bundle",
22
- "typecheck": "tsc --noEmit",
23
- "clean": "rm -rf dist"
24
- },
25
17
  "dependencies": {
26
18
  "@inquirer/prompts": "^8.2.1",
27
19
  "chalk": "^5.4.1",
@@ -31,9 +23,9 @@
31
23
  },
32
24
  "devDependencies": {
33
25
  "@types/node": "^22.13.4",
34
- "@useai/shared": "workspace:*",
35
26
  "tsup": "^8.0.0",
36
- "typescript": "^5.7.3"
27
+ "typescript": "^5.7.3",
28
+ "@useai/shared": "0.3.0"
37
29
  },
38
30
  "repository": {
39
31
  "type": "git",
@@ -42,5 +34,12 @@
42
34
  "homepage": "https://useai.dev",
43
35
  "engines": {
44
36
  "node": ">=18"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.build.json",
40
+ "dev": "tsc --watch",
41
+ "bundle": "tsup src/index.ts --format esm --target node18 --no-splitting",
42
+ "typecheck": "tsc --noEmit",
43
+ "clean": "rm -rf dist"
45
44
  }
46
- }
45
+ }