@claudetools/tools 0.8.3 → 0.8.4

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 (60) hide show
  1. package/dist/cli.js +41 -0
  2. package/dist/context/deduplication.d.ts +72 -0
  3. package/dist/context/deduplication.js +77 -0
  4. package/dist/context/deduplication.test.d.ts +6 -0
  5. package/dist/context/deduplication.test.js +84 -0
  6. package/dist/context/emergency-eviction.d.ts +73 -0
  7. package/dist/context/emergency-eviction.example.d.ts +13 -0
  8. package/dist/context/emergency-eviction.example.js +94 -0
  9. package/dist/context/emergency-eviction.js +226 -0
  10. package/dist/context/eviction-engine.d.ts +76 -0
  11. package/dist/context/eviction-engine.example.d.ts +7 -0
  12. package/dist/context/eviction-engine.example.js +144 -0
  13. package/dist/context/eviction-engine.js +176 -0
  14. package/dist/context/example-usage.d.ts +1 -0
  15. package/dist/context/example-usage.js +128 -0
  16. package/dist/context/exchange-summariser.d.ts +80 -0
  17. package/dist/context/exchange-summariser.js +261 -0
  18. package/dist/context/health-monitor.d.ts +97 -0
  19. package/dist/context/health-monitor.example.d.ts +1 -0
  20. package/dist/context/health-monitor.example.js +164 -0
  21. package/dist/context/health-monitor.js +210 -0
  22. package/dist/context/importance-scorer.d.ts +94 -0
  23. package/dist/context/importance-scorer.example.d.ts +1 -0
  24. package/dist/context/importance-scorer.example.js +140 -0
  25. package/dist/context/importance-scorer.js +187 -0
  26. package/dist/context/index.d.ts +9 -0
  27. package/dist/context/index.js +16 -0
  28. package/dist/context/session-helper.d.ts +10 -0
  29. package/dist/context/session-helper.js +51 -0
  30. package/dist/context/session-store.d.ts +94 -0
  31. package/dist/context/session-store.js +286 -0
  32. package/dist/context/usage-estimator.d.ts +131 -0
  33. package/dist/context/usage-estimator.js +260 -0
  34. package/dist/context/usage-estimator.test.d.ts +1 -0
  35. package/dist/context/usage-estimator.test.js +208 -0
  36. package/dist/context-cli.d.ts +16 -0
  37. package/dist/context-cli.js +309 -0
  38. package/dist/handlers/codedna-handlers.d.ts +1 -1
  39. package/dist/handlers/tool-handlers.js +82 -10
  40. package/dist/helpers/api-client.d.ts +5 -1
  41. package/dist/helpers/api-client.js +3 -1
  42. package/dist/helpers/error-tracking.js +1 -1
  43. package/dist/helpers/usage-analytics.js +1 -1
  44. package/dist/hooks/index.d.ts +4 -0
  45. package/dist/hooks/index.js +6 -0
  46. package/dist/hooks/post-tool-use-hook-cli.d.ts +2 -0
  47. package/dist/hooks/post-tool-use-hook-cli.js +34 -0
  48. package/dist/hooks/post-tool-use.d.ts +67 -0
  49. package/dist/hooks/post-tool-use.js +234 -0
  50. package/dist/hooks/stop-hook-cli.d.ts +2 -0
  51. package/dist/hooks/stop-hook-cli.js +34 -0
  52. package/dist/hooks/stop.d.ts +64 -0
  53. package/dist/hooks/stop.js +192 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +1 -0
  56. package/dist/logger.d.ts +1 -1
  57. package/dist/logger.js +4 -0
  58. package/dist/setup.js +206 -2
  59. package/dist/tools.js +4 -0
  60. package/package.json +3 -1
package/dist/setup.js CHANGED
@@ -747,11 +747,22 @@ fi
747
747
  # Escape the query for JSON (handle quotes and newlines)
748
748
  ESCAPED_QUERY=$(echo "$USER_QUERY" | head -c 500 | jq -Rs '.')
749
749
 
