@claudetools/tools 0.4.0 → 0.5.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/README.md +60 -4
- package/dist/cli.js +0 -0
- package/dist/codedna/parser.d.ts +40 -3
- package/dist/codedna/parser.js +65 -8
- package/dist/codedna/registry.js +4 -1
- package/dist/codedna/template-engine.js +66 -32
- package/dist/handlers/codedna-handlers.d.ts +1 -1
- package/dist/handlers/codedna-handlers.js +27 -0
- package/dist/helpers/api-client.js +7 -0
- package/dist/helpers/codedna-monitoring.d.ts +34 -0
- package/dist/helpers/codedna-monitoring.js +159 -0
- package/dist/helpers/error-tracking.d.ts +73 -0
- package/dist/helpers/error-tracking.js +164 -0
- package/dist/helpers/usage-analytics.d.ts +91 -0
- package/dist/helpers/usage-analytics.js +256 -0
- package/docs/AUTO-REGISTRATION.md +353 -0
- package/docs/CLAUDE4_PROMPT_ANALYSIS.md +589 -0
- package/docs/ENTITY_DSL_REFERENCE.md +685 -0
- package/docs/MODERN_STACK_COMPLETE_GUIDE.md +706 -0
- package/docs/PROMPT_STANDARDIZATION_RESULTS.md +324 -0
- package/docs/PROMPT_TIER_TEMPLATES.md +787 -0
- package/docs/RESEARCH_METHODOLOGY_EXTRACTION.md +336 -0
- package/package.json +12 -3
- package/scripts/verify-prompt-compliance.sh +197 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// CodeDNA Usage Analytics
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Track CodeDNA usage patterns, token savings, and generator popularity
|
|
6
|
+
// for analytics and optimization insights.
|
|
7
|
+
//
|
|
8
|
+
import { storeFact, searchMemory } from './api-client.js';
|
|
9
|
+
import { DEFAULT_USER_ID, resolveProjectId } from './config.js';
|
|
10
|
+
export class UsageAnalytics {
|
|
11
|
+
static instance;
|
|
12
|
+
constructor() { }
|
|
13
|
+
static getInstance() {
|
|
14
|
+
if (!UsageAnalytics.instance) {
|
|
15
|
+
UsageAnalytics.instance = new UsageAnalytics();
|
|
16
|
+
}
|
|
17
|
+
return UsageAnalytics.instance;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Track a successful generation
|
|
21
|
+
*/
|
|
22
|
+
async trackGeneration(event) {
|
|
23
|
+
try {
|
|
24
|
+
const usageEvent = {
|
|
25
|
+
...event,
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
success: true,
|
|
28
|
+
};
|
|
29
|
+
const userId = DEFAULT_USER_ID;
|
|
30
|
+
const projectId = event.projectId || resolveProjectId();
|
|
31
|
+
// Store as fact in memory system
|
|
32
|
+
await storeFact(projectId, 'CodeDNA', 'GENERATION_COMPLETED', event.generator || event.operation, JSON.stringify(usageEvent), userId);
|
|
33
|
+
console.log('[CodeDNA Analytics]', {
|
|
34
|
+
operation: event.operation,
|
|
35
|
+
generator: event.generator,
|
|
36
|
+
tokensSaved: event.tokensSaved,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// Don't fail the operation if analytics fails
|
|
41
|
+
console.error('[CodeDNA Analytics Failed]', error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Track a validation
|
|
46
|
+
*/
|
|
47
|
+
async trackValidation(spec, valid, entityName, fieldCount, projectId) {
|
|
48
|
+
await this.trackGeneration({
|
|
49
|
+
operation: 'validate_spec',
|
|
50
|
+
entityName,
|
|
51
|
+
fieldCount,
|
|
52
|
+
specLength: spec.length,
|
|
53
|
+
success: valid,
|
|
54
|
+
projectId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Export singleton instance
|
|
59
|
+
export const analytics = UsageAnalytics.getInstance();
|
|
60
|
+
/**
|
|
61
|
+
* Query usage analytics
|
|
62
|
+
*/
|
|
63
|
+
export async function getUsageAnalytics(query) {
|
|
64
|
+
const userId = DEFAULT_USER_ID;
|
|
65
|
+
const projectId = resolveProjectId();
|
|
66
|
+
// Search for usage facts
|
|
67
|
+
const searchQuery = `CodeDNA GENERATION_COMPLETED ${query.operation || ''}`;
|
|
68
|
+
const result = await searchMemory(projectId, searchQuery, 500, userId);
|
|
69
|
+
// Parse usage events from facts
|
|
70
|
+
const events = result.relevant_facts
|
|
71
|
+
.map((fact) => {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(fact.fact);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
.filter((e) => e !== null)
|
|
80
|
+
.filter((e) => {
|
|
81
|
+
const eventTime = new Date(e.timestamp).getTime();
|
|
82
|
+
const start = new Date(query.startTime).getTime();
|
|
83
|
+
const end = new Date(query.endTime).getTime();
|
|
84
|
+
return eventTime >= start && eventTime <= end;
|
|
85
|
+
})
|
|
86
|
+
.filter((e) => !query.operation || e.operation === query.operation)
|
|
87
|
+
.filter((e) => !query.generator || e.generator === query.generator)
|
|
88
|
+
.filter((e) => !query.framework || e.framework === query.framework);
|
|
89
|
+
// Calculate statistics
|
|
90
|
+
const totalTokensSaved = events.reduce((sum, e) => sum + (e.tokensSaved || 0), 0);
|
|
91
|
+
const totalLinesOfCode = events.reduce((sum, e) => sum + (e.linesOfCode || 0), 0);
|
|
92
|
+
const totalFiles = events.reduce((sum, e) => sum + (e.filesGenerated || 0), 0);
|
|
93
|
+
// Generator statistics
|
|
94
|
+
const generatorStats = {};
|
|
95
|
+
events.forEach(event => {
|
|
96
|
+
const gen = event.generator || 'unknown';
|
|
97
|
+
if (!generatorStats[gen]) {
|
|
98
|
+
generatorStats[gen] = { count: 0, tokensSaved: 0, linesOfCode: 0, filesGenerated: 0 };
|
|
99
|
+
}
|
|
100
|
+
generatorStats[gen].count++;
|
|
101
|
+
generatorStats[gen].tokensSaved += event.tokensSaved || 0;
|
|
102
|
+
generatorStats[gen].linesOfCode += event.linesOfCode || 0;
|
|
103
|
+
generatorStats[gen].filesGenerated += event.filesGenerated || 0;
|
|
104
|
+
});
|
|
105
|
+
// Framework statistics
|
|
106
|
+
const frameworkStats = {};
|
|
107
|
+
events.forEach(event => {
|
|
108
|
+
if (event.framework) {
|
|
109
|
+
frameworkStats[event.framework] = (frameworkStats[event.framework] || 0) + 1;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// Options usage
|
|
113
|
+
let authCount = 0;
|
|
114
|
+
let validationCount = 0;
|
|
115
|
+
let testsCount = 0;
|
|
116
|
+
events.forEach(event => {
|
|
117
|
+
if (event.options?.auth)
|
|
118
|
+
authCount++;
|
|
119
|
+
if (event.options?.validation)
|
|
120
|
+
validationCount++;
|
|
121
|
+
if (event.options?.tests)
|
|
122
|
+
testsCount++;
|
|
123
|
+
});
|
|
124
|
+
// Entity patterns
|
|
125
|
+
const entityPatterns = {};
|
|
126
|
+
events.forEach(event => {
|
|
127
|
+
if (event.entityName) {
|
|
128
|
+
entityPatterns[event.entityName] = (entityPatterns[event.entityName] || 0) + 1;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
// Average execution time
|
|
132
|
+
const executionTimes = events.filter(e => e.executionTimeMs).map(e => e.executionTimeMs);
|
|
133
|
+
const avgExecutionTime = executionTimes.length > 0
|
|
134
|
+
? executionTimes.reduce((sum, t) => sum + t, 0) / executionTimes.length
|
|
135
|
+
: undefined;
|
|
136
|
+
return {
|
|
137
|
+
totalGenerations: events.length,
|
|
138
|
+
totalTokensSaved,
|
|
139
|
+
avgTokensSavedPerGeneration: events.length > 0 ? totalTokensSaved / events.length : 0,
|
|
140
|
+
totalLinesOfCode,
|
|
141
|
+
totalFiles,
|
|
142
|
+
generatorStats,
|
|
143
|
+
frameworkStats,
|
|
144
|
+
optionsUsage: {
|
|
145
|
+
auth: authCount,
|
|
146
|
+
validation: validationCount,
|
|
147
|
+
tests: testsCount,
|
|
148
|
+
},
|
|
149
|
+
entityPatterns,
|
|
150
|
+
avgExecutionTime,
|
|
151
|
+
recentGenerations: events.slice(0, 10).reverse(),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get analytics for last 24 hours
|
|
156
|
+
*/
|
|
157
|
+
export async function getLast24HoursAnalytics() {
|
|
158
|
+
const endTime = new Date().toISOString();
|
|
159
|
+
const startTime = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
160
|
+
return getUsageAnalytics({ startTime, endTime });
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get analytics for last 7 days
|
|
164
|
+
*/
|
|
165
|
+
export async function getLast7DaysAnalytics() {
|
|
166
|
+
const endTime = new Date().toISOString();
|
|
167
|
+
const startTime = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
168
|
+
return getUsageAnalytics({ startTime, endTime });
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get analytics for last 30 days
|
|
172
|
+
*/
|
|
173
|
+
export async function getLast30DaysAnalytics() {
|
|
174
|
+
const endTime = new Date().toISOString();
|
|
175
|
+
const startTime = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
176
|
+
return getUsageAnalytics({ startTime, endTime });
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Print analytics report
|
|
180
|
+
*/
|
|
181
|
+
export function printAnalytics(result, title = 'CodeDNA Usage Analytics') {
|
|
182
|
+
console.log('\n=================================================');
|
|
183
|
+
console.log(title);
|
|
184
|
+
console.log('=================================================\n');
|
|
185
|
+
console.log('Overview:');
|
|
186
|
+
console.log(` Total Generations: ${result.totalGenerations}`);
|
|
187
|
+
console.log(` Total Tokens Saved: ${result.totalTokensSaved.toLocaleString()}`);
|
|
188
|
+
console.log(` Avg Tokens/Generation: ${Math.round(result.avgTokensSavedPerGeneration).toLocaleString()}`);
|
|
189
|
+
console.log(` Total Lines of Code: ${result.totalLinesOfCode.toLocaleString()}`);
|
|
190
|
+
console.log(` Total Files Generated: ${result.totalFiles}`);
|
|
191
|
+
if (result.avgExecutionTime) {
|
|
192
|
+
console.log(` Avg Execution Time: ${Math.round(result.avgExecutionTime)}ms`);
|
|
193
|
+
}
|
|
194
|
+
console.log();
|
|
195
|
+
if (Object.keys(result.generatorStats).length > 0) {
|
|
196
|
+
console.log('Generator Usage:');
|
|
197
|
+
Object.entries(result.generatorStats)
|
|
198
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
199
|
+
.forEach(([gen, stats]) => {
|
|
200
|
+
console.log(` ${gen}:`);
|
|
201
|
+
console.log(` Count: ${stats.count}`);
|
|
202
|
+
console.log(` Tokens Saved: ${stats.tokensSaved.toLocaleString()}`);
|
|
203
|
+
console.log(` Lines of Code: ${stats.linesOfCode.toLocaleString()}`);
|
|
204
|
+
console.log(` Files Generated: ${stats.filesGenerated}`);
|
|
205
|
+
});
|
|
206
|
+
console.log();
|
|
207
|
+
}
|
|
208
|
+
if (Object.keys(result.frameworkStats).length > 0) {
|
|
209
|
+
console.log('Framework Popularity:');
|
|
210
|
+
Object.entries(result.frameworkStats)
|
|
211
|
+
.sort((a, b) => b[1] - a[1])
|
|
212
|
+
.forEach(([framework, count]) => {
|
|
213
|
+
console.log(` ${framework}: ${count} generations`);
|
|
214
|
+
});
|
|
215
|
+
console.log();
|
|
216
|
+
}
|
|
217
|
+
console.log('Options Usage:');
|
|
218
|
+
console.log(` Auth: ${result.optionsUsage.auth} generations`);
|
|
219
|
+
console.log(` Validation: ${result.optionsUsage.validation} generations`);
|
|
220
|
+
console.log(` Tests: ${result.optionsUsage.tests} generations`);
|
|
221
|
+
console.log();
|
|
222
|
+
if (Object.keys(result.entityPatterns).length > 0) {
|
|
223
|
+
console.log('Popular Entities:');
|
|
224
|
+
Object.entries(result.entityPatterns)
|
|
225
|
+
.sort((a, b) => b[1] - a[1])
|
|
226
|
+
.slice(0, 10)
|
|
227
|
+
.forEach(([entity, count]) => {
|
|
228
|
+
console.log(` ${entity}: ${count} times`);
|
|
229
|
+
});
|
|
230
|
+
console.log();
|
|
231
|
+
}
|
|
232
|
+
if (result.recentGenerations.length > 0) {
|
|
233
|
+
console.log('Recent Generations:');
|
|
234
|
+
result.recentGenerations.forEach((gen, i) => {
|
|
235
|
+
console.log(` ${i + 1}. [${gen.timestamp}] ${gen.operation}`);
|
|
236
|
+
console.log(` Generator: ${gen.generator || 'N/A'}`);
|
|
237
|
+
console.log(` Entity: ${gen.entityName || 'N/A'}`);
|
|
238
|
+
console.log(` Tokens Saved: ${(gen.tokensSaved || 0).toLocaleString()}`);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
console.log('\n=================================================\n');
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Weekly analytics summary
|
|
245
|
+
*/
|
|
246
|
+
export async function weeklyAnalyticsSummary() {
|
|
247
|
+
console.log('Generating weekly CodeDNA analytics summary...\n');
|
|
248
|
+
const result = await getLast7DaysAnalytics();
|
|
249
|
+
printAnalytics(result, 'CodeDNA Weekly Analytics Summary');
|
|
250
|
+
// Calculate ROI
|
|
251
|
+
const estimatedCost = result.totalTokensSaved * 0.00001; // ~$0.01 per 1000 tokens
|
|
252
|
+
console.log('💰 Estimated Cost Savings:');
|
|
253
|
+
console.log(` Traditional approach: $${estimatedCost.toFixed(2)}`);
|
|
254
|
+
console.log(` With CodeDNA: $${(estimatedCost * 0.01).toFixed(2)} (99% savings)`);
|
|
255
|
+
console.log(` Total saved: $${(estimatedCost * 0.99).toFixed(2)}\n`);
|
|
256
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Auto Project Registration
|
|
2
|
+
|
|
3
|
+
The ClaudeTools Memory MCP server now supports automatic project registration. When you use the server in a new directory, it will automatically register that directory as a project with the ClaudeTools API and cache the binding locally.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
### 1. Project ID Resolution Flow
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MCP Server Starts
|
|
11
|
+
↓
|
|
12
|
+
Check CLAUDETOOLS_PROJECT_ID env var
|
|
13
|
+
↓ (if not set)
|
|
14
|
+
Check ~/.claudetools/projects.json cache
|
|
15
|
+
↓ (if no binding found)
|
|
16
|
+
Auto-register via API (if autoRegister: true)
|
|
17
|
+
↓
|
|
18
|
+
Cache project_id locally
|
|
19
|
+
↓
|
|
20
|
+
Use project_id for all memory operations
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. System Registration
|
|
24
|
+
|
|
25
|
+
On first use, the server registers your machine:
|
|
26
|
+
|
|
27
|
+
- **System ID**: Unique identifier for this machine (e.g., `sys_abc123...`)
|
|
28
|
+
- **Hostname**: Your machine's hostname
|
|
29
|
+
- **Platform**: Operating system (darwin, linux, win32)
|
|
30
|
+
- **Username**: Current user
|
|
31
|
+
|
|
32
|
+
The system ID is cached in `~/.claudetools/projects.json`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"system_id": "sys_bdbcff93724e4a05b488",
|
|
37
|
+
"bindings": [...],
|
|
38
|
+
"last_sync": "2025-12-03T02:37:44.635Z"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Project Binding
|
|
43
|
+
|
|
44
|
+
When you use the MCP server in a directory for the first time:
|
|
45
|
+
|
|
46
|
+
1. **Detects** project metadata:
|
|
47
|
+
- Project name (from directory name)
|
|
48
|
+
- Local path (absolute path)
|
|
49
|
+
- Git remote (if available)
|
|
50
|
+
|
|
51
|
+
2. **Registers** via API:
|
|
52
|
+
- Creates project in your ClaudeTools account
|
|
53
|
+
- Returns project_id (UUID format: `proj_xxxxxxxxxxxxxxxxxxxx`)
|
|
54
|
+
|
|
55
|
+
3. **Creates binding** linking:
|
|
56
|
+
- System ID (your machine)
|
|
57
|
+
- Project ID (the project)
|
|
58
|
+
- Local path (where the project lives)
|
|
59
|
+
|
|
60
|
+
4. **Caches** binding locally in `~/.claudetools/projects.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"bindings": [
|
|
65
|
+
{
|
|
66
|
+
"binding_id": "bind_904de164763a47d0924e",
|
|
67
|
+
"project_id": "proj_3a33ce7579874351b990",
|
|
68
|
+
"system_id": "sys_bdbcff93724e4a05b488",
|
|
69
|
+
"local_path": "/Users/you/Projects/myproject",
|
|
70
|
+
"git_remote": "git@github.com:you/myproject.git",
|
|
71
|
+
"project_name": "myproject",
|
|
72
|
+
"org_id": "your-org-id",
|
|
73
|
+
"cached_at": "2025-12-03T02:37:44.635Z"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"system_id": "sys_bdbcff93724e4a05b488",
|
|
77
|
+
"last_sync": "2025-12-03T02:37:44.635Z"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Configuration
|
|
82
|
+
|
|
83
|
+
### Enable/Disable Auto-Registration
|
|
84
|
+
|
|
85
|
+
Edit `~/.claudetools/config.json`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"autoRegister": true, // Enable auto-registration (default)
|
|
90
|
+
"apiUrl": "https://api.claudetools.dev",
|
|
91
|
+
"apiKey": "ct_live_..."
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
To disable auto-registration:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"autoRegister": false
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Set API Key
|
|
104
|
+
|
|
105
|
+
Three ways to configure your API key:
|
|
106
|
+
|
|
107
|
+
1. **Environment variable** (highest priority):
|
|
108
|
+
```bash
|
|
109
|
+
export CLAUDETOOLS_API_KEY="ct_live_..."
|
|
110
|
+
# or
|
|
111
|
+
export MEMORY_API_KEY="ct_live_..."
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
2. **Config file**:
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"apiKey": "ct_live_..."
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
3. **.env file** (in project root):
|
|
122
|
+
```bash
|
|
123
|
+
MEMORY_API_KEY=ct_live_...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Manual Project ID
|
|
127
|
+
|
|
128
|
+
If you want to use a specific project ID instead of auto-registering:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
export CLAUDETOOLS_PROJECT_ID="proj_xxxxxxxxxxxxxxxxxxxx"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Project ID Format Validation
|
|
135
|
+
|
|
136
|
+
All project IDs must follow the format:
|
|
137
|
+
- Prefix: `proj_`
|
|
138
|
+
- 20 hexadecimal characters
|
|
139
|
+
- Example: `proj_3a33ce7579874351b990`
|
|
140
|
+
|
|
141
|
+
Invalid formats will be rejected with a clear error message.
|
|
142
|
+
|
|
143
|
+
## Cache Management
|
|
144
|
+
|
|
145
|
+
### View Cache
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cat ~/.claudetools/projects.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Clear Cache
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
rm ~/.claudetools/projects.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The server will auto-register on next use (if `autoRegister: true`).
|
|
158
|
+
|
|
159
|
+
### Sync from API
|
|
160
|
+
|
|
161
|
+
Future feature - will pull all your project bindings from the API:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { syncProjectsFromAPI } from './helpers/project-registration.js';
|
|
165
|
+
await syncProjectsFromAPI();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Path Matching
|
|
169
|
+
|
|
170
|
+
The server uses smart path matching:
|
|
171
|
+
|
|
172
|
+
1. **Exact match**: `/Users/you/Projects/myproject` matches exactly
|
|
173
|
+
2. **Prefix match**: `/Users/you/Projects/myproject/src` uses parent binding
|
|
174
|
+
3. **Longest match**: If multiple bindings match, uses the longest path
|
|
175
|
+
|
|
176
|
+
Example:
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"bindings": [
|
|
180
|
+
{"local_path": "/Users/you/Projects"},
|
|
181
|
+
{"local_path": "/Users/you/Projects/myproject"}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Using server from `/Users/you/Projects/myproject/src`:
|
|
187
|
+
- ✅ Uses: `/Users/you/Projects/myproject` (longer match)
|
|
188
|
+
- ❌ Not: `/Users/you/Projects` (shorter match)
|
|
189
|
+
|
|
190
|
+
## Error Handling
|
|
191
|
+
|
|
192
|
+
### No API Key
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
Error: No API key found. Set CLAUDETOOLS_API_KEY or MEMORY_API_KEY in environment,
|
|
196
|
+
or configure via ~/.claudetools/config.json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Solution**: Set API key using one of the methods above.
|
|
200
|
+
|
|
201
|
+
### Auto-Registration Disabled
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Error: No project binding found for /path/to/project and auto-registration is disabled.
|
|
205
|
+
To register this project:
|
|
206
|
+
1. Set CLAUDETOOLS_PROJECT_ID environment variable with a valid proj_* UUID
|
|
207
|
+
2. Enable autoRegister in ~/.claudetools/config.json
|
|
208
|
+
3. Or manually register via API
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Solution**: Either:
|
|
212
|
+
- Enable auto-registration in config
|
|
213
|
+
- Set `CLAUDETOOLS_PROJECT_ID` manually
|
|
214
|
+
- Register project via ClaudeTools dashboard
|
|
215
|
+
|
|
216
|
+
### Invalid Project ID Format
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
Error: Invalid project ID format: my-project-123
|
|
220
|
+
Expected format: proj_xxxxxxxxxxxxxxxxxxxx (20 hex chars)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Solution**: Use a valid UUID format or let the system auto-register.
|
|
224
|
+
|
|
225
|
+
### Network Error
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
Error: Failed to auto-register project: API error: 500 Internal Server Error
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Solution**: Check:
|
|
232
|
+
1. API URL is correct in config
|
|
233
|
+
2. Network connectivity to `api.claudetools.dev`
|
|
234
|
+
3. API service status
|
|
235
|
+
|
|
236
|
+
## Testing
|
|
237
|
+
|
|
238
|
+
Run the registration test suite:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
cd mcp-server
|
|
242
|
+
npm run build
|
|
243
|
+
npx tsx test-registration.ts
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Expected output:
|
|
247
|
+
```
|
|
248
|
+
================================================================================
|
|
249
|
+
Testing Auto-Registration System
|
|
250
|
+
================================================================================
|
|
251
|
+
|
|
252
|
+
Test 1: Direct registration via getOrRegisterProject()
|
|
253
|
+
✅ Success: proj_3a33ce7579874351b990
|
|
254
|
+
|
|
255
|
+
Test 2: Resolution via resolveProjectIdAsync()
|
|
256
|
+
✅ Success: proj_3a33ce7579874351b990
|
|
257
|
+
|
|
258
|
+
Test 3: Verify consistency
|
|
259
|
+
✅ Both methods returned same ID: proj_3a33ce7579874351b990
|
|
260
|
+
|
|
261
|
+
Test 4: Check cache file
|
|
262
|
+
Cache file exists at: /Users/you/.claudetools/projects.json
|
|
263
|
+
Bindings count: 1
|
|
264
|
+
System ID: sys_bdbcff93724e4a05b488
|
|
265
|
+
|
|
266
|
+
✅ All tests passed!
|
|
267
|
+
================================================================================
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Known Issues
|
|
271
|
+
|
|
272
|
+
### API Schema Error (Temporary)
|
|
273
|
+
|
|
274
|
+
Currently, the project creation endpoint has a schema issue:
|
|
275
|
+
```
|
|
276
|
+
Error: table projects has no column named created_by
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Workaround**: Use an existing cached binding or manually set `CLAUDETOOLS_PROJECT_ID`.
|
|
280
|
+
|
|
281
|
+
This will be fixed in the next API deployment.
|
|
282
|
+
|
|
283
|
+
## Migration from Name-Based IDs
|
|
284
|
+
|
|
285
|
+
If you were previously using name-based IDs like `claude-code-myproject`:
|
|
286
|
+
|
|
287
|
+
1. **Delete old cache**: `rm ~/.claudetools/projects.json`
|
|
288
|
+
2. **Set API key**: Export `CLAUDETOOLS_API_KEY`
|
|
289
|
+
3. **Restart server**: Will auto-register with proper UUID
|
|
290
|
+
|
|
291
|
+
Or keep using the old ID by setting:
|
|
292
|
+
```bash
|
|
293
|
+
export CLAUDETOOLS_PROJECT_ID="claude-code-myproject"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
However, this is **not recommended** as UUID-based IDs provide:
|
|
297
|
+
- ✅ Global uniqueness
|
|
298
|
+
- ✅ Cross-machine project sharing
|
|
299
|
+
- ✅ Proper project management in ClaudeTools dashboard
|
|
300
|
+
- ✅ Multi-system bindings
|
|
301
|
+
|
|
302
|
+
## Architecture
|
|
303
|
+
|
|
304
|
+
### Key Files
|
|
305
|
+
|
|
306
|
+
- `src/helpers/project-registration.ts` - Registration logic
|
|
307
|
+
- `src/helpers/config.ts` - Project ID resolution
|
|
308
|
+
- `src/helpers/config-manager.ts` - Configuration management
|
|
309
|
+
- `~/.claudetools/config.json` - User configuration
|
|
310
|
+
- `~/.claudetools/projects.json` - Cached bindings
|
|
311
|
+
|
|
312
|
+
### API Endpoints
|
|
313
|
+
|
|
314
|
+
- `POST /api/v1/systems/register` - Register machine
|
|
315
|
+
- `POST /api/v1/projects` - Create project
|
|
316
|
+
- `POST /api/v1/systems/{system_id}/bindings` - Create binding
|
|
317
|
+
- `GET /api/v1/systems/{system_id}/bindings` - List bindings (future)
|
|
318
|
+
|
|
319
|
+
### Data Flow
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
User → MCP Server → resolveProjectIdAsync()
|
|
323
|
+
↓
|
|
324
|
+
Check cache
|
|
325
|
+
↓ (miss)
|
|
326
|
+
getOrRegisterProject()
|
|
327
|
+
↓
|
|
328
|
+
ensureSystemRegistered() → API
|
|
329
|
+
↓
|
|
330
|
+
registerProject() → API
|
|
331
|
+
↓
|
|
332
|
+
updateProjectsCache()
|
|
333
|
+
↓
|
|
334
|
+
Return project_id
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Best Practices
|
|
338
|
+
|
|
339
|
+
1. **Set API key globally**: Use environment variable or config file
|
|
340
|
+
2. **Enable auto-registration**: Let the system handle project setup
|
|
341
|
+
3. **One binding per project**: Don't create multiple bindings for same path
|
|
342
|
+
4. **Git remotes help**: They make project identification easier
|
|
343
|
+
5. **Cache is local**: Each machine has its own bindings cache
|
|
344
|
+
|
|
345
|
+
## Future Enhancements
|
|
346
|
+
|
|
347
|
+
- [ ] Sync bindings from API on startup
|
|
348
|
+
- [ ] Detect project changes (git remote updates)
|
|
349
|
+
- [ ] Multi-org support
|
|
350
|
+
- [ ] Project metadata updates
|
|
351
|
+
- [ ] Binding removal/cleanup
|
|
352
|
+
- [ ] Cross-machine project resolution
|
|
353
|
+
- [ ] Project templates
|