@colin4k1024/tsp 2.4.7 → 2.4.9
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 +4 -4
- package/hooks/hooks.json +11 -0
- package/manifests/install-modules.json +5 -2
- package/package.json +1 -2
- package/schemas/install-modules.schema.json +52 -0
- package/scripts/codegraph-preflight.js +68 -75
- package/scripts/hooks/codegraph-auto-init.js +324 -0
- package/scripts/install-apply.js +2 -1
- package/scripts/install-codegraph.js +235 -25
- package/scripts/install-plan.js +4 -1
- package/scripts/lib/install/apply.js +5 -0
- package/scripts/lib/install-executor.js +6 -0
- package/skills/codegraph/SKILL.md +6 -5
- package/skills/goframe-v2/examples/practices/quick-demo/manifest/config/config.yaml +14 -14
- package/skills/repo-scan/SKILL.md +63 -63
- package/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_platform_artifacts.cpython-311.pyc +0 -0
- package/scripts/__pycache__/install_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/langfuse_trace.cpython-311.pyc +0 -0
- package/scripts/__pycache__/query_audit_logs.cpython-311.pyc +0 -0
- package/scripts/__pycache__/scan_leaked_keys.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-313.pyc +0 -0
- package/scripts/__pycache__/validate_library.cpython-311.pyc +0 -0
- package/scripts/__pycache__/validate_workflow_state.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/store.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/mcp_health_check.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/observe.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_end.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_logger.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_query.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/hook_contract.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/memory_store.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/utils.cpython-311.pyc +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
const { resolveCodeGraphBin } = require('../install-codegraph');
|
|
9
|
+
|
|
10
|
+
const STATE_FILE = path.join('.codegraph', 'tsp-auto-init-state.json');
|
|
11
|
+
const DEFAULT_RETRY_MS = 60 * 60 * 1000;
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
13
|
+
const PROJECT_MARKERS = Object.freeze([
|
|
14
|
+
'package.json',
|
|
15
|
+
'pyproject.toml',
|
|
16
|
+
'Cargo.toml',
|
|
17
|
+
'go.mod',
|
|
18
|
+
'pom.xml',
|
|
19
|
+
'build.gradle',
|
|
20
|
+
'build.gradle.kts',
|
|
21
|
+
'Gemfile',
|
|
22
|
+
'composer.json',
|
|
23
|
+
'mix.exs',
|
|
24
|
+
'deno.json',
|
|
25
|
+
'deno.jsonc',
|
|
26
|
+
'README.md',
|
|
27
|
+
'AGENTS.md',
|
|
28
|
+
'CLAUDE.md',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
function isDisabled() {
|
|
32
|
+
const value = String(process.env.TSP_CODEGRAPH_AUTO_INIT || '').trim().toLowerCase();
|
|
33
|
+
return value === '0' || value === 'false' || value === 'off';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readHookInput(rawInput) {
|
|
37
|
+
if (!rawInput || !String(rawInput).trim()) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(rawInput);
|
|
43
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
44
|
+
} catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizePath(candidate) {
|
|
50
|
+
try {
|
|
51
|
+
return fs.realpathSync(candidate);
|
|
52
|
+
} catch {
|
|
53
|
+
return path.resolve(candidate);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getStartDir(rawInput) {
|
|
58
|
+
if (process.env.CODEGRAPH_AUTO_INIT_PROJECT_ROOT) {
|
|
59
|
+
return path.resolve(process.env.CODEGRAPH_AUTO_INIT_PROJECT_ROOT);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const input = readHookInput(rawInput);
|
|
63
|
+
const candidates = [
|
|
64
|
+
input.cwd,
|
|
65
|
+
input.projectDir,
|
|
66
|
+
input.project_dir,
|
|
67
|
+
input.projectPath,
|
|
68
|
+
input.project_path,
|
|
69
|
+
input.workspaceDir,
|
|
70
|
+
input.workspace_dir,
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
for (const candidate of candidates) {
|
|
74
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
75
|
+
return path.resolve(candidate.trim());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return process.cwd();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseGitDirFile(filePath) {
|
|
83
|
+
try {
|
|
84
|
+
const content = fs.readFileSync(filePath, 'utf8').trim();
|
|
85
|
+
const match = content.match(/^gitdir:\s*(.+)$/i);
|
|
86
|
+
if (!match) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const gitDir = match[1].trim();
|
|
90
|
+
return path.resolve(path.dirname(filePath), gitDir);
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function findGitRoot(startDir) {
|
|
97
|
+
let current = normalizePath(startDir);
|
|
98
|
+
const root = path.parse(current).root;
|
|
99
|
+
|
|
100
|
+
while (current && current !== root) {
|
|
101
|
+
const gitPath = path.join(current, '.git');
|
|
102
|
+
if (fs.existsSync(gitPath)) {
|
|
103
|
+
return current;
|
|
104
|
+
}
|
|
105
|
+
current = path.dirname(current);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const rootGitPath = path.join(root, '.git');
|
|
109
|
+
return fs.existsSync(rootGitPath) ? root : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function findMarkerRoot(startDir) {
|
|
113
|
+
let current = normalizePath(startDir);
|
|
114
|
+
const root = path.parse(current).root;
|
|
115
|
+
const home = normalizePath(os.homedir());
|
|
116
|
+
|
|
117
|
+
while (current && current !== root) {
|
|
118
|
+
if (current === home) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (PROJECT_MARKERS.some(marker => fs.existsSync(path.join(current, marker)))) {
|
|
122
|
+
return current;
|
|
123
|
+
}
|
|
124
|
+
current = path.dirname(current);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveProjectRoot(rawInput) {
|
|
131
|
+
const startDir = getStartDir(rawInput);
|
|
132
|
+
if (!fs.existsSync(startDir)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return findGitRoot(startDir) || findMarkerRoot(startDir);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function codegraphDbPath(projectRoot) {
|
|
140
|
+
return path.join(projectRoot, '.codegraph', 'codegraph.db');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function statePath(projectRoot) {
|
|
144
|
+
return path.join(projectRoot, STATE_FILE);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readState(projectRoot) {
|
|
148
|
+
try {
|
|
149
|
+
return JSON.parse(fs.readFileSync(statePath(projectRoot), 'utf8'));
|
|
150
|
+
} catch {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function writeState(projectRoot, state) {
|
|
156
|
+
const filePath = statePath(projectRoot);
|
|
157
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
158
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function retryDelayMs() {
|
|
162
|
+
const parsed = Number(process.env.TSP_CODEGRAPH_AUTO_INIT_RETRY_MS || DEFAULT_RETRY_MS);
|
|
163
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_RETRY_MS;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function shouldThrottleFailure(projectRoot, now = Date.now()) {
|
|
167
|
+
const state = readState(projectRoot);
|
|
168
|
+
if (state.status !== 'failed' || !state.failedAt) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const failedAt = Date.parse(state.failedAt);
|
|
173
|
+
if (!Number.isFinite(failedAt)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return now - failedAt < retryDelayMs();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function resolveGitDir(projectRoot) {
|
|
181
|
+
const gitPath = path.join(projectRoot, '.git');
|
|
182
|
+
try {
|
|
183
|
+
const stat = fs.statSync(gitPath);
|
|
184
|
+
if (stat.isDirectory()) {
|
|
185
|
+
return gitPath;
|
|
186
|
+
}
|
|
187
|
+
if (stat.isFile()) {
|
|
188
|
+
return parseGitDirFile(gitPath);
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function ensureGitExclude(projectRoot) {
|
|
198
|
+
const gitDir = resolveGitDir(projectRoot);
|
|
199
|
+
if (!gitDir) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const excludePath = path.join(gitDir, 'info', 'exclude');
|
|
204
|
+
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
|
205
|
+
const existing = fs.existsSync(excludePath) ? fs.readFileSync(excludePath, 'utf8') : '';
|
|
206
|
+
const lines = existing.split(/\r?\n/).map(line => line.trim());
|
|
207
|
+
if (lines.includes('.codegraph/')) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const prefix = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
|
|
212
|
+
fs.appendFileSync(excludePath, `${prefix}.codegraph/\n`, 'utf8');
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function timeoutMs() {
|
|
217
|
+
const parsed = Number(process.env.TSP_CODEGRAPH_AUTO_INIT_TIMEOUT_MS || DEFAULT_TIMEOUT_MS);
|
|
218
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function truncate(text, maxLength = 2000) {
|
|
222
|
+
const value = String(text || '');
|
|
223
|
+
return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function initializeProject(projectRoot) {
|
|
227
|
+
const resolved = resolveCodeGraphBin({ ignoreForceStandalone: true });
|
|
228
|
+
if (!resolved) {
|
|
229
|
+
throw new Error('CodeGraph binary not found. Install it with the official standalone installer or set CODEGRAPH_INSTALL_BIN.');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const result = spawnSync(resolved.command, [...resolved.argsPrefix, 'init', '-i', projectRoot], {
|
|
233
|
+
cwd: projectRoot,
|
|
234
|
+
env: process.env,
|
|
235
|
+
encoding: 'utf8',
|
|
236
|
+
timeout: timeoutMs(),
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (result.error) {
|
|
240
|
+
throw result.error;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (result.status !== 0) {
|
|
244
|
+
throw new Error(truncate(`${result.stdout || ''}${result.stderr || ''}`.trim() || `codegraph exited with status ${result.status}`));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
binary: resolved.displayCommand,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function runAutoInit(rawInput = '') {
|
|
253
|
+
if (isDisabled()) {
|
|
254
|
+
return { status: 'skipped', reason: 'disabled' };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const projectRoot = resolveProjectRoot(rawInput);
|
|
258
|
+
if (!projectRoot) {
|
|
259
|
+
return { status: 'skipped', reason: 'no-project-root' };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (fs.existsSync(codegraphDbPath(projectRoot))) {
|
|
263
|
+
return { status: 'skipped', reason: 'already-initialized', projectRoot };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (shouldThrottleFailure(projectRoot)) {
|
|
267
|
+
return { status: 'skipped', reason: 'recent-failure', projectRoot };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
ensureGitExclude(projectRoot);
|
|
272
|
+
const initialized = initializeProject(projectRoot);
|
|
273
|
+
writeState(projectRoot, {
|
|
274
|
+
status: 'initialized',
|
|
275
|
+
projectRoot,
|
|
276
|
+
initializedAt: new Date().toISOString(),
|
|
277
|
+
binary: initialized.binary,
|
|
278
|
+
});
|
|
279
|
+
return { status: 'initialized', projectRoot };
|
|
280
|
+
} catch (error) {
|
|
281
|
+
writeState(projectRoot, {
|
|
282
|
+
status: 'failed',
|
|
283
|
+
projectRoot,
|
|
284
|
+
failedAt: new Date().toISOString(),
|
|
285
|
+
error: truncate(error && error.message ? error.message : error),
|
|
286
|
+
});
|
|
287
|
+
return { status: 'failed', projectRoot, error };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function run(rawInput = '') {
|
|
292
|
+
const result = runAutoInit(rawInput);
|
|
293
|
+
if (result.status === 'failed' && process.env.TSP_CODEGRAPH_AUTO_INIT_VERBOSE === '1') {
|
|
294
|
+
return {
|
|
295
|
+
stderr: `[CodeGraph] auto-init failed for ${result.projectRoot}: ${result.error && result.error.message ? result.error.message : result.error}\n`,
|
|
296
|
+
exitCode: 0,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return { exitCode: 0 };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (require.main === module) {
|
|
303
|
+
let raw = '';
|
|
304
|
+
process.stdin.setEncoding('utf8');
|
|
305
|
+
process.stdin.on('data', chunk => {
|
|
306
|
+
raw += chunk;
|
|
307
|
+
});
|
|
308
|
+
process.stdin.on('end', () => {
|
|
309
|
+
run(raw);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = {
|
|
314
|
+
codegraphDbPath,
|
|
315
|
+
ensureGitExclude,
|
|
316
|
+
findGitRoot,
|
|
317
|
+
findMarkerRoot,
|
|
318
|
+
readState,
|
|
319
|
+
resolveProjectRoot,
|
|
320
|
+
run,
|
|
321
|
+
runAutoInit,
|
|
322
|
+
shouldThrottleFailure,
|
|
323
|
+
statePath,
|
|
324
|
+
};
|
package/scripts/install-apply.js
CHANGED
|
@@ -218,7 +218,8 @@ function printHumanPlan(plan, dryRun, knownRisks = []) {
|
|
|
218
218
|
if (Array.isArray(plan.externalInstalls) && plan.externalInstalls.length > 0) {
|
|
219
219
|
console.log('\nPlanned external installs:');
|
|
220
220
|
for (const externalInstall of plan.externalInstalls) {
|
|
221
|
-
|
|
221
|
+
const mode = externalInstall.failureMode === 'warn' ? ' [warn-on-failure]' : '';
|
|
222
|
+
console.log(`- ${externalInstall.id}${mode}: ${externalInstall.description || externalInstall.script}`);
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const os = require('os')
|
|
4
6
|
const path = require('path')
|
|
5
7
|
const { spawnSync } = require('child_process')
|
|
6
8
|
|
|
9
|
+
const INSTALL_SH_URL = 'https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh'
|
|
10
|
+
const INSTALL_PS1_URL = 'https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1'
|
|
11
|
+
|
|
7
12
|
const SUPPORTED_TARGETS = Object.freeze({
|
|
8
13
|
claude: 'claude',
|
|
9
14
|
codex: 'codex',
|
|
@@ -50,29 +55,144 @@ function parseArgs(argv) {
|
|
|
50
55
|
return parsed
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
function
|
|
58
|
+
function isWindows(platform = process.platform) {
|
|
59
|
+
return platform === 'win32'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function executableNames(name, platform = process.platform) {
|
|
63
|
+
if (!isWindows(platform)) {
|
|
64
|
+
return [name]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const extensions = String(process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
|
68
|
+
.split(';')
|
|
69
|
+
.map(value => value.trim().toLowerCase())
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
const lowerName = name.toLowerCase()
|
|
72
|
+
if (extensions.some(ext => lowerName.endsWith(ext))) {
|
|
73
|
+
return [name]
|
|
74
|
+
}
|
|
75
|
+
return [name, ...extensions.map(ext => `${name}${ext}`)]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function canExecute(filePath) {
|
|
79
|
+
try {
|
|
80
|
+
fs.accessSync(filePath, fs.constants.X_OK)
|
|
81
|
+
return true
|
|
82
|
+
} catch {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function findOnPath(commandName, options = {}) {
|
|
88
|
+
const searchPath = options.pathValue !== undefined ? options.pathValue : process.env.PATH
|
|
89
|
+
const platform = options.platform || process.platform
|
|
90
|
+
const delimiter = isWindows(platform) ? ';' : path.delimiter
|
|
91
|
+
const names = executableNames(commandName, platform)
|
|
92
|
+
|
|
93
|
+
for (const entry of String(searchPath || '').split(delimiter)) {
|
|
94
|
+
if (!entry) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
for (const name of names) {
|
|
98
|
+
const candidate = path.join(entry, name)
|
|
99
|
+
if (canExecute(candidate)) {
|
|
100
|
+
return candidate
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function defaultCodeGraphCandidates(options = {}) {
|
|
109
|
+
const homeDir = options.homeDir || os.homedir()
|
|
110
|
+
const platform = options.platform || process.platform
|
|
111
|
+
|
|
112
|
+
if (isWindows(platform)) {
|
|
113
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local')
|
|
114
|
+
return [
|
|
115
|
+
path.join(localAppData, 'codegraph', 'current', 'bin', 'codegraph.exe'),
|
|
116
|
+
path.join(localAppData, 'codegraph', 'current', 'bin', 'codegraph.cmd'),
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return [
|
|
121
|
+
path.join(homeDir, '.local', 'bin', 'codegraph'),
|
|
122
|
+
path.join(homeDir, '.codegraph', 'current', 'bin', 'codegraph'),
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveCodeGraphBin(options = {}) {
|
|
54
127
|
if (process.env.CODEGRAPH_INSTALL_BIN) {
|
|
55
128
|
return {
|
|
56
129
|
command: process.env.CODEGRAPH_INSTALL_BIN,
|
|
57
130
|
argsPrefix: [],
|
|
58
131
|
displayCommand: process.env.CODEGRAPH_INSTALL_BIN,
|
|
132
|
+
source: 'env',
|
|
59
133
|
}
|
|
60
134
|
}
|
|
61
135
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
136
|
+
if (process.env.CODEGRAPH_INSTALL_FORCE_STANDALONE === '1' && !options.ignoreForceStandalone) {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const pathCandidate = findOnPath('codegraph', options)
|
|
141
|
+
if (pathCandidate) {
|
|
142
|
+
return {
|
|
143
|
+
command: pathCandidate,
|
|
144
|
+
argsPrefix: [],
|
|
145
|
+
displayCommand: pathCandidate,
|
|
146
|
+
source: 'path',
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const candidate of defaultCodeGraphCandidates(options)) {
|
|
151
|
+
if (canExecute(candidate)) {
|
|
152
|
+
return {
|
|
153
|
+
command: candidate,
|
|
154
|
+
argsPrefix: [],
|
|
155
|
+
displayCommand: candidate,
|
|
156
|
+
source: 'default-location',
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function resolvePowerShellCommand() {
|
|
165
|
+
return findOnPath('powershell.exe') || findOnPath('powershell') || findOnPath('pwsh') || 'powershell.exe'
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildStandaloneInstallerInfo(platform = process.platform) {
|
|
169
|
+
if (platform === 'darwin' || platform === 'linux') {
|
|
170
|
+
return {
|
|
171
|
+
supported: true,
|
|
172
|
+
platform,
|
|
173
|
+
display: `curl -fsSL ${INSTALL_SH_URL} -o "$TMPDIR/codegraph-install.sh" && sh "$TMPDIR/codegraph-install.sh"`,
|
|
174
|
+
requiredCommand: 'curl',
|
|
175
|
+
url: INSTALL_SH_URL,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (platform === 'win32') {
|
|
180
|
+
return {
|
|
181
|
+
supported: true,
|
|
182
|
+
platform,
|
|
183
|
+
display: `irm ${INSTALL_PS1_URL} | iex`,
|
|
184
|
+
requiredCommand: resolvePowerShellCommand(),
|
|
185
|
+
url: INSTALL_PS1_URL,
|
|
186
|
+
}
|
|
69
187
|
}
|
|
70
188
|
|
|
71
|
-
const binPath = path.join(path.dirname(packageJsonPath), 'dist', 'bin', 'codegraph.js')
|
|
72
189
|
return {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
190
|
+
supported: false,
|
|
191
|
+
platform,
|
|
192
|
+
display: '',
|
|
193
|
+
requiredCommand: '',
|
|
194
|
+
url: '',
|
|
195
|
+
reason: `CodeGraph standalone installer does not support platform: ${platform}`,
|
|
76
196
|
}
|
|
77
197
|
}
|
|
78
198
|
|
|
@@ -88,27 +208,101 @@ function buildInstallCommand(target) {
|
|
|
88
208
|
|
|
89
209
|
const resolved = resolveCodeGraphBin()
|
|
90
210
|
const args = [
|
|
91
|
-
...resolved.argsPrefix,
|
|
211
|
+
...(resolved ? resolved.argsPrefix : []),
|
|
92
212
|
'install',
|
|
93
213
|
`--target=${mappedTarget}`,
|
|
94
214
|
'--location=global',
|
|
95
215
|
'--yes',
|
|
96
216
|
]
|
|
217
|
+
const displayCommand = resolved ? resolved.displayCommand : 'codegraph'
|
|
97
218
|
|
|
98
219
|
return {
|
|
99
220
|
supported: true,
|
|
100
221
|
target: mappedTarget,
|
|
222
|
+
command: resolved ? resolved.command : null,
|
|
223
|
+
args,
|
|
224
|
+
display: [displayCommand, ...args.slice(resolved ? resolved.argsPrefix.length : 0)].join(' '),
|
|
225
|
+
needsBootstrap: !resolved,
|
|
226
|
+
bootstrap: buildStandaloneInstallerInfo(),
|
|
227
|
+
binarySource: resolved ? resolved.source : null,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function spawnChecked(command, args, options = {}) {
|
|
232
|
+
const result = spawnSync(command, args, {
|
|
233
|
+
cwd: options.cwd || process.cwd(),
|
|
234
|
+
env: options.env || process.env,
|
|
235
|
+
encoding: 'utf8',
|
|
236
|
+
stdio: options.stdio || ['inherit', process.stderr, process.stderr],
|
|
237
|
+
timeout: options.timeout,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
return typeof result.status === 'number' ? result.status : 1
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function runStandaloneInstaller() {
|
|
244
|
+
const installer = buildStandaloneInstallerInfo()
|
|
245
|
+
if (!installer.supported) {
|
|
246
|
+
throw new Error(installer.reason)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (installer.platform === 'darwin' || installer.platform === 'linux') {
|
|
250
|
+
const curl = findOnPath('curl') || 'curl'
|
|
251
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-install-'))
|
|
252
|
+
const scriptPath = path.join(tempDir, 'install.sh')
|
|
253
|
+
try {
|
|
254
|
+
let status = spawnChecked(curl, ['-fsSL', INSTALL_SH_URL, '-o', scriptPath])
|
|
255
|
+
if (status !== 0) {
|
|
256
|
+
return status
|
|
257
|
+
}
|
|
258
|
+
status = spawnChecked('sh', [scriptPath])
|
|
259
|
+
return status
|
|
260
|
+
} finally {
|
|
261
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return spawnChecked(resolvePowerShellCommand(), [
|
|
266
|
+
'-NoProfile',
|
|
267
|
+
'-ExecutionPolicy',
|
|
268
|
+
'Bypass',
|
|
269
|
+
'-Command',
|
|
270
|
+
`irm ${INSTALL_PS1_URL} | iex`,
|
|
271
|
+
])
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildTargetInstallInvocation(target) {
|
|
275
|
+
const mappedTarget = mapTarget(target)
|
|
276
|
+
const resolved = resolveCodeGraphBin({ ignoreForceStandalone: true })
|
|
277
|
+
if (!mappedTarget || !resolved) {
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const args = [
|
|
282
|
+
...resolved.argsPrefix,
|
|
283
|
+
'install',
|
|
284
|
+
`--target=${mappedTarget}`,
|
|
285
|
+
'--location=global',
|
|
286
|
+
'--yes',
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
return {
|
|
101
290
|
command: resolved.command,
|
|
102
291
|
args,
|
|
103
|
-
display: [resolved.displayCommand, ...args.slice(resolved.argsPrefix.length)].join(' '),
|
|
104
292
|
}
|
|
105
293
|
}
|
|
106
294
|
|
|
107
295
|
function printHelp() {
|
|
108
296
|
console.log(`Usage: node scripts/install-codegraph.js [--target <claude|codex|cursor|opencode>] [--dry-run]
|
|
109
297
|
|
|
110
|
-
|
|
111
|
-
|
|
298
|
+
Installs the standalone CodeGraph CLI with the official upstream curl/PowerShell installer when needed,
|
|
299
|
+
then configures CodeGraph for the current TSP install target only.
|
|
300
|
+
|
|
301
|
+
Environment:
|
|
302
|
+
CODEGRAPH_INSTALL_BIN=<path> Use an existing CodeGraph binary, including offline installs.
|
|
303
|
+
CODEGRAPH_INSTALL_FORCE_STANDALONE=1 Re-run the official standalone installer before target config.
|
|
304
|
+
|
|
305
|
+
Project indexes are created by the Claude SessionStart auto-init hook or by running codegraph init -i.
|
|
112
306
|
`)
|
|
113
307
|
}
|
|
114
308
|
|
|
@@ -126,19 +320,31 @@ function run(argv = process.argv.slice(2)) {
|
|
|
126
320
|
}
|
|
127
321
|
|
|
128
322
|
if (options.dryRun) {
|
|
129
|
-
|
|
323
|
+
if (install.needsBootstrap) {
|
|
324
|
+
console.log(`CodeGraph standalone install command: ${install.bootstrap.display}`)
|
|
325
|
+
} else {
|
|
326
|
+
console.log(`CodeGraph binary: ${install.command} (${install.binarySource})`)
|
|
327
|
+
}
|
|
328
|
+
console.log(`CodeGraph target install command: ${install.display}`)
|
|
130
329
|
return 0
|
|
131
330
|
}
|
|
132
331
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
332
|
+
if (install.needsBootstrap) {
|
|
333
|
+
console.error('Installing CodeGraph standalone CLI with the official upstream installer')
|
|
334
|
+
const bootstrapStatus = runStandaloneInstaller()
|
|
335
|
+
if (bootstrapStatus !== 0) {
|
|
336
|
+
return bootstrapStatus
|
|
337
|
+
}
|
|
338
|
+
}
|
|
140
339
|
|
|
141
|
-
|
|
340
|
+
const invocation = buildTargetInstallInvocation(install.target)
|
|
341
|
+
if (!invocation) {
|
|
342
|
+
console.error('CodeGraph binary is still unavailable after standalone install. Add it to PATH or set CODEGRAPH_INSTALL_BIN.')
|
|
343
|
+
return 1
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.error(`Running CodeGraph installer for ${install.target}`)
|
|
347
|
+
return spawnChecked(invocation.command, invocation.args)
|
|
142
348
|
}
|
|
143
349
|
|
|
144
350
|
if (require.main === module) {
|
|
@@ -152,7 +358,11 @@ if (require.main === module) {
|
|
|
152
358
|
|
|
153
359
|
module.exports = {
|
|
154
360
|
buildInstallCommand,
|
|
361
|
+
buildStandaloneInstallerInfo,
|
|
362
|
+
defaultCodeGraphCandidates,
|
|
363
|
+
findOnPath,
|
|
155
364
|
mapTarget,
|
|
156
365
|
parseArgs,
|
|
366
|
+
resolveCodeGraphBin,
|
|
157
367
|
run,
|
|
158
368
|
}
|
package/scripts/install-plan.js
CHANGED
|
@@ -156,6 +156,8 @@ function resolvePlanExternalInstalls(plan) {
|
|
|
156
156
|
args: Array.isArray(externalInstall.args)
|
|
157
157
|
? externalInstall.args.map(value => String(value))
|
|
158
158
|
: [],
|
|
159
|
+
failureMode: externalInstall.failureMode === 'warn' ? 'warn' : 'error',
|
|
160
|
+
failureHint: typeof externalInstall.failureHint === 'string' ? externalInstall.failureHint : '',
|
|
159
161
|
target: plan.target || null,
|
|
160
162
|
profileId: plan.profileId || null,
|
|
161
163
|
}));
|
|
@@ -213,7 +215,8 @@ function printPlan(plan) {
|
|
|
213
215
|
console.log('');
|
|
214
216
|
console.log(`External install plan (${externalInstalls.length}):`);
|
|
215
217
|
for (const externalInstall of externalInstalls) {
|
|
216
|
-
|
|
218
|
+
const mode = externalInstall.failureMode === 'warn' ? ' [warn-on-failure]' : '';
|
|
219
|
+
console.log(`- ${externalInstall.id}${mode}: ${externalInstall.description || externalInstall.script}`);
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
}
|
|
@@ -219,6 +219,11 @@ function runExternalInstall(externalInstall) {
|
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
if (result.status !== 0) {
|
|
222
|
+
if (externalInstall.failureMode === 'warn') {
|
|
223
|
+
console.error(`Warning: optional external install failed: ${label}`);
|
|
224
|
+
console.error(externalInstall.failureHint || 'TSP core install will continue; rerun when the external dependency is reachable.');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
222
227
|
throw new Error(`External install failed: ${label}`);
|
|
223
228
|
}
|
|
224
229
|
}
|