@claudetools/tools 0.2.1 → 0.3.0

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 } from './setup.js';
10
+ import { runSetup, runUninstall, runInit } 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
@@ -47,6 +47,7 @@ Options:
47
47
  -h, --help Show this help
48
48
 
49
49
  Commands:
50
+ init Initialize current directory as a project
50
51
  watch Start the file watcher daemon
51
52
  watch --stop Stop the watcher daemon
52
53
  watch --status Check watcher status
@@ -70,6 +71,13 @@ else if (values.uninstall) {
70
71
  process.exit(1);
71
72
  });
72
73
  }
74
+ else if (positionals[0] === 'init') {
75
+ // Handle init command
76
+ runInit().catch((error) => {
77
+ console.error('Init failed:', error);
78
+ process.exit(1);
79
+ });
80
+ }
73
81
  else if (positionals[0] === 'watch') {
74
82
  // Handle watch command
75
83
  const watchArgs = process.argv.slice(3); // Get args after 'watch'
@@ -85,15 +85,21 @@ async function ensureSystemRegistered() {
85
85
  */
86
86
  function detectGitRemote(localPath) {
87
87
  try {
88
+ // Resolve symlinks and validate path to prevent path traversal attacks
89
+ const resolvedPath = fs.realpathSync(localPath);
90
+ // Ensure path is absolute and doesn't contain path traversal
91
+ if (!path.isAbsolute(resolvedPath) || resolvedPath.includes('..')) {
92
+ return undefined;
93
+ }
88
94
  const gitRemote = execSync('git config --get remote.origin.url', {
89
- cwd: localPath,
95
+ cwd: resolvedPath,
90
96
  encoding: 'utf-8',
91
97
  stdio: ['pipe', 'pipe', 'ignore'],
92
98
  }).trim();
93
99
  return gitRemote || undefined;
94
100
  }
95
101
  catch {
96
- // Not a git repo or no remote configured
102
+ // Not a git repo, no remote configured, or invalid path
97
103
  return undefined;
98
104
  }
99
105
  }
package/dist/setup.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export declare function runSetup(): Promise<void>;
2
2
  export declare function runUninstall(): Promise<void>;
3
+ export declare function runInit(): Promise<void>;
package/dist/setup.js CHANGED
@@ -10,6 +10,7 @@ import { join, basename } from 'path';
10
10
  import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'fs';
11
11
  import { randomUUID } from 'crypto';
12
12
  import { loadConfigFromFile, saveConfig, ensureConfigDir, getConfigPath, DEFAULT_CONFIG, } from './helpers/config-manager.js';
13
+ import { GLOBAL_TEMPLATE, SECTION_START, SECTION_END, getProjectTemplate, } from './templates/claude-md.js';
13
14
  // -----------------------------------------------------------------------------
14
15
  // Constants
15
16
  // -----------------------------------------------------------------------------
@@ -18,6 +19,7 @@ const CLAUDETOOLS_DIR = join(homedir(), '.claudetools');
18
19
  const MCP_CONFIG_PATH = join(CLAUDE_DIR, 'mcp.json');
19
20
  const SETTINGS_PATH = join(CLAUDE_DIR, 'settings.json');
20
21
  const HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
22
+ const CLAUDE_MD_PATH = join(CLAUDE_DIR, 'CLAUDE.md');
21
23
  const SYSTEM_FILE = join(CLAUDETOOLS_DIR, 'system.json');
22
24
  const PROJECTS_FILE = join(CLAUDETOOLS_DIR, 'projects.json');
23
25
  // -----------------------------------------------------------------------------
@@ -455,26 +457,111 @@ async function installHooks() {
455
457
  // Session start hook - ensures watcher is running
456
458
  const sessionStartHook = `#!/bin/bash
457
459
  # ClaudeTools Session Start Hook
458
- # Ensures the code watcher is running when Claude Code starts
460
+ # 1. Ensures the code watcher is running
461
+ # 2. Fetches and injects project context for the session
459
462
 
460
463
  # Skip if disabled
461
464
  if [ "$CLAUDE_DISABLE_HOOKS" = "1" ]; then exit 0; fi
462
465
 
463
- # Check if watcher is already running
466
+ CONFIG_FILE="$HOME/.claudetools/config.json"
467
+ CONTEXT_FILE="$HOME/.claudetools/session-context.md"
468
+ PROJECTS_FILE="$HOME/.claudetools/projects.json"
464
469
  WATCHER_PID_FILE="/tmp/claudetools-watcher.pid"
470
+
471
+ # --- Watcher Management ---
472
+
473
+ # Check if watcher is already running
465
474
  if [ -f "$WATCHER_PID_FILE" ]; then
466
475
  PID=$(cat "$WATCHER_PID_FILE")
467
- if kill -0 "$PID" 2>/dev/null; then
468
- # Watcher is running
469
- exit 0
476
+ if ! kill -0 "$PID" 2>/dev/null; then
477
+ # PID file exists but process is dead, clean up
478
+ rm -f "$WATCHER_PID_FILE"
470
479
  fi
471
480
  fi
472
481
 
473
- # Start watcher in background if claudetools is installed
474
- if command -v claudetools &> /dev/null; then
482
+ # Start watcher in background if not running
483
+ if [ ! -f "$WATCHER_PID_FILE" ] && command -v claudetools &> /dev/null; then
475
484
  nohup claudetools watch > /tmp/claudetools-watcher.log 2>&1 &
476
485
  echo $! > "$WATCHER_PID_FILE"
477
486
  fi
487
+
488
+ # --- Context Injection ---
489
+
490
+ # Read config
491
+ if [ ! -f "$CONFIG_FILE" ]; then exit 0; fi
492
+
493
+ API_URL=$(jq -r '.apiUrl // "https://api.claudetools.dev"' "$CONFIG_FILE" 2>/dev/null)
494
+ API_KEY=$(jq -r '.apiKey // empty' "$CONFIG_FILE" 2>/dev/null)
495
+
496
+ if [ -z "$API_KEY" ]; then exit 0; fi
497
+
498
+ # Get current project from working directory
499
+ CWD=$(pwd)
500
+ PROJECT_ID=""
501
+ PROJECT_NAME=""
502
+
503
+ if [ -f "$PROJECTS_FILE" ]; then
504
+ # Find project by path prefix match
505
+ PROJECT_DATA=$(jq -r --arg cwd "$CWD" '
506
+ .bindings[]? | select(.local_path != null) |
507
+ select($cwd | startswith(.local_path)) |
508
+ {project_id, project_name}' "$PROJECTS_FILE" 2>/dev/null | head -1)
509
+
510
+ if [ -n "$PROJECT_DATA" ]; then
511
+ PROJECT_ID=$(echo "$PROJECT_DATA" | jq -r '.project_id // empty')
512
+ PROJECT_NAME=$(echo "$PROJECT_DATA" | jq -r '.project_name // empty')
513
+ fi
514
+ fi
515
+
516
+ # If no project found, use directory name
517
+ if [ -z "$PROJECT_NAME" ]; then
518
+ PROJECT_NAME=$(basename "$CWD")
519
+ fi
520
+
521
+ # Fetch context from API (with 2s timeout)
522
+ if [ -n "$PROJECT_ID" ]; then
523
+ CONTEXT=$(curl -s --max-time 2 -X POST "$API_URL/api/v1/context/session" \\
524
+ -H "Authorization: Bearer $API_KEY" \\
525
+ -H "Content-Type: application/json" \\
526
+ -d "{\\"project_id\\": \\"$PROJECT_ID\\"}" 2>/dev/null)
527
+
528
+ if [ -n "$CONTEXT" ] && [ "$CONTEXT" != "null" ]; then
529
+ # Extract facts and patterns from response
530
+ FACTS=$(echo "$CONTEXT" | jq -r '.facts // []' 2>/dev/null)
531
+ PATTERNS=$(echo "$CONTEXT" | jq -r '.patterns // []' 2>/dev/null)
532
+
533
+ # Generate session context file
534
+ cat > "$CONTEXT_FILE" << CTXEOF
535
+ # Session Context: $PROJECT_NAME
536
+
537
+ **Project ID:** $PROJECT_ID
538
+ **Generated:** $(date -u +"%Y-%m-%dT%H:%M:%SZ")
539
+
540
+ ## Relevant Facts
541
+
542
+ $(echo "$FACTS" | jq -r '.[] | "- \\(.entity1) \\(.relationship) \\(.entity2): \\(.context)"' 2>/dev/null || echo "- No facts stored yet")
543
+
544
+ ## Detected Patterns
545
+
546
+ $(echo "$PATTERNS" | jq -r '.[] | "- **\\(.name)**: \\(.description)"' 2>/dev/null || echo "- No patterns detected yet")
547
+
548
+ ---
549
+ *This file is regenerated each session. Do not edit manually.*
550
+ CTXEOF
551
+ fi
552
+ else
553
+ # No project registered, create minimal context file
554
+ cat > "$CONTEXT_FILE" << CTXEOF
555
+ # Session Context: $PROJECT_NAME
556
+
557
+ **Status:** Project not registered with ClaudeTools
558
+
559
+ Run \\\`claudetools init\\\` in this directory to register the project and enable memory features.
560
+
561
+ ---
562
+ *This file is regenerated each session.*
563
+ CTXEOF
564
+ fi
478
565
  `;
479
566
  const sessionStartPath = join(HOOKS_DIR, 'session-start.sh');
480
567
  if (existsSync(sessionStartPath)) {
@@ -662,6 +749,83 @@ async function configureSettings() {
662
749
  success(`Saved settings to ${SETTINGS_PATH}`);
663
750
  }
664
751
  // -----------------------------------------------------------------------------
752
+ // CLAUDE.md Configuration
753
+ // -----------------------------------------------------------------------------
754
+ async function configureCLAUDEMD() {
755
+ header('CLAUDE.md Configuration');
756
+ ensureClaudeDir();
757
+ // Check if CLAUDE.md exists
758
+ let existingContent = '';
759
+ if (existsSync(CLAUDE_MD_PATH)) {
760
+ existingContent = readFileSync(CLAUDE_MD_PATH, 'utf-8');
761
+ // Check if claudetools section already exists
762
+ if (existingContent.includes(SECTION_START)) {
763
+ info('ClaudeTools section already exists in CLAUDE.md');
764
+ const { update } = await prompts({
765
+ type: 'confirm',
766
+ name: 'update',
767
+ message: 'Update existing ClaudeTools section?',
768
+ initial: true,
769
+ });
770
+ if (update) {
771
+ // Remove existing section
772
+ const startIdx = existingContent.indexOf(SECTION_START);
773
+ const endIdx = existingContent.indexOf(SECTION_END);
774
+ if (startIdx !== -1 && endIdx !== -1) {
775
+ existingContent =
776
+ existingContent.slice(0, startIdx).trimEnd() +
777
+ '\n' +
778
+ existingContent.slice(endIdx + SECTION_END.length).trimStart();
779
+ }
780
+ }
781
+ else {
782
+ return;
783
+ }
784
+ }
785
+ // Backup existing file
786
+ const backup = backupFile(CLAUDE_MD_PATH);
787
+ if (backup) {
788
+ info(`Backed up existing CLAUDE.md to ${basename(backup)}`);
789
+ }
790
+ }
791
+ // Append claudetools section
792
+ const newContent = existingContent.trimEnd() + '\n' + GLOBAL_TEMPLATE;
793
+ writeFileSync(CLAUDE_MD_PATH, newContent);
794
+ success('Added ClaudeTools section to CLAUDE.md');
795
+ }
796
+ function removeCLAUDEMDSection() {
797
+ if (!existsSync(CLAUDE_MD_PATH)) {
798
+ return;
799
+ }
800
+ try {
801
+ let content = readFileSync(CLAUDE_MD_PATH, 'utf-8');
802
+ if (!content.includes(SECTION_START)) {
803
+ return;
804
+ }
805
+ const startIdx = content.indexOf(SECTION_START);
806
+ const endIdx = content.indexOf(SECTION_END);
807
+ if (startIdx !== -1 && endIdx !== -1) {
808
+ content =
809
+ content.slice(0, startIdx).trimEnd() +
810
+ '\n' +
811
+ content.slice(endIdx + SECTION_END.length).trimStart();
812
+ // If content is now empty (only whitespace), remove the file
813
+ if (content.trim() === '') {
814
+ const { unlinkSync } = require('fs');
815
+ unlinkSync(CLAUDE_MD_PATH);
816
+ success('Removed CLAUDE.md (was only claudetools content)');
817
+ }
818
+ else {
819
+ writeFileSync(CLAUDE_MD_PATH, content.trim() + '\n');
820
+ success('Removed ClaudeTools section from CLAUDE.md');
821
+ }
822
+ }
823
+ }
824
+ catch {
825
+ error('Could not update CLAUDE.md');
826
+ }
827
+ }
828
+ // -----------------------------------------------------------------------------
665
829
  // Verification
666
830
  // -----------------------------------------------------------------------------
667
831
  async function verifySetup(config) {
@@ -712,6 +876,19 @@ async function verifySetup(config) {
712
876
  else {
713
877
  error('Settings not found');
714
878
  }
879
+ // Check CLAUDE.md configured
880
+ if (existsSync(CLAUDE_MD_PATH)) {
881
+ const content = readFileSync(CLAUDE_MD_PATH, 'utf-8');
882
+ if (content.includes(SECTION_START)) {
883
+ success('CLAUDE.md configured');
884
+ }
885
+ else {
886
+ error('CLAUDE.md missing ClaudeTools section');
887
+ }
888
+ }
889
+ else {
890
+ error('CLAUDE.md not found');
891
+ }
715
892
  }
716
893
  // -----------------------------------------------------------------------------
717
894
  // Main Setup Flow
@@ -728,7 +905,7 @@ export async function runSetup() {
728
905
  // Step 1: Authentication
729
906
  header('Authentication');
730
907
  if (config.apiKey) {
731
- info(`Existing API key found: ${config.apiKey.substring(0, 10)}...`);
908
+ info(`Existing API key found: ${config.apiKey.substring(0, 6)}...`);
732
909
  const { replace } = await prompts({
733
910
  type: 'confirm',
734
911
  name: 'replace',
@@ -801,7 +978,9 @@ export async function runSetup() {
801
978
  await installHooks();
802
979
  // Step 8: Configure Settings
803
980
  await configureSettings();
804
- // Step 9: Verify
981
+ // Step 9: Configure CLAUDE.md
982
+ await configureCLAUDEMD();
983
+ // Step 10: Verify
805
984
  await verifySetup(extendedConfig);
806
985
  // Done
807
986
  header('Setup Complete');
@@ -889,6 +1068,128 @@ export async function runUninstall() {
889
1068
  // Process might already be dead
890
1069
  }
891
1070
  }
1071
+ // Remove CLAUDE.md section
1072
+ removeCLAUDEMDSection();
892
1073
  console.log('\n' + chalk.green('ClaudeTools removed from Claude Code.'));
893
1074
  console.log(chalk.dim('Your ~/.claudetools/ config and data are preserved.\n'));
894
1075
  }
1076
+ // -----------------------------------------------------------------------------
1077
+ // Project Init
1078
+ // -----------------------------------------------------------------------------
1079
+ export async function runInit() {
1080
+ console.log('\n' + chalk.bold.cyan(' ClaudeTools Project Init') + '\n');
1081
+ const cwd = process.cwd();
1082
+ const projectName = basename(cwd);
1083
+ const projectClaudeDir = join(cwd, '.claude');
1084
+ const projectClaudeMd = join(projectClaudeDir, 'CLAUDE.md');
1085
+ // Check if config exists
1086
+ const config = await loadConfigFromFile();
1087
+ if (!config?.apiKey) {
1088
+ error('ClaudeTools not configured. Run "claudetools --setup" first.');
1089
+ process.exit(1);
1090
+ }
1091
+ // Try to register project with API
1092
+ const spinner = ora('Registering project...').start();
1093
+ let projectId;
1094
+ try {
1095
+ const response = await fetch(`${config.apiUrl || DEFAULT_CONFIG.apiUrl}/api/v1/projects/register`, {
1096
+ method: 'POST',
1097
+ headers: {
1098
+ 'Content-Type': 'application/json',
1099
+ 'Authorization': `Bearer ${config.apiKey}`,
1100
+ },
1101
+ body: JSON.stringify({
1102
+ name: projectName,
1103
+ local_path: cwd,
1104
+ }),
1105
+ });
1106
+ if (response.ok) {
1107
+ const data = await response.json();
1108
+ projectId = data.project_id;
1109
+ spinner.succeed(`Registered project: ${projectId}`);
1110
+ // Update local projects.json
1111
+ try {
1112
+ const projectsData = existsSync(PROJECTS_FILE)
1113
+ ? JSON.parse(readFileSync(PROJECTS_FILE, 'utf-8'))
1114
+ : { bindings: [] };
1115
+ // Check if already in bindings
1116
+ const existingIdx = projectsData.bindings.findIndex((b) => b.local_path === cwd);
1117
+ if (existingIdx >= 0) {
1118
+ projectsData.bindings[existingIdx].project_id = projectId;
1119
+ }
1120
+ else {
1121
+ projectsData.bindings.push({
1122
+ project_id: projectId,
1123
+ local_path: cwd,
1124
+ project_name: projectName,
1125
+ cached_at: new Date().toISOString(),
1126
+ });
1127
+ }
1128
+ projectsData.last_sync = new Date().toISOString();
1129
+ writeFileSync(PROJECTS_FILE, JSON.stringify(projectsData, null, 2));
1130
+ }
1131
+ catch {
1132
+ // Non-fatal
1133
+ }
1134
+ }
1135
+ else {
1136
+ spinner.warn('Could not register with API, using local ID');
1137
+ projectId = `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
1138
+ }
1139
+ }
1140
+ catch {
1141
+ spinner.warn('Could not reach API, using local ID');
1142
+ projectId = `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
1143
+ }
1144
+ // Create .claude directory if needed
1145
+ if (!existsSync(projectClaudeDir)) {
1146
+ mkdirSync(projectClaudeDir, { recursive: true });
1147
+ success('Created .claude/ directory');
1148
+ }
1149
+ // Create or update CLAUDE.md
1150
+ let existingContent = '';
1151
+ if (existsSync(projectClaudeMd)) {
1152
+ existingContent = readFileSync(projectClaudeMd, 'utf-8');
1153
+ // Check if claudetools section already exists
1154
+ if (existingContent.includes(SECTION_START)) {
1155
+ info('ClaudeTools section already exists');
1156
+ const { update } = await prompts({
1157
+ type: 'confirm',
1158
+ name: 'update',
1159
+ message: 'Update existing ClaudeTools section?',
1160
+ initial: true,
1161
+ });
1162
+ if (update) {
1163
+ // Remove existing section
1164
+ const startIdx = existingContent.indexOf(SECTION_START);
1165
+ const endIdx = existingContent.indexOf(SECTION_END);
1166
+ if (startIdx !== -1 && endIdx !== -1) {
1167
+ existingContent =
1168
+ existingContent.slice(0, startIdx).trimEnd() +
1169
+ '\n' +
1170
+ existingContent.slice(endIdx + SECTION_END.length).trimStart();
1171
+ }
1172
+ }
1173
+ else {
1174
+ console.log('\n' + chalk.green('Project already initialized.'));
1175
+ return;
1176
+ }
1177
+ }
1178
+ // Backup existing file
1179
+ const backup = backupFile(projectClaudeMd);
1180
+ if (backup) {
1181
+ info(`Backed up existing CLAUDE.md to ${basename(backup)}`);
1182
+ }
1183
+ }
1184
+ // Write project CLAUDE.md
1185
+ const projectContent = getProjectTemplate(projectId, projectName);
1186
+ const newContent = existingContent.trimEnd() + '\n' + projectContent;
1187
+ writeFileSync(projectClaudeMd, newContent);
1188
+ success('Created .claude/CLAUDE.md');
1189
+ // Summary
1190
+ console.log('\n' + chalk.green(' Project initialized!\n'));
1191
+ console.log(' ' + chalk.bold('Project:') + ` ${projectName}`);
1192
+ console.log(' ' + chalk.bold('ID:') + ` ${projectId}`);
1193
+ console.log(' ' + chalk.bold('Config:') + ` ${projectClaudeMd}\n`);
1194
+ console.log(chalk.dim(' Memory tools are now configured for this project.\n'));
1195
+ }
@@ -0,0 +1,24 @@
1
+ export declare const SECTION_START = "<!-- CLAUDETOOLS:START -->";
2
+ export declare const SECTION_END = "<!-- CLAUDETOOLS:END -->";
3
+ export declare const PROJECT_SECTION_START = "<!-- CLAUDETOOLS:PROJECT:START -->";
4
+ export declare const PROJECT_SECTION_END = "<!-- CLAUDETOOLS:PROJECT:END -->";
5
+ /**
6
+ * Global CLAUDE.md content - added to ~/.claude/CLAUDE.md
7
+ */
8
+ export declare const GLOBAL_TEMPLATE = "\n<!-- CLAUDETOOLS:START -->\n# ClaudeTools Memory System\n\nYou have access to a persistent memory system via the `claudetools_memory` MCP server. Use it to remember context across sessions.\n\n## Memory Tools\n\n### Searching Memory\n```\nmemory_search(query: \"authentication patterns\")\n```\nSearch for relevant facts, entities, and past context. Use this when:\n- Starting work on a feature to recall past decisions\n- Looking for patterns or conventions used before\n- Finding related code or architectural context\n\n### Storing Facts\n```\nmemory_store_fact(\n entity1: \"UserService\",\n relationship: \"USES\",\n entity2: \"bcrypt\",\n context: \"Password hashing uses bcrypt with 12 rounds\"\n)\n```\nStore important facts as relationships between entities. Use for:\n- Architectural decisions\n- Code patterns and conventions\n- Dependencies and relationships\n- User preferences learned during conversation\n\n### Context Injection\nContext is automatically injected at the start of each session based on the current project. Check `~/.claudetools/session-context.md` for project-specific context.\n\n## Task Management\n\n### Creating Work Plans\n```\ntask_plan(\n goal: \"Add user authentication\",\n epic_title: \"User Auth System\",\n tasks: [...]\n)\n```\nBreak down complex work into tracked tasks.\n\n### Starting Tasks\n```\ntask_start(task_id: \"task_xxx\")\n```\nClaim a task before working on it.\n\n### Completing Tasks\n```\ntask_complete(task_id: \"task_xxx\", summary: \"Implemented JWT auth with refresh tokens\")\n```\nMark tasks done with a summary of work completed.\n\n## Codebase Intelligence\n\n### Finding Code\n```\ncodebase_map() # Get overview of project structure\ncodebase_find(\"UserService\") # Find symbols/files\ncodebase_context(\"src/auth.ts\") # Get file dependencies\n```\n\n### Impact Analysis\n```\nanalyze_impact(function_name: \"validateToken\")\n```\nSee what would be affected by changing a function.\n\n## Best Practices\n\n1. **Search before implementing** - Check memory for existing patterns\n2. **Store decisions** - Save architectural choices as facts\n3. **Use task tracking** - Break complex work into tasks\n4. **Complete tasks** - Always mark tasks done with summaries\n<!-- CLAUDETOOLS:END -->\n";
9
+ /**
10
+ * Project-level CLAUDE.md content - added to .claude/CLAUDE.md
11
+ */
12
+ export declare function getProjectTemplate(projectId: string, projectName: string): string;
13
+ /**
14
+ * Session context template - written to ~/.claudetools/session-context.md
15
+ */
16
+ export declare function getSessionContextTemplate(projectName: string, projectId: string, facts: Array<{
17
+ entity1: string;
18
+ relationship: string;
19
+ entity2: string;
20
+ context: string;
21
+ }>, patterns: Array<{
22
+ name: string;
23
+ description: string;
24
+ }>): string;
@@ -0,0 +1,157 @@
1
+ // =============================================================================
2
+ // CLAUDE.md Template Content
3
+ // =============================================================================
4
+ // Templates for global and project-level CLAUDE.md integration
5
+ // Markers for identifying claudetools sections
6
+ export const SECTION_START = '<!-- CLAUDETOOLS:START -->';
7
+ export const SECTION_END = '<!-- CLAUDETOOLS:END -->';
8
+ export const PROJECT_SECTION_START = '<!-- CLAUDETOOLS:PROJECT:START -->';
9
+ export const PROJECT_SECTION_END = '<!-- CLAUDETOOLS:PROJECT:END -->';
10
+ /**
11
+ * Global CLAUDE.md content - added to ~/.claude/CLAUDE.md
12
+ */
13
+ export const GLOBAL_TEMPLATE = `
14
+ ${SECTION_START}
15
+ # ClaudeTools Memory System
16
+
17
+ You have access to a persistent memory system via the \`claudetools_memory\` MCP server. Use it to remember context across sessions.
18
+
19
+ ## Memory Tools
20
+
21
+ ### Searching Memory
22
+ \`\`\`
23
+ memory_search(query: "authentication patterns")
24
+ \`\`\`
25
+ Search for relevant facts, entities, and past context. Use this when:
26
+ - Starting work on a feature to recall past decisions
27
+ - Looking for patterns or conventions used before
28
+ - Finding related code or architectural context
29
+
30
+ ### Storing Facts
31
+ \`\`\`
32
+ memory_store_fact(
33
+ entity1: "UserService",
34
+ relationship: "USES",
35
+ entity2: "bcrypt",
36
+ context: "Password hashing uses bcrypt with 12 rounds"
37
+ )
38
+ \`\`\`
39
+ Store important facts as relationships between entities. Use for:
40
+ - Architectural decisions
41
+ - Code patterns and conventions
42
+ - Dependencies and relationships
43
+ - User preferences learned during conversation
44
+
45
+ ### Context Injection
46
+ Context is automatically injected at the start of each session based on the current project. Check \`~/.claudetools/session-context.md\` for project-specific context.
47
+
48
+ ## Task Management
49
+
50
+ ### Creating Work Plans
51
+ \`\`\`
52
+ task_plan(
53
+ goal: "Add user authentication",
54
+ epic_title: "User Auth System",
55
+ tasks: [...]
56
+ )
57
+ \`\`\`
58
+ Break down complex work into tracked tasks.
59
+
60
+ ### Starting Tasks
61
+ \`\`\`
62
+ task_start(task_id: "task_xxx")
63
+ \`\`\`
64
+ Claim a task before working on it.
65
+
66
+ ### Completing Tasks
67
+ \`\`\`
68
+ task_complete(task_id: "task_xxx", summary: "Implemented JWT auth with refresh tokens")
69
+ \`\`\`
70
+ Mark tasks done with a summary of work completed.
71
+
72
+ ## Codebase Intelligence
73
+
74
+ ### Finding Code
75
+ \`\`\`
76
+ codebase_map() # Get overview of project structure
77
+ codebase_find("UserService") # Find symbols/files
78
+ codebase_context("src/auth.ts") # Get file dependencies
79
+ \`\`\`
80
+
81
+ ### Impact Analysis
82
+ \`\`\`
83
+ analyze_impact(function_name: "validateToken")
84
+ \`\`\`
85
+ See what would be affected by changing a function.
86
+
87
+ ## Best Practices
88
+
89
+ 1. **Search before implementing** - Check memory for existing patterns
90
+ 2. **Store decisions** - Save architectural choices as facts
91
+ 3. **Use task tracking** - Break complex work into tasks
92
+ 4. **Complete tasks** - Always mark tasks done with summaries
93
+ ${SECTION_END}
94
+ `;
95
+ /**
96
+ * Project-level CLAUDE.md content - added to .claude/CLAUDE.md
97
+ */
98
+ export function getProjectTemplate(projectId, projectName) {
99
+ return `
100
+ ${SECTION_START}
101
+ # Project: ${projectName}
102
+
103
+ This project is registered with ClaudeTools Memory.
104
+
105
+ **Project ID:** \`${projectId}\`
106
+
107
+ ## Project Memory
108
+
109
+ Use memory tools to search and store project-specific context:
110
+
111
+ \`\`\`
112
+ # Search this project's memory
113
+ memory_search(query: "your search", project_id: "${projectId}")
114
+
115
+ # Store a project fact
116
+ memory_store_fact(
117
+ entity1: "ComponentName",
118
+ relationship: "IMPLEMENTS",
119
+ entity2: "PatternName",
120
+ context: "Description of the relationship",
121
+ project_id: "${projectId}"
122
+ )
123
+ \`\`\`
124
+
125
+ ## Session Context
126
+
127
+ Project-specific context is injected automatically. Check \`~/.claudetools/session-context.md\` for current context.
128
+ ${SECTION_END}
129
+ `;
130
+ }
131
+ /**
132
+ * Session context template - written to ~/.claudetools/session-context.md
133
+ */
134
+ export function getSessionContextTemplate(projectName, projectId, facts, patterns) {
135
+ const factsList = facts.length > 0
136
+ ? facts.map(f => `- ${f.entity1} ${f.relationship} ${f.entity2}: ${f.context}`).join('\n')
137
+ : '- No facts stored yet';
138
+ const patternsList = patterns.length > 0
139
+ ? patterns.map(p => `- **${p.name}**: ${p.description}`).join('\n')
140
+ : '- No patterns detected yet';
141
+ return `# Session Context: ${projectName}
142
+
143
+ **Project ID:** ${projectId}
144
+ **Generated:** ${new Date().toISOString()}
145
+
146
+ ## Relevant Facts
147
+
148
+ ${factsList}
149
+
150
+ ## Detected Patterns
151
+
152
+ ${patternsList}
153
+
154
+ ---
155
+ *This file is regenerated each session. Do not edit manually.*
156
+ `;
157
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudetools/tools",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Persistent AI memory, task management, and codebase intelligence for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",