@claudetools/tools 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.
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { parseArgs } from 'node:util';
7
7
  import { readFileSync } from 'node:fs';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { dirname, join } from 'node:path';
10
- import { runSetup, runUninstall, runInit } from './setup.js';
10
+ import { runSetup, runUninstall, runInit, runCleanup } from './setup.js';
11
11
  import { startServer } from './index.js';
12
12
  import { startWatcher, stopWatcher, watcherStatus } from './watcher.js';
13
13
  // Get version from package.json
@@ -49,6 +49,7 @@ Options:
49
49
 
50
50
  Commands:
51
51
  init Initialize current directory as a project
52
+ cleanup Remove legacy config and broken hooks
52
53
  watch Start the file watcher daemon
53
54
  watch --stop Stop the watcher daemon
54
55
  watch --status Check watcher status
@@ -79,6 +80,13 @@ else if (positionals[0] === 'init') {
79
80
  process.exit(1);
80
81
  });
81
82
  }
83
+ else if (positionals[0] === 'cleanup') {
84
+ // Handle cleanup command
85
+ runCleanup().catch((error) => {
86
+ console.error('Cleanup failed:', error);
87
+ process.exit(1);
88
+ });
89
+ }
82
90
  else if (positionals[0] === 'watch') {
83
91
  // Handle watch command
84
92
  const watchArgs = process.argv.slice(3); // Get args after 'watch'
@@ -14,7 +14,8 @@ export interface ProjectsCache {
14
14
  last_sync: string;
15
15
  }
16
16
  /**
17
- * Validates that a project ID is a proper UUID format
17
+ * Validates that a project ID is in a valid format
18
+ * Accepts both API-registered (proj_*) and local fallback (local_*) formats
18
19
  */
19
20
  export declare function isValidProjectId(id: string): boolean;
20
21
  /**
@@ -13,10 +13,19 @@ export const PROJECTS_FILE = getProjectsPath();
13
13
  // Cache for resolved project ID
14
14
  let resolvedProjectId = null;
15
15
  /**
16
- * Validates that a project ID is a proper UUID format
16
+ * Validates that a project ID is in a valid format
17
+ * Accepts both API-registered (proj_*) and local fallback (local_*) formats
17
18
  */
18
19
  export function isValidProjectId(id) {
19
- return /^proj_[a-f0-9]{20}$/.test(id);
20
+ // API-registered format: proj_ followed by 20 hex chars
21
+ if (/^proj_[a-f0-9]{20}$/.test(id)) {
22
+ return true;
23
+ }
24
+ // Local fallback format: local_ followed by sanitized project name
25
+ if (/^local_[a-z0-9_]+$/.test(id)) {
26
+ return true;
27
+ }
28
+ return false;
20
29
  }
21
30
  /**
22
31
  * Finds a project binding in the cache by directory path
@@ -78,7 +87,7 @@ export async function resolveProjectIdAsync() {
78
87
  // Validate UUID format
79
88
  if (!isValidProjectId(envProjectId)) {
80
89
  throw new Error(`Invalid project ID format in CLAUDETOOLS_PROJECT_ID: ${envProjectId}. ` +
81
- `Expected format: proj_xxxxxxxxxxxxxxxxxxxx (20 hex chars)`);
90
+ `Expected format: proj_xxxxxxxxxxxxxxxxxxxx or local_projectname`);
82
91
  }
83
92
  resolvedProjectId = envProjectId;
84
93
  mcpLogger.debug('MEMORY', `Using project ID from env: ${resolvedProjectId}`);
@@ -135,7 +144,7 @@ export function getDefaultProjectId() {
135
144
  if (config.defaultProjectId) {
136
145
  if (!isValidProjectId(config.defaultProjectId)) {
137
146
  throw new Error(`Invalid defaultProjectId in config: ${config.defaultProjectId}. ` +
138
- `Expected format: proj_xxxxxxxxxxxxxxxxxxxx (20 hex chars)`);
147
+ `Expected format: proj_xxxxxxxxxxxxxxxxxxxx or local_projectname`);
139
148
  }
140
149
  _defaultProjectId = config.defaultProjectId;
141
150
  return _defaultProjectId;
@@ -155,7 +164,7 @@ export async function getDefaultProjectIdAsync() {
155
164
  if (config.defaultProjectId) {
156
165
  if (!isValidProjectId(config.defaultProjectId)) {
157
166
  throw new Error(`Invalid defaultProjectId in config: ${config.defaultProjectId}. ` +
158
- `Expected format: proj_xxxxxxxxxxxxxxxxxxxx (20 hex chars)`);
167
+ `Expected format: proj_xxxxxxxxxxxxxxxxxxxx or local_projectname`);
159
168
  }
160
169
  _defaultProjectId = config.defaultProjectId;
161
170
  return _defaultProjectId;
package/dist/setup.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export declare function runSetup(): Promise<void>;
2
2
  export declare function runUninstall(): Promise<void>;
3
3
  export declare function runInit(): Promise<void>;
4
+ export declare function runCleanup(): Promise<void>;
package/dist/setup.js CHANGED
@@ -18,11 +18,19 @@ import { GLOBAL_TEMPLATE, SECTION_START, SECTION_END, getProjectTemplate, } from
18
18
  const CLAUDE_DIR = join(homedir(), '.claude');
19
19
  const CLAUDETOOLS_DIR = join(homedir(), '.claudetools');
20
20
  const MCP_CONFIG_PATH = join(CLAUDE_DIR, 'mcp.json');
21
+ const CLAUDE_DESKTOP_CONFIG_PATH = join(CLAUDE_DIR, 'claude_desktop_config.json');
21
22
  const SETTINGS_PATH = join(CLAUDE_DIR, 'settings.json');
22
23
  const HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
23
24
  const CLAUDE_MD_PATH = join(CLAUDE_DIR, 'CLAUDE.md');
24
25
  const SYSTEM_FILE = join(CLAUDETOOLS_DIR, 'system.json');
25
26
  const PROJECTS_FILE = join(CLAUDETOOLS_DIR, 'projects.json');
27
+ // Legacy env vars that should be removed from MCP configs
28
+ const LEGACY_ENV_VARS = [
29
+ 'CLAUDETOOLS_PROJECT_ID',
30
+ 'CLAUDETOOLS_USER_ID',
31
+ 'AUTO_INJECT_CONTEXT',
32
+ 'MEMORY_PROJECT_ID',
33
+ ];
26
34
  // -----------------------------------------------------------------------------
27
35
  // Utility Functions
28
36
  // -----------------------------------------------------------------------------
@@ -40,6 +48,61 @@ function error(msg) {
40
48
  function info(msg) {
41
49
  console.log(chalk.blue('ℹ ') + msg);
42
50
  }
51
+ /**
52
+ * Clean up legacy env vars from MCP server configs
53
+ * This removes deprecated vars like CLAUDETOOLS_PROJECT_ID that cause validation errors
54
+ */
55
+ function cleanupLegacyMcpConfig(configPath) {
56
+ if (!existsSync(configPath)) {
57
+ return false;
58
+ }
59
+ try {
60
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
61
+ let modified = false;
62
+ if (config.mcpServers) {
63
+ for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
64
+ if (serverConfig.env) {
65
+ for (const legacyVar of LEGACY_ENV_VARS) {
66
+ if (legacyVar in serverConfig.env) {
67
+ delete serverConfig.env[legacyVar];
68
+ modified = true;
69
+ info(`Removed legacy ${legacyVar} from ${serverName}`);
70
+ }
71
+ }
72
+ // Remove empty env object
73
+ if (Object.keys(serverConfig.env).length === 0) {
74
+ delete serverConfig.env;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ if (modified) {
80
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
81
+ return true;
82
+ }
83
+ }
84
+ catch (err) {
85
+ // Non-fatal - just skip cleanup
86
+ }
87
+ return false;
88
+ }
89
+ /**
90
+ * Clean up all legacy MCP configs (both mcp.json and claude_desktop_config.json)
91
+ */
92
+ function cleanupAllLegacyConfigs() {
93
+ let cleaned = false;
94
+ if (cleanupLegacyMcpConfig(MCP_CONFIG_PATH)) {
95
+ success('Cleaned up legacy config in mcp.json');
96
+ cleaned = true;
97
+ }
98
+ if (cleanupLegacyMcpConfig(CLAUDE_DESKTOP_CONFIG_PATH)) {
99
+ success('Cleaned up legacy config in claude_desktop_config.json');
100
+ cleaned = true;
101
+ }
102
+ if (!cleaned) {
103
+ info('No legacy config cleanup needed');
104
+ }
105
+ }
43
106
  // -----------------------------------------------------------------------------
44
107
  // System Registration
45
108
  // -----------------------------------------------------------------------------
@@ -999,15 +1062,18 @@ export async function runSetup() {
999
1062
  header('Saving Configuration');
1000
1063
  await saveConfig(extendedConfig);
1001
1064
  success(`Configuration saved to ${getConfigPath()}`);
1002
- // Step 6: Configure Claude Code MCP
1065
+ // Step 6: Clean up legacy configs
1066
+ header('Legacy Config Cleanup');
1067
+ cleanupAllLegacyConfigs();
1068
+ // Step 7: Configure Claude Code MCP
1003
1069
  await configureMcpSettings(services);
1004
- // Step 7: Install Hooks
1070
+ // Step 8: Install Hooks
1005
1071
  await installHooks();
1006
- // Step 8: Configure Settings
1072
+ // Step 9: Configure Settings
1007
1073
  await configureSettings();
1008
- // Step 9: Configure CLAUDE.md
1074
+ // Step 10: Configure CLAUDE.md
1009
1075
  await configureCLAUDEMD();
1010
- // Step 10: Verify
1076
+ // Step 11: Verify
1011
1077
  await verifySetup(extendedConfig);
1012
1078
  // Done
1013
1079
  header('Setup Complete');
@@ -1268,3 +1334,61 @@ export async function runInit() {
1268
1334
  console.log(' ' + chalk.bold('Config:') + ` ${projectClaudeMd}\n`);
1269
1335
  console.log(chalk.dim(' Memory tools are now configured for this project.\n'));
1270
1336
  }
1337
+ // -----------------------------------------------------------------------------
1338
+ // Cleanup Command (standalone)
1339
+ // -----------------------------------------------------------------------------
1340
+ export async function runCleanup() {
1341
+ console.log('\n' + chalk.bold.cyan(' ClaudeTools Legacy Cleanup') + '\n');
1342
+ header('Cleaning Legacy Configurations');
1343
+ cleanupAllLegacyConfigs();
1344
+ // Also clean up any project-level legacy hooks
1345
+ const cwd = process.cwd();
1346
+ const projectSettingsPath = join(cwd, '.claude', 'settings.json');
1347
+ if (existsSync(projectSettingsPath)) {
1348
+ try {
1349
+ const settings = JSON.parse(readFileSync(projectSettingsPath, 'utf-8'));
1350
+ let modified = false;
1351
+ if (settings.hooks) {
1352
+ for (const hookType of Object.keys(settings.hooks)) {
1353
+ const hooks = settings.hooks[hookType];
1354
+ if (Array.isArray(hooks)) {
1355
+ const filtered = hooks.filter((h) => {
1356
+ // Remove hooks pointing to non-existent files or legacy memory-capture hooks
1357
+ if (h.hooks) {
1358
+ return !h.hooks.some(hk => {
1359
+ if (!hk.command)
1360
+ return false;
1361
+ // Remove if it's a legacy memory hook
1362
+ if (hk.command.includes('memory-capture') ||
1363
+ hk.command.includes('memory-context-injection') ||
1364
+ hk.command.includes('memory-session-save')) {
1365
+ return true;
1366
+ }
1367
+ // Remove if the file doesn't exist
1368
+ if (hk.command.startsWith('/') && !existsSync(hk.command.split(' ')[0])) {
1369
+ return true;
1370
+ }
1371
+ return false;
1372
+ });
1373
+ }
1374
+ return true;
1375
+ });
1376
+ if (filtered.length !== hooks.length) {
1377
+ settings.hooks[hookType] = filtered;
1378
+ modified = true;
1379
+ }
1380
+ }
1381
+ }
1382
+ }
1383
+ if (modified) {
1384
+ writeFileSync(projectSettingsPath, JSON.stringify(settings, null, 2));
1385
+ success('Cleaned up project-level legacy hooks');
1386
+ }
1387
+ }
1388
+ catch {
1389
+ // Non-fatal
1390
+ }
1391
+ }
1392
+ console.log('\n' + chalk.green(' Cleanup complete!'));
1393
+ console.log(chalk.dim(' Restart Claude Code for changes to take effect.\n'));
1394
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudetools/tools",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Persistent AI memory, task management, and codebase intelligence for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",