750
+ # Get session state for query-aware budget allocation (if SESSION_ID available)
751
+ SESSION_STATE_JSON='{}'
752
+ if [ -n "$SESSION_ID" ]; then
753
+ # Try to get session state from SessionStore
754
+ TOOLS_DIR="$HOME/.claudetools/node_modules/@claudetools/tools"
755
+ if [ -f "$TOOLS_DIR/dist/context/session-helper.js" ]; then
756
+ SESSION_STATE_JSON=$(node "$TOOLS_DIR/dist/context/session-helper.js" "$SESSION_ID" 2>/dev/null || echo '{}')
757
+ fi
758
+ fi
759
+
750
760
  # Inject context with semantic search based on the user's prompt
761
+ # Include session_state if available for dynamic budget calculation
751
762
  RESULT=$(curl -s --max-time 2 -X POST "$API_URL/api/v1/context/inject" \\
752
763
  -H "Authorization: Bearer $API_KEY" \\
753
764
  -H "Content-Type: application/json" \\
754
- -d "{\\"query\\": $ESCAPED_QUERY, \\"project_id\\": \\"$PROJECT_ID\\", \\"cwd\\": \\"$CWD\\"}" \\
765
+ -d "{\\"query\\": $ESCAPED_QUERY, \\"project_id\\": \\"$PROJECT_ID\\", \\"cwd\\": \\"$CWD\\", \\"session_state\\": $SESSION_STATE_JSON}" \\
755
766
  2>/dev/null)
756
767
 
757
768
  # Output context as additionalContext JSON for Claude Code
@@ -1330,6 +1341,170 @@ export async function runUninstall() {
1330
1341
  console.log('\n' + chalk.green('ClaudeTools removed from Claude Code.'));
1331
1342
  console.log(chalk.dim('Your ~/.claudetools/ config and data are preserved.\n'));
1332
1343
  }
