@claudetools/tools 0.3.1 → 0.3.3
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/setup.js +117 -43
- package/package.json +1 -1
package/dist/setup.js
CHANGED
|
@@ -7,8 +7,9 @@ import chalk from 'chalk';
|
|
|
7
7
|
import ora from 'ora';
|
|
8
8
|
import { homedir, hostname, platform } from 'os';
|
|
9
9
|
import { join, basename } from 'path';
|
|
10
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'fs';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, realpathSync } from 'fs';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
12
13
|
import { loadConfigFromFile, saveConfig, ensureConfigDir, getConfigPath, DEFAULT_CONFIG, } from './helpers/config-manager.js';
|
|
13
14
|
import { GLOBAL_TEMPLATE, SECTION_START, SECTION_END, getProjectTemplate, } from './templates/claude-md.js';
|
|
14
15
|
// -----------------------------------------------------------------------------
|
|
@@ -110,6 +111,29 @@ function initializeProjectsFile() {
|
|
|
110
111
|
}, null, 2));
|
|
111
112
|
}
|
|
112
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Detect git remote for a directory (secure implementation)
|
|
116
|
+
*/
|
|
117
|
+
function detectGitRemote(localPath) {
|
|
118
|
+
try {
|
|
119
|
+
// Resolve symlinks and validate path to prevent path traversal attacks
|
|
120
|
+
const resolvedPath = realpathSync(localPath);
|
|
121
|
+
// Ensure path is absolute and doesn't contain path traversal
|
|
122
|
+
if (!resolvedPath.startsWith('/') || resolvedPath.includes('..')) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
const gitRemote = execSync('git config --get remote.origin.url', {
|
|
126
|
+
cwd: resolvedPath,
|
|
127
|
+
encoding: 'utf-8',
|
|
128
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
129
|
+
}).trim();
|
|
130
|
+
return gitRemote || undefined;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Not a git repo or no remote configured
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
113
137
|
// -----------------------------------------------------------------------------
|
|
114
138
|
// Authentication
|
|
115
139
|
// -----------------------------------------------------------------------------
|
|
@@ -480,9 +504,11 @@ if [ -f "$WATCHER_PID_FILE" ]; then
|
|
|
480
504
|
fi
|
|
481
505
|
|
|
482
506
|
# Start watcher in background if not running
|
|
507
|
+
# The watcher manages its own PID file, so don't write it here
|
|
483
508
|
if [ ! -f "$WATCHER_PID_FILE" ] && command -v claudetools &> /dev/null; then
|
|
484
|
-
nohup claudetools watch
|
|
485
|
-
|
|
509
|
+
nohup claudetools watch >> /tmp/claudetools-watcher.log 2>&1 &
|
|
510
|
+
# Give the watcher a moment to start and write its PID file
|
|
511
|
+
sleep 1
|
|
486
512
|
fi
|
|
487
513
|
|
|
488
514
|
# --- Context Injection ---
|
|
@@ -1088,58 +1114,106 @@ export async function runInit() {
|
|
|
1088
1114
|
error('ClaudeTools not configured. Run "claudetools --setup" first.');
|
|
1089
1115
|
process.exit(1);
|
|
1090
1116
|
}
|
|
1117
|
+
// Load system info
|
|
1118
|
+
const systemInfo = loadSystemInfo();
|
|
1119
|
+
if (!systemInfo?.system_id) {
|
|
1120
|
+
error('System not registered. Run "claudetools --setup" first.');
|
|
1121
|
+
process.exit(1);
|
|
1122
|
+
}
|
|
1123
|
+
// Detect git remote for this directory
|
|
1124
|
+
const gitRemote = detectGitRemote(cwd);
|
|
1091
1125
|
// Try to register project with API
|
|
1092
1126
|
const spinner = ora('Registering project...').start();
|
|
1093
1127
|
let projectId;
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1128
|
+
// Use auto-bind endpoint if git remote exists
|
|
1129
|
+
if (gitRemote) {
|
|
1130
|
+
try {
|
|
1131
|
+
const response = await fetch(`${config.apiUrl || DEFAULT_CONFIG.apiUrl}/api/v1/systems/${systemInfo.system_id}/bindings/auto`, {
|
|
1132
|
+
method: 'POST',
|
|
1133
|
+
headers: {
|
|
1134
|
+
'Content-Type': 'application/json',
|
|
1135
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1136
|
+
},
|
|
1137
|
+
body: JSON.stringify({
|
|
1138
|
+
local_path: cwd,
|
|
1139
|
+
git_remote: gitRemote,
|
|
1140
|
+
project_name: projectName,
|
|
1141
|
+
}),
|
|
1142
|
+
});
|
|
1143
|
+
if (response.ok) {
|
|
1144
|
+
const data = await response.json();
|
|
1145
|
+
projectId = data.data?.project_id || `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
1146
|
+
const registeredName = data.data?.project_name || projectName;
|
|
1147
|
+
spinner.succeed(`Registered project: ${registeredName} (${projectId})`);
|
|
1148
|
+
// Update local projects.json
|
|
1149
|
+
try {
|
|
1150
|
+
const projectsData = existsSync(PROJECTS_FILE)
|
|
1151
|
+
? JSON.parse(readFileSync(PROJECTS_FILE, 'utf-8'))
|
|
1152
|
+
: { bindings: [], system_id: systemInfo.system_id };
|
|
1153
|
+
// Check if already in bindings
|
|
1154
|
+
const existingIdx = projectsData.bindings.findIndex((b) => b.local_path === cwd);
|
|
1155
|
+
if (existingIdx >= 0) {
|
|
1156
|
+
projectsData.bindings[existingIdx].project_id = projectId;
|
|
1157
|
+
projectsData.bindings[existingIdx].project_name = registeredName;
|
|
1158
|
+
projectsData.bindings[existingIdx].git_remote = gitRemote;
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
projectsData.bindings.push({
|
|
1162
|
+
project_id: projectId,
|
|
1163
|
+
local_path: cwd,
|
|
1164
|
+
project_name: registeredName,
|
|
1165
|
+
git_remote: gitRemote,
|
|
1166
|
+
system_id: systemInfo.system_id,
|
|
1167
|
+
cached_at: new Date().toISOString(),
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
projectsData.last_sync = new Date().toISOString();
|
|
1171
|
+
writeFileSync(PROJECTS_FILE, JSON.stringify(projectsData, null, 2));
|
|
1119
1172
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
project_id: projectId,
|
|
1123
|
-
local_path: cwd,
|
|
1124
|
-
project_name: projectName,
|
|
1125
|
-
cached_at: new Date().toISOString(),
|
|
1126
|
-
});
|
|
1173
|
+
catch {
|
|
1174
|
+
// Non-fatal
|
|
1127
1175
|
}
|
|
1128
|
-
projectsData.last_sync = new Date().toISOString();
|
|
1129
|
-
writeFileSync(PROJECTS_FILE, JSON.stringify(projectsData, null, 2));
|
|
1130
1176
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1177
|
+
else {
|
|
1178
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1179
|
+
spinner.warn(`Could not register with API: ${errorData.error || response.status}`);
|
|
1180
|
+
projectId = `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
1133
1181
|
}
|
|
1134
1182
|
}
|
|
1135
|
-
|
|
1136
|
-
spinner.warn('Could not
|
|
1183
|
+
catch (err) {
|
|
1184
|
+
spinner.warn('Could not reach API, using local ID');
|
|
1137
1185
|
projectId = `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
1138
1186
|
}
|
|
1139
1187
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1188
|
+
else {
|
|
1189
|
+
// No git remote - use local ID
|
|
1190
|
+
spinner.info('No git remote detected, using local project ID');
|
|
1142
1191
|
projectId = `local_${projectName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
1192
|
+
// Still update local projects.json for session context
|
|
1193
|
+
try {
|
|
1194
|
+
const projectsData = existsSync(PROJECTS_FILE)
|
|
1195
|
+
? JSON.parse(readFileSync(PROJECTS_FILE, 'utf-8'))
|
|
1196
|
+
: { bindings: [], system_id: systemInfo.system_id };
|
|
1197
|
+
const existingIdx = projectsData.bindings.findIndex((b) => b.local_path === cwd);
|
|
1198
|
+
if (existingIdx >= 0) {
|
|
1199
|
+
projectsData.bindings[existingIdx].project_id = projectId;
|
|
1200
|
+
projectsData.bindings[existingIdx].project_name = projectName;
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
projectsData.bindings.push({
|
|
1204
|
+
project_id: projectId,
|
|
1205
|
+
local_path: cwd,
|
|
1206
|
+
project_name: projectName,
|
|
1207
|
+
system_id: systemInfo.system_id,
|
|
1208
|
+
cached_at: new Date().toISOString(),
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
projectsData.last_sync = new Date().toISOString();
|
|
1212
|
+
writeFileSync(PROJECTS_FILE, JSON.stringify(projectsData, null, 2));
|
|
1213
|
+
}
|
|
1214
|
+
catch {
|
|
1215
|
+
// Non-fatal
|
|
1216
|
+
}
|
|
1143
1217
|
}
|
|
1144
1218
|
// Create .claude directory if needed
|
|
1145
1219
|
if (!existsSync(projectClaudeDir)) {
|