1344
+ async function runOnboarding(apiUrl, apiKey, projectId, projectName) {
1345
+ header('Project Onboarding');
1346
+ info('Answer a few questions to help Claude understand your project better.');
1347
+ console.log(chalk.dim('This creates memory facts that provide context in future sessions.\n'));
1348
+ const spinner = ora('Starting onboarding session...').start();
1349
+ try {
1350
+ // Start onboarding session
1351
+ const startResponse = await fetch(`${apiUrl}/api/v1/onboarding/start`, {
1352
+ method: 'POST',
1353
+ headers: {
1354
+ 'Content-Type': 'application/json',
1355
+ 'Authorization': `Bearer ${apiKey}`,
1356
+ },
1357
+ body: JSON.stringify({ project_id: projectId }),
1358
+ });
1359
+ if (!startResponse.ok) {
1360
+ spinner.fail('Could not start onboarding');
1361
+ const errorData = await startResponse.json().catch(() => ({}));
1362
+ error(errorData.error || `HTTP ${startResponse.status}`);
1363
+ return;
1364
+ }
1365
+ const startData = await startResponse.json();
1366
+ if (!startData.success || !startData.data) {
1367
+ spinner.fail('Failed to start onboarding');
1368
+ return;
1369
+ }
1370
+ spinner.succeed('Onboarding session started');
1371
+ const sessionId = startData.data.session_id;
1372
+ let questions = startData.data.questions;
1373
+ let state = startData.data.state;
1374
+ let questionCount = 0;
1375
+ const maxQuestions = 8; // Limit to avoid fatigue
1376
+ // Interactive question loop
1377
+ while (questions.length > 0 && questionCount < maxQuestions) {
1378
+ const question = questions[0];
1379
+ questionCount++;
1380
+ console.log('');
1381
+ console.log(chalk.cyan(`Question ${questionCount}`) + (question.category ? chalk.dim(` [${question.category}]`) : ''));
1382
+ console.log(chalk.bold(question.question));
1383
+ let answer;
1384
+ if (question.options && question.options.length > 0) {
1385
+ // Multiple choice question
1386
+ const { selected } = await prompts({
1387
+ type: 'select',
1388
+ name: 'selected',
1389
+ message: 'Select an option:',
1390
+ choices: [
1391
+ ...question.options.map(opt => ({ title: opt, value: opt })),
1392
+ { title: 'Skip this question', value: '__skip__' },
1393
+ ],
1394
+ });
1395
+ if (selected === '__skip__' || !selected) {
1396
+ questions.shift();
1397
+ continue;
1398
+ }
1399
+ answer = selected;
1400
+ }
1401
+ else {
1402
+ // Free text question
1403
+ const { response } = await prompts({
1404
+ type: 'text',
1405
+ name: 'response',
1406
+ message: '>',
1407
+ validate: (v) => v.length > 0 || 'Please enter a response (or type "skip" to skip)',
1408
+ });
1409
+ if (!response || response.toLowerCase() === 'skip') {
1410
+ questions.shift();
1411
+ continue;
1412
+ }
1413
+ answer = response;
1414
+ }
1415
+ // Submit answer
1416
+ const answerSpinner = ora('Processing...').start();
1417
+ try {
1418
+ const answerResponse = await fetch(`${apiUrl}/api/v1/onboarding/answer`, {
1419
+ method: 'POST',
1420
+ headers: {
1421
+ 'Content-Type': 'application/json',
1422
+ 'Authorization': `Bearer ${apiKey}`,
1423
+ },
1424
+ body: JSON.stringify({
1425
+ session_id: sessionId,
1426
+ question_id: question.id,
1427
+ answer,
1428
+ }),
1429
+ });
1430
+ if (answerResponse.ok) {
1431
+ const answerData = await answerResponse.json();
1432
+ if (answerData.success && answerData.data) {
1433
+ const factsCount = answerData.data.facts_extracted || 0;
1434
+ if (factsCount > 0) {
1435
+ answerSpinner.succeed(`Recorded (${factsCount} fact${factsCount > 1 ? 's' : ''} extracted)`);
1436
+ }
1437
+ else {
1438
+ answerSpinner.succeed('Recorded');
1439
+ }
1440
+ // Update questions and state
1441
+ if (answerData.data.next_questions) {
1442
+ questions = answerData.data.next_questions;
1443
+ }
1444
+ else {
1445
+ questions.shift();
1446
+ }
1447
+ if (answerData.data.state) {
1448
+ state = answerData.data.state;
1449
+ }
1450
+ }
1451
+ else {
1452
+ answerSpinner.warn('Answer recorded (no facts extracted)');
1453
+ questions.shift();
1454
+ }
1455
+ }
1456
+ else {
1457
+ answerSpinner.fail('Could not submit answer');
1458
+ questions.shift();
1459
+ }
1460
+ }
1461
+ catch {
1462
+ answerSpinner.fail('Network error');
1463
+ questions.shift();
1464
+ }
1465
+ // Check if we should stop
1466
+ if (state.phase === 'confirmation' || state.phase === 'done') {
1467
+ break;
1468
+ }
1469
+ }
1470
+ // Complete onboarding
1471
+ console.log('');
1472
+ const completeSpinner = ora('Completing onboarding...').start();
1473
+ try {
1474
+ const completeResponse = await fetch(`${apiUrl}/api/v1/onboarding/complete`, {
1475
+ method: 'POST',
1476
+ headers: {
1477
+ 'Content-Type': 'application/json',
1478
+ 'Authorization': `Bearer ${apiKey}`,
1479
+ },
1480
+ body: JSON.stringify({
1481
+ session_id: sessionId,
1482
+ project_id: projectId,
1483
+ }),
1484
+ });
1485
+ if (completeResponse.ok) {
1486
+ const completeData = await completeResponse.json();
1487
+ if (completeData.success && completeData.data) {
1488
+ const totalFacts = completeData.data.facts_extracted || 0;
1489
+ completeSpinner.succeed(`Onboarding complete! ${totalFacts} facts stored in memory.`);
1490
+ }
1491
+ else {
1492
+ completeSpinner.succeed('Onboarding complete!');
1493
+ }
1494
+ }
1495
+ else {
1496
+ completeSpinner.warn('Onboarding session saved (completion had issues)');
1497
+ }
1498
+ }
1499
+ catch {
1500
+ completeSpinner.warn('Could not finalize onboarding');
1501
+ }
1502
+ }
1503
+ catch (err) {
1504
+ spinner.fail('Onboarding failed');
1505
+ error(err instanceof Error ? err.message : String(err));
1506
+ }
1507
+ }
1333
1508
  // -----------------------------------------------------------------------------
1334
1509
  // Project Init
1335
1510
  // -----------------------------------------------------------------------------
@@ -1491,12 +1666,41 @@ export async function runInit() {
1491
1666
  const newContent = existingContent.trimEnd() + '\n' + projectContent;
1492
1667
  writeFileSync(projectClaudeMd, newContent);
1493
1668
  success('Created .claude/CLAUDE.md');
1669
+ // Ensure hooks are installed and configured
1670
+ // (User might have run init without setup, or hooks might have been deleted)
1671
+ header('Hook Configuration');
1672
+ try {
1673
+ await installHooks();
1674
+ await configureSettings();
1675
+ info('Hooks configured successfully');
1676
+ }
1677
+ catch (err) {
1678
+ // Non-fatal - just warn
1679
+ console.log(chalk.yellow('⚠ Warning: Could not configure hooks. Run "claudetools --setup" to fix.'));
1680
+ }
1494
1681
  // Summary
1495
1682
  console.log('\n' + chalk.green(' Project initialized!\n'));
1496
1683
  console.log(' ' + chalk.bold('Project:') + ` ${projectName}`);
1497
1684
  console.log(' ' + chalk.bold('ID:') + ` ${projectId}`);
1498
1685
  console.log(' ' + chalk.bold('Config:') + ` ${projectClaudeMd}\n`);
1499
- console.log(chalk.dim(' Memory tools are now configured for this project.\n'));
1686
+ // Offer onboarding
1687
+ if (config.apiKey) {
1688
+ const { runOnboard } = await prompts({
1689
+ type: 'confirm',
1690
+ name: 'runOnboard',
1691
+ message: 'Run project onboarding? (Helps Claude understand your project)',
1692
+ initial: true,
1693
+ });
1694
+ if (runOnboard) {
1695
+ await runOnboarding(config.apiUrl || DEFAULT_CONFIG.apiUrl, config.apiKey, projectId, projectName);
1696
+ }
1697
+ else {
1698
+ console.log(chalk.dim('\n You can run onboarding later with: claudetools onboard\n'));
1699
+ }
1700
+ }
1701
+ else {
1702
+ console.log(chalk.dim(' Memory tools are now configured for this project.\n'));
1703
+ }
1500
1704
  }
1501
1705
  // -----------------------------------------------------------------------------
1502
1706
  // Cleanup Command (standalone)
package/dist/tools.js CHANGED
@@ -146,6 +146,10 @@ EXAMPLES:
146
146
  type: 'string',
147
147
  description: 'Project ID (optional, uses default if not provided)',
148
148
  },
149
+ is_critical: {
150
+ type: 'boolean',
151
+ description: 'Mark as critical fact - always injected regardless of query relevance. Use sparingly for truly essential facts.',
152
+ },
149
153
  },
150
154
  required: ['entity1', 'relationship', 'entity2', 'context'],
151
155
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudetools/tools",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Persistent AI memory, task management, and codebase intelligence for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -65,10 +65,12 @@
65
65
  "typescript": "^5.3.0"
66
66
  },
67
67
  "devDependencies": {
68
+ "@types/better-sqlite3": "^7.6.13",
68
69
  "@types/node": "^20.10.0",
69
70
  "@types/nunjucks": "^3.2.6",
70
71
  "@types/prompts": "^2.4.9",
71
72
  "@vitest/ui": "^4.0.15",
73
+ "better-sqlite3": "^12.5.0",
72
74
  "tsx": "^4.7.0",
73
75
  "typescript": "^5.3.0",
74
76
  "vitest": "^4.0.15"