@vohongtho.infotech/code-intel 1.0.1 → 1.0.2
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 +24 -3
- package/dist/cli/hook.js +348 -0
- package/dist/cli/hook.js.map +1 -0
- package/dist/cli/main.js +1039 -46
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +229 -112
- package/dist/index.js.map +1 -1
- package/dist/web/assets/{es-Bu8iwdFw.js → es-DiINqj58.js} +1 -1
- package/dist/web/assets/index-CzWucUxe.js +354 -0
- package/dist/web/assets/index-D3zJQH9-.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -2
- package/dist/web/assets/index-C9M6YLlS.css +0 -2
- package/dist/web/assets/index-CKc3HEpe.js +0 -354
package/dist/cli/main.js
CHANGED
|
@@ -9,7 +9,7 @@ import winston from 'winston';
|
|
|
9
9
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
10
10
|
import fs39, { readFileSync, existsSync } from 'fs';
|
|
11
11
|
import path39, { dirname, join } from 'path';
|
|
12
|
-
import
|
|
12
|
+
import os18 from 'os';
|
|
13
13
|
import { Registry, collectDefaultMetrics, Counter, Histogram, Gauge } from 'prom-client';
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
@@ -38,6 +38,7 @@ import https from 'https';
|
|
|
38
38
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
39
39
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
40
40
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
41
|
+
import process2 from 'process';
|
|
41
42
|
|
|
42
43
|
var __defProp = Object.defineProperty;
|
|
43
44
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -327,13 +328,13 @@ var init_logger = __esm({
|
|
|
327
328
|
};
|
|
328
329
|
}
|
|
329
330
|
/** Global log directory: ~/.code-intel/logs */
|
|
330
|
-
static LOG_DIR = path39.join(
|
|
331
|
+
static LOG_DIR = path39.join(os18.homedir(), ".code-intel", "logs");
|
|
331
332
|
static getLogger() {
|
|
332
333
|
if (!_Logger.instance) {
|
|
333
334
|
const isProduction = process.env.NODE_ENV === "production";
|
|
334
335
|
const logLevel = process.env.LOG_LEVEL ?? "info";
|
|
335
336
|
const transports = [];
|
|
336
|
-
transports.push(new winston.transports.Console());
|
|
337
|
+
transports.push(new winston.transports.Console({ stderrLevels: ["error", "warn", "info", "http", "verbose", "debug", "silly"] }));
|
|
337
338
|
if (!isProduction) {
|
|
338
339
|
try {
|
|
339
340
|
if (!fs39.existsSync(_Logger.LOG_DIR)) {
|
|
@@ -3115,7 +3116,7 @@ var init_llm_governance = __esm({
|
|
|
3115
3116
|
}
|
|
3116
3117
|
/** Path to the JSONL log file. */
|
|
3117
3118
|
getLogPath() {
|
|
3118
|
-
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path39.join(
|
|
3119
|
+
return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path39.join(os18.homedir(), ".code-intel", "llm-governance.jsonl");
|
|
3119
3120
|
}
|
|
3120
3121
|
/**
|
|
3121
3122
|
* Append an entry to the governance log.
|
|
@@ -3276,6 +3277,49 @@ var init_anthropic = __esm({
|
|
|
3276
3277
|
}
|
|
3277
3278
|
});
|
|
3278
3279
|
|
|
3280
|
+
// src/llm/providers/custom.ts
|
|
3281
|
+
var custom_exports = {};
|
|
3282
|
+
__export(custom_exports, {
|
|
3283
|
+
CustomProvider: () => CustomProvider
|
|
3284
|
+
});
|
|
3285
|
+
var CustomProvider;
|
|
3286
|
+
var init_custom = __esm({
|
|
3287
|
+
"src/llm/providers/custom.ts"() {
|
|
3288
|
+
CustomProvider = class {
|
|
3289
|
+
modelName;
|
|
3290
|
+
baseUrl;
|
|
3291
|
+
apiKey;
|
|
3292
|
+
constructor(baseUrl, model, apiKey = "") {
|
|
3293
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
3294
|
+
this.modelName = model;
|
|
3295
|
+
this.apiKey = apiKey;
|
|
3296
|
+
}
|
|
3297
|
+
async summarize(prompt2) {
|
|
3298
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
3299
|
+
const headers = {
|
|
3300
|
+
"Content-Type": "application/json"
|
|
3301
|
+
};
|
|
3302
|
+
if (this.apiKey) {
|
|
3303
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
3304
|
+
}
|
|
3305
|
+
const body = JSON.stringify({
|
|
3306
|
+
model: this.modelName,
|
|
3307
|
+
messages: [{ role: "user", content: prompt2 }],
|
|
3308
|
+
max_tokens: 200,
|
|
3309
|
+
temperature: 0.3
|
|
3310
|
+
});
|
|
3311
|
+
const res = await fetch(url, { method: "POST", headers, body });
|
|
3312
|
+
if (!res.ok) {
|
|
3313
|
+
const text = await res.text().catch(() => res.statusText);
|
|
3314
|
+
throw new Error(`Custom LLM API error ${res.status}: ${text}`);
|
|
3315
|
+
}
|
|
3316
|
+
const data = await res.json();
|
|
3317
|
+
return data.choices?.[0]?.message?.content?.trim() ?? "";
|
|
3318
|
+
}
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
});
|
|
3322
|
+
|
|
3279
3323
|
// src/llm/providers/ollama.ts
|
|
3280
3324
|
var ollama_exports = {};
|
|
3281
3325
|
__export(ollama_exports, {
|
|
@@ -3319,7 +3363,7 @@ __export(factory_exports, {
|
|
|
3319
3363
|
createLLMProvider: () => createLLMProvider
|
|
3320
3364
|
});
|
|
3321
3365
|
async function createLLMProvider(config = {}) {
|
|
3322
|
-
const { provider = "ollama", model } = config;
|
|
3366
|
+
const { provider = "ollama", model, baseUrl, apiKey } = config;
|
|
3323
3367
|
switch (provider) {
|
|
3324
3368
|
case "openai": {
|
|
3325
3369
|
const { OpenAIProvider: OpenAIProvider2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
@@ -3329,6 +3373,13 @@ async function createLLMProvider(config = {}) {
|
|
|
3329
3373
|
const { AnthropicProvider: AnthropicProvider2 } = await Promise.resolve().then(() => (init_anthropic(), anthropic_exports));
|
|
3330
3374
|
return new AnthropicProvider2(model);
|
|
3331
3375
|
}
|
|
3376
|
+
case "custom": {
|
|
3377
|
+
const { CustomProvider: CustomProvider2 } = await Promise.resolve().then(() => (init_custom(), custom_exports));
|
|
3378
|
+
const url = baseUrl ?? "http://localhost:1234/v1";
|
|
3379
|
+
const mdl = model ?? "default";
|
|
3380
|
+
const key = apiKey ?? "";
|
|
3381
|
+
return new CustomProvider2(url, mdl, key);
|
|
3382
|
+
}
|
|
3332
3383
|
case "ollama":
|
|
3333
3384
|
default: {
|
|
3334
3385
|
const { OllamaProvider: OllamaProvider2 } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
@@ -3482,7 +3533,7 @@ var init_worker_pool = __esm({
|
|
|
3482
3533
|
constructor(opts) {
|
|
3483
3534
|
super();
|
|
3484
3535
|
this.workerScript = opts.workerScript;
|
|
3485
|
-
this.workerCount = opts.workerCount ?? Math.max(1,
|
|
3536
|
+
this.workerCount = opts.workerCount ?? Math.max(1, os18.cpus().length - 1);
|
|
3486
3537
|
this.maxQueueSize = opts.maxQueueSize ?? 200;
|
|
3487
3538
|
this.maxTaskRetries = opts.maxTaskRetries ?? 2;
|
|
3488
3539
|
}
|
|
@@ -3610,7 +3661,7 @@ var init_parse_phase_parallel = __esm({
|
|
|
3610
3661
|
if (!context2.fileCache) context2.fileCache = /* @__PURE__ */ new Map();
|
|
3611
3662
|
if (!context2.fileFunctionIndex) context2.fileFunctionIndex = /* @__PURE__ */ new Map();
|
|
3612
3663
|
const filePaths = context2.filePaths;
|
|
3613
|
-
const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1,
|
|
3664
|
+
const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1, os18.cpus().length - 1);
|
|
3614
3665
|
const CONCURRENCY = 64;
|
|
3615
3666
|
for (let i = 0; i < filePaths.length; i += CONCURRENCY) {
|
|
3616
3667
|
const batch = filePaths.slice(i, i + CONCURRENCY);
|
|
@@ -3739,7 +3790,7 @@ var init_resolve_phase_parallel = __esm({
|
|
|
3739
3790
|
const snapshot = { symbolIndex, fileSymbolIndex, fileIndex, workspaceRoot };
|
|
3740
3791
|
const workerScript = workerScriptPath2();
|
|
3741
3792
|
const workerScriptExists = fs39.existsSync(workerScript);
|
|
3742
|
-
const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1,
|
|
3793
|
+
const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1, os18.cpus().length - 1);
|
|
3743
3794
|
if (!workerScriptExists || workerCount === 1) {
|
|
3744
3795
|
logger_default.info(`[resolve-parallel] falling back to sequential`);
|
|
3745
3796
|
const { resolvePhase: resolvePhase2 } = await Promise.resolve().then(() => (init_resolve_phase(), resolve_phase_exports));
|
|
@@ -4531,7 +4582,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
4531
4582
|
} catch {
|
|
4532
4583
|
}
|
|
4533
4584
|
}
|
|
4534
|
-
const tmpDir = fs39.mkdtempSync(path39.join(
|
|
4585
|
+
const tmpDir = fs39.mkdtempSync(path39.join(os18.tmpdir(), "code-intel-csv-"));
|
|
4535
4586
|
try {
|
|
4536
4587
|
const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
|
|
4537
4588
|
const edgeGroups = writeEdgeCSV(graph, tmpDir);
|
|
@@ -4717,7 +4768,7 @@ function removeRepo(repoPath) {
|
|
|
4717
4768
|
var GLOBAL_DIR, REPOS_FILE;
|
|
4718
4769
|
var init_repo_registry = __esm({
|
|
4719
4770
|
"src/storage/repo-registry.ts"() {
|
|
4720
|
-
GLOBAL_DIR = path39.join(
|
|
4771
|
+
GLOBAL_DIR = path39.join(os18.homedir(), ".code-intel");
|
|
4721
4772
|
REPOS_FILE = path39.join(GLOBAL_DIR, "repos.json");
|
|
4722
4773
|
}
|
|
4723
4774
|
});
|
|
@@ -4888,7 +4939,7 @@ function loadSyncResult(groupName) {
|
|
|
4888
4939
|
var GROUPS_DIR;
|
|
4889
4940
|
var init_group_registry = __esm({
|
|
4890
4941
|
"src/multi-repo/group-registry.ts"() {
|
|
4891
|
-
GROUPS_DIR = path39.join(
|
|
4942
|
+
GROUPS_DIR = path39.join(os18.homedir(), ".code-intel", "groups");
|
|
4892
4943
|
}
|
|
4893
4944
|
});
|
|
4894
4945
|
|
|
@@ -5447,14 +5498,14 @@ function runPrerequisiteChecks() {
|
|
|
5447
5498
|
});
|
|
5448
5499
|
}
|
|
5449
5500
|
try {
|
|
5450
|
-
const out = execSync(`df -BM "${
|
|
5501
|
+
const out = execSync(`df -BM "${os18.homedir()}" 2>/dev/null | tail -1 | awk '{print $4}'`, { encoding: "utf8" });
|
|
5451
5502
|
const availMB = parseInt(out.trim().replace("M", ""), 10);
|
|
5452
5503
|
if (Number.isFinite(availMB) && availMB < 500) {
|
|
5453
5504
|
results.push({
|
|
5454
5505
|
name: "Disk space",
|
|
5455
5506
|
ok: false,
|
|
5456
5507
|
level: "warn",
|
|
5457
|
-
message: `Low disk space: ${availMB} MB available in ${
|
|
5508
|
+
message: `Low disk space: ${availMB} MB available in ${os18.homedir()} (500 MB recommended)`
|
|
5458
5509
|
});
|
|
5459
5510
|
}
|
|
5460
5511
|
} catch {
|
|
@@ -5475,6 +5526,7 @@ var init_codes = __esm({
|
|
|
5475
5526
|
RATE_LIMIT_EXCEEDED: "CI-1100",
|
|
5476
5527
|
PAYLOAD_TOO_LARGE: "CI-1101",
|
|
5477
5528
|
INVALID_REQUEST: "CI-1200",
|
|
5529
|
+
CONFLICT: "CI-1409",
|
|
5478
5530
|
// Config (CI-2xxx)
|
|
5479
5531
|
CONFIG_INVALID: "CI-2000",
|
|
5480
5532
|
CONFIG_NOT_FOUND: "CI-2001",
|
|
@@ -5544,7 +5596,7 @@ var init_fs_secure = __esm({
|
|
|
5544
5596
|
}
|
|
5545
5597
|
});
|
|
5546
5598
|
function getUsersDBPath() {
|
|
5547
|
-
return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path39.join(
|
|
5599
|
+
return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path39.join(os18.homedir(), ".code-intel", "users.db");
|
|
5548
5600
|
}
|
|
5549
5601
|
function getOrCreateUsersDB() {
|
|
5550
5602
|
if (!_usersDB) {
|
|
@@ -5837,17 +5889,17 @@ function getScryptN() {
|
|
|
5837
5889
|
return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
|
|
5838
5890
|
}
|
|
5839
5891
|
function getSecretsPath() {
|
|
5840
|
-
return process.env["CODE_INTEL_SECRETS_PATH"] ?? path39.join(
|
|
5892
|
+
return process.env["CODE_INTEL_SECRETS_PATH"] ?? path39.join(os18.homedir(), ".code-intel", ".secrets");
|
|
5841
5893
|
}
|
|
5842
5894
|
function getMasterPassword() {
|
|
5843
5895
|
const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
|
|
5844
5896
|
if (fromEnv && fromEnv.length >= 16) return fromEnv;
|
|
5845
5897
|
let username = "unknown";
|
|
5846
5898
|
try {
|
|
5847
|
-
username =
|
|
5899
|
+
username = os18.userInfo().username;
|
|
5848
5900
|
} catch {
|
|
5849
5901
|
}
|
|
5850
|
-
return `code-intel-machine:${
|
|
5902
|
+
return `code-intel-machine:${os18.hostname()}:${username}:${os18.platform()}`;
|
|
5851
5903
|
}
|
|
5852
5904
|
function deriveKey(password, salt) {
|
|
5853
5905
|
return crypto5.scryptSync(password, salt, KEY_LEN, { N: getScryptN(), r: 8, p: 1 });
|
|
@@ -8313,6 +8365,7 @@ async function runInitWizard(opts = {}) {
|
|
|
8313
8365
|
{ label: "Ollama (local, free, requires Ollama running)", value: "ollama" },
|
|
8314
8366
|
{ label: "OpenAI (requires OPENAI_API_KEY env var)", value: "openai" },
|
|
8315
8367
|
{ label: "Anthropic (requires ANTHROPIC_API_KEY env var)", value: "anthropic" },
|
|
8368
|
+
{ label: "Custom (OpenAI-compatible API \u2014 enter URL, token & model)", value: "custom" },
|
|
8316
8369
|
{ label: "Skip (configure later)", value: "none" }
|
|
8317
8370
|
], 0);
|
|
8318
8371
|
cfg.llm.provider = llmProvider;
|
|
@@ -8328,6 +8381,19 @@ async function runInitWizard(opts = {}) {
|
|
|
8328
8381
|
cfg.llm.model = "llama3";
|
|
8329
8382
|
cfg.llm.apiKey = "";
|
|
8330
8383
|
console.log(" Make sure Ollama is running: https://ollama.com");
|
|
8384
|
+
} else if (llmProvider === "custom") {
|
|
8385
|
+
console.log(" Configure your OpenAI-compatible provider (e.g. LM Studio, vLLM, Together, Groq, Azure).\n");
|
|
8386
|
+
const baseUrl = (await prompt(rl, " API Base URL (e.g. http://localhost:1234/v1): ")).trim();
|
|
8387
|
+
cfg.llm.baseUrl = baseUrl || "http://localhost:1234/v1";
|
|
8388
|
+
const apiKey = (await prompt(rl, " API Token/Key (leave blank if not required): ")).trim();
|
|
8389
|
+
cfg.llm.apiKey = apiKey || "";
|
|
8390
|
+
const model = (await prompt(rl, " Model name (e.g. mistral-7b-instruct): ")).trim();
|
|
8391
|
+
cfg.llm.model = model || "default";
|
|
8392
|
+
console.log(`
|
|
8393
|
+
\u2705 Custom provider configured:`);
|
|
8394
|
+
console.log(` URL: ${cfg.llm.baseUrl}`);
|
|
8395
|
+
console.log(` Model: ${cfg.llm.model}`);
|
|
8396
|
+
console.log(` Token: ${cfg.llm.apiKey ? "(set)" : "(none)"}`);
|
|
8331
8397
|
} else {
|
|
8332
8398
|
cfg.llm.apiKey = "";
|
|
8333
8399
|
console.log(" Skipped. Run `code-intel config set llm.provider openai` later.");
|
|
@@ -8392,7 +8458,7 @@ function printNextSteps() {
|
|
|
8392
8458
|
var GLOBAL_DIR2, CONFIG_PATH, DEFAULT_CONFIG, EDITORS;
|
|
8393
8459
|
var init_init_wizard = __esm({
|
|
8394
8460
|
"src/cli/init-wizard.ts"() {
|
|
8395
|
-
GLOBAL_DIR2 = path39.join(
|
|
8461
|
+
GLOBAL_DIR2 = path39.join(os18.homedir(), ".code-intel");
|
|
8396
8462
|
CONFIG_PATH = path39.join(GLOBAL_DIR2, "config.json");
|
|
8397
8463
|
DEFAULT_CONFIG = {
|
|
8398
8464
|
$schema: "https://code-intel.dev/config-schema.json",
|
|
@@ -8705,9 +8771,10 @@ var init_config_manager = __esm({
|
|
|
8705
8771
|
/access[_-]?token/i
|
|
8706
8772
|
];
|
|
8707
8773
|
CONFIG_SCHEMA = {
|
|
8708
|
-
"llm.provider": { type: "string", enum: ["openai", "anthropic", "ollama", "none"], default: "ollama", description: "LLM provider for AI summaries" },
|
|
8774
|
+
"llm.provider": { type: "string", enum: ["openai", "anthropic", "ollama", "custom", "none"], default: "ollama", description: "LLM provider for AI summaries" },
|
|
8709
8775
|
"llm.model": { type: "string", default: "llama3", description: "LLM model name" },
|
|
8710
8776
|
"llm.apiKey": { type: "string", default: "", description: "API key \u2014 use $ENV_VAR syntax, e.g. $OPENAI_API_KEY" },
|
|
8777
|
+
"llm.baseUrl": { type: "string", default: "", description: "Base URL for custom OpenAI-compatible API (e.g. http://localhost:1234/v1)" },
|
|
8711
8778
|
"llm.batchSize": { type: "number", minimum: 1, maximum: 100, default: 20, description: "Concurrent LLM calls per batch" },
|
|
8712
8779
|
"llm.maxTokensPerSummary": { type: "number", minimum: 10, maximum: 2e3, default: 100, description: "Max tokens per AI summary" },
|
|
8713
8780
|
"embeddings.model": { type: "string", default: "all-MiniLM-L6-v2", description: "Embedding model name" },
|
|
@@ -10394,7 +10461,7 @@ var JobsDB = class {
|
|
|
10394
10461
|
}
|
|
10395
10462
|
};
|
|
10396
10463
|
function getJobsDBPath() {
|
|
10397
|
-
return path39.join(
|
|
10464
|
+
return path39.join(os18.homedir(), ".code-intel", "jobs.db");
|
|
10398
10465
|
}
|
|
10399
10466
|
var _jobsDB = null;
|
|
10400
10467
|
function getOrCreateJobsDB() {
|
|
@@ -10489,14 +10556,14 @@ var BACKUP_VERSION = "1.0";
|
|
|
10489
10556
|
var ALGORITHM = "aes-256-gcm";
|
|
10490
10557
|
var IV_LENGTH = 16;
|
|
10491
10558
|
function getBackupDir() {
|
|
10492
|
-
return path39.join(
|
|
10559
|
+
return path39.join(os18.homedir(), ".code-intel", "backups");
|
|
10493
10560
|
}
|
|
10494
10561
|
function getBackupKey() {
|
|
10495
10562
|
const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
|
|
10496
10563
|
if (keyHex && keyHex.length >= 64) {
|
|
10497
10564
|
return Buffer.from(keyHex.slice(0, 64), "hex");
|
|
10498
10565
|
}
|
|
10499
|
-
const seed = `code-intel-backup-${
|
|
10566
|
+
const seed = `code-intel-backup-${os18.hostname()}-${os18.homedir()}`;
|
|
10500
10567
|
return crypto5.createHash("sha256").update(seed).digest();
|
|
10501
10568
|
}
|
|
10502
10569
|
function encryptBuffer(data, key) {
|
|
@@ -10538,11 +10605,11 @@ var BackupService = class {
|
|
|
10538
10605
|
filesToBackup.push({ name: f, localPath: fp });
|
|
10539
10606
|
}
|
|
10540
10607
|
}
|
|
10541
|
-
const registryPath = path39.join(
|
|
10608
|
+
const registryPath = path39.join(os18.homedir(), ".code-intel", "registry.json");
|
|
10542
10609
|
if (fs39.existsSync(registryPath)) {
|
|
10543
10610
|
filesToBackup.push({ name: "registry.json", localPath: registryPath });
|
|
10544
10611
|
}
|
|
10545
|
-
const usersDbPath = path39.join(
|
|
10612
|
+
const usersDbPath = path39.join(os18.homedir(), ".code-intel", "users.db");
|
|
10546
10613
|
if (fs39.existsSync(usersDbPath)) {
|
|
10547
10614
|
filesToBackup.push({ name: "users.db", localPath: usersDbPath });
|
|
10548
10615
|
}
|
|
@@ -10708,7 +10775,7 @@ var BackupService = class {
|
|
|
10708
10775
|
}
|
|
10709
10776
|
let destPath;
|
|
10710
10777
|
if (name === "registry.json" || name === "users.db") {
|
|
10711
|
-
destPath = path39.join(
|
|
10778
|
+
destPath = path39.join(os18.homedir(), ".code-intel", name);
|
|
10712
10779
|
} else {
|
|
10713
10780
|
destPath = path39.join(codeIntelDir, name);
|
|
10714
10781
|
}
|
|
@@ -12387,6 +12454,97 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12387
12454
|
}
|
|
12388
12455
|
res.json(result);
|
|
12389
12456
|
});
|
|
12457
|
+
app.post("/api/v1/groups", requireAuth, requireRole("analyst"), (req, res) => {
|
|
12458
|
+
const { name } = req.body;
|
|
12459
|
+
if (!name || !name.trim()) {
|
|
12460
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: "Group name is required" } });
|
|
12461
|
+
return;
|
|
12462
|
+
}
|
|
12463
|
+
const trimmed = name.trim();
|
|
12464
|
+
if (groupExists(trimmed)) {
|
|
12465
|
+
res.status(409).json({ error: { code: ErrorCodes.CONFLICT, message: `Group "${trimmed}" already exists` } });
|
|
12466
|
+
return;
|
|
12467
|
+
}
|
|
12468
|
+
const group = { name: trimmed, createdAt: (/* @__PURE__ */ new Date()).toISOString(), members: [] };
|
|
12469
|
+
saveGroup(group);
|
|
12470
|
+
res.status(201).json(group);
|
|
12471
|
+
});
|
|
12472
|
+
app.delete("/api/v1/groups/:name", requireAuth, requireRole("analyst"), (req, res) => {
|
|
12473
|
+
const groupName = req.params["name"];
|
|
12474
|
+
if (!groupExists(groupName)) {
|
|
12475
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Group not found" } });
|
|
12476
|
+
return;
|
|
12477
|
+
}
|
|
12478
|
+
deleteGroup(groupName);
|
|
12479
|
+
res.status(204).end();
|
|
12480
|
+
});
|
|
12481
|
+
app.patch("/api/v1/groups/:name", requireAuth, requireRole("analyst"), (req, res) => {
|
|
12482
|
+
const groupName = req.params["name"];
|
|
12483
|
+
const group = loadGroup(groupName);
|
|
12484
|
+
if (!group) {
|
|
12485
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Group not found" } });
|
|
12486
|
+
return;
|
|
12487
|
+
}
|
|
12488
|
+
const { name } = req.body;
|
|
12489
|
+
if (!name || !name.trim()) {
|
|
12490
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: "New name is required" } });
|
|
12491
|
+
return;
|
|
12492
|
+
}
|
|
12493
|
+
const newName = name.trim();
|
|
12494
|
+
if (newName !== group.name && groupExists(newName)) {
|
|
12495
|
+
res.status(409).json({ error: { code: ErrorCodes.CONFLICT, message: `Group "${newName}" already exists` } });
|
|
12496
|
+
return;
|
|
12497
|
+
}
|
|
12498
|
+
if (newName !== group.name) {
|
|
12499
|
+
deleteGroup(group.name);
|
|
12500
|
+
group.name = newName;
|
|
12501
|
+
}
|
|
12502
|
+
saveGroup(group);
|
|
12503
|
+
res.json(group);
|
|
12504
|
+
});
|
|
12505
|
+
app.post("/api/v1/groups/:name/members", requireAuth, requireRole("analyst"), (req, res) => {
|
|
12506
|
+
const groupName = req.params["name"];
|
|
12507
|
+
const group = loadGroup(groupName);
|
|
12508
|
+
if (!group) {
|
|
12509
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Group not found" } });
|
|
12510
|
+
return;
|
|
12511
|
+
}
|
|
12512
|
+
const { groupPath, registryName } = req.body;
|
|
12513
|
+
if (!groupPath || !registryName) {
|
|
12514
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: "groupPath and registryName are required" } });
|
|
12515
|
+
return;
|
|
12516
|
+
}
|
|
12517
|
+
const registry = loadRegistry();
|
|
12518
|
+
if (!registry.find((r) => r.name === registryName)) {
|
|
12519
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: `Repo "${registryName}" not found in registry. Run code-intel analyze first.` } });
|
|
12520
|
+
return;
|
|
12521
|
+
}
|
|
12522
|
+
try {
|
|
12523
|
+
const updated = addMember(groupName, { groupPath, registryName });
|
|
12524
|
+
res.json(updated);
|
|
12525
|
+
} catch (err) {
|
|
12526
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: err instanceof Error ? err.message : String(err) } });
|
|
12527
|
+
}
|
|
12528
|
+
});
|
|
12529
|
+
app.delete("/api/v1/groups/:name/members", requireAuth, requireRole("analyst"), (req, res) => {
|
|
12530
|
+
const groupName = req.params["name"];
|
|
12531
|
+
const group = loadGroup(groupName);
|
|
12532
|
+
if (!group) {
|
|
12533
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "Group not found" } });
|
|
12534
|
+
return;
|
|
12535
|
+
}
|
|
12536
|
+
const { groupPath } = req.body;
|
|
12537
|
+
if (!groupPath) {
|
|
12538
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: "groupPath is required" } });
|
|
12539
|
+
return;
|
|
12540
|
+
}
|
|
12541
|
+
try {
|
|
12542
|
+
const updated = removeMember(groupName, groupPath);
|
|
12543
|
+
res.json(updated);
|
|
12544
|
+
} catch (err) {
|
|
12545
|
+
res.status(400).json({ error: { code: ErrorCodes.INVALID_REQUEST, message: err instanceof Error ? err.message : String(err) } });
|
|
12546
|
+
}
|
|
12547
|
+
});
|
|
12390
12548
|
app.post("/api/v1/groups/:name/sync", async (req, res) => {
|
|
12391
12549
|
const group = loadGroup(req.params.name);
|
|
12392
12550
|
if (!group) {
|
|
@@ -12397,8 +12555,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12397
12555
|
const result = await syncGroup(group);
|
|
12398
12556
|
saveSyncResult(result);
|
|
12399
12557
|
group.lastSync = result.syncedAt;
|
|
12400
|
-
|
|
12401
|
-
saveGroup2(group);
|
|
12558
|
+
saveGroup(group);
|
|
12402
12559
|
res.json(result);
|
|
12403
12560
|
} catch (err) {
|
|
12404
12561
|
res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err) } });
|
|
@@ -14954,6 +15111,14 @@ function writeContextFiles(workspaceRoot, projectName, stats, skills) {
|
|
|
14954
15111
|
const kiroDir = path39.join(workspaceRoot, ".kiro", "steering");
|
|
14955
15112
|
if (!fs39.existsSync(kiroDir)) fs39.mkdirSync(kiroDir, { recursive: true });
|
|
14956
15113
|
upsertFile(path39.join(kiroDir, "code-intel.md"), block, "code-intel.md");
|
|
15114
|
+
upsertFile(path39.join(workspaceRoot, ".clinerules"), block, ".clinerules");
|
|
15115
|
+
upsertFile(path39.join(workspaceRoot, ".windsurfrules"), block, ".windsurfrules");
|
|
15116
|
+
const kilocodeDir = path39.join(workspaceRoot, ".kilocode", "rules");
|
|
15117
|
+
if (!fs39.existsSync(kilocodeDir)) fs39.mkdirSync(kilocodeDir, { recursive: true });
|
|
15118
|
+
upsertFile(path39.join(kilocodeDir, "code-intel-rules.md"), block, "code-intel-rules.md");
|
|
15119
|
+
const agentsDir = path39.join(workspaceRoot, ".agents", "rules");
|
|
15120
|
+
if (!fs39.existsSync(agentsDir)) fs39.mkdirSync(agentsDir, { recursive: true });
|
|
15121
|
+
upsertFile(path39.join(agentsDir, "code-intel-rules.md"), block, "code-intel-rules.md");
|
|
14957
15122
|
}
|
|
14958
15123
|
function buildBlock(projectName, stats, skills) {
|
|
14959
15124
|
const skillTableRows = skills.map(
|
|
@@ -15451,7 +15616,7 @@ var MigrationRunner = class {
|
|
|
15451
15616
|
try {
|
|
15452
15617
|
const dbFile = this.db.name;
|
|
15453
15618
|
if (!dbFile || !fs39.existsSync(dbFile)) return;
|
|
15454
|
-
const backupDir = path39.join(
|
|
15619
|
+
const backupDir = path39.join(os18.homedir(), ".code-intel", "backups", "pre-migration");
|
|
15455
15620
|
fs39.mkdirSync(backupDir, { recursive: true });
|
|
15456
15621
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
15457
15622
|
const baseName = path39.basename(dbFile, ".db");
|
|
@@ -15607,7 +15772,7 @@ init_tracing();
|
|
|
15607
15772
|
init_init_wizard();
|
|
15608
15773
|
init_config_manager();
|
|
15609
15774
|
init_codes();
|
|
15610
|
-
var GLOBAL_DIR3 = path39.join(
|
|
15775
|
+
var GLOBAL_DIR3 = path39.join(os18.homedir(), ".code-intel");
|
|
15611
15776
|
function loadRepoPaths() {
|
|
15612
15777
|
try {
|
|
15613
15778
|
const data = fs39.readFileSync(path39.join(GLOBAL_DIR3, "repos.json"), "utf-8");
|
|
@@ -15881,7 +16046,7 @@ function autoInstallCompletion() {
|
|
|
15881
16046
|
}
|
|
15882
16047
|
console.log(` Detected shell: ${shell}`);
|
|
15883
16048
|
if (shell === "fish") {
|
|
15884
|
-
const dir = path39.join(
|
|
16049
|
+
const dir = path39.join(os18.homedir(), ".config", "fish", "completions");
|
|
15885
16050
|
const dest = path39.join(dir, "code-intel.fish");
|
|
15886
16051
|
fs39.mkdirSync(dir, { recursive: true });
|
|
15887
16052
|
fs39.writeFileSync(dest, fishCompletion(), "utf-8");
|
|
@@ -15894,7 +16059,7 @@ source <(code-intel completion zsh)
|
|
|
15894
16059
|
` : `
|
|
15895
16060
|
source <(code-intel completion bash)
|
|
15896
16061
|
`;
|
|
15897
|
-
const rcFile = shell === "zsh" ? path39.join(
|
|
16062
|
+
const rcFile = shell === "zsh" ? path39.join(os18.homedir(), ".zshrc") : path39.join(os18.homedir(), ".bashrc");
|
|
15898
16063
|
try {
|
|
15899
16064
|
const existing = fs39.existsSync(rcFile) ? fs39.readFileSync(rcFile, "utf-8") : "";
|
|
15900
16065
|
if (existing.includes("code-intel completion")) {
|
|
@@ -15921,7 +16086,7 @@ function generateCompletion(shell) {
|
|
|
15921
16086
|
return fishCompletion();
|
|
15922
16087
|
}
|
|
15923
16088
|
}
|
|
15924
|
-
var GLOBAL_DIR4 = path39.join(
|
|
16089
|
+
var GLOBAL_DIR4 = path39.join(os18.homedir(), ".code-intel");
|
|
15925
16090
|
var META_PATH = path39.join(GLOBAL_DIR4, "update-meta.json");
|
|
15926
16091
|
var PACKAGE_NAME = "code-intel";
|
|
15927
16092
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
@@ -16047,14 +16212,170 @@ async function runUpdate(opts = {}) {
|
|
|
16047
16212
|
process.exit(1);
|
|
16048
16213
|
}
|
|
16049
16214
|
}
|
|
16215
|
+
var SOURCE_EXT = /* @__PURE__ */ new Set([
|
|
16216
|
+
"ts",
|
|
16217
|
+
"tsx",
|
|
16218
|
+
"js",
|
|
16219
|
+
"jsx",
|
|
16220
|
+
"mjs",
|
|
16221
|
+
"cjs",
|
|
16222
|
+
"py",
|
|
16223
|
+
"pyi",
|
|
16224
|
+
"rs",
|
|
16225
|
+
"go",
|
|
16226
|
+
"java",
|
|
16227
|
+
"kt",
|
|
16228
|
+
"kts",
|
|
16229
|
+
"rb",
|
|
16230
|
+
"cs",
|
|
16231
|
+
"cpp",
|
|
16232
|
+
"cc",
|
|
16233
|
+
"cxx",
|
|
16234
|
+
"c",
|
|
16235
|
+
"h",
|
|
16236
|
+
"hpp",
|
|
16237
|
+
"swift",
|
|
16238
|
+
"scala",
|
|
16239
|
+
"php"
|
|
16240
|
+
]);
|
|
16241
|
+
var REGEX_META_RE = /[.*+?^${}()|[\]\\]/;
|
|
16242
|
+
var SYMBOL_ID_RE = /^[A-Za-z_$][A-Za-z0-9_$.-]*$/;
|
|
16243
|
+
function isSymbolLike(term) {
|
|
16244
|
+
return SYMBOL_ID_RE.test(term) && !REGEX_META_RE.test(term);
|
|
16245
|
+
}
|
|
16246
|
+
function isSourceFile(filePath) {
|
|
16247
|
+
const dot = filePath.lastIndexOf(".");
|
|
16248
|
+
if (dot === -1) return false;
|
|
16249
|
+
return SOURCE_EXT.has(filePath.slice(dot + 1).toLowerCase());
|
|
16250
|
+
}
|
|
16251
|
+
function fileStem(filePath) {
|
|
16252
|
+
const base = filePath.includes("/") ? filePath.slice(filePath.lastIndexOf("/") + 1) : filePath;
|
|
16253
|
+
const dot = base.lastIndexOf(".");
|
|
16254
|
+
return dot === -1 ? base : base.slice(0, dot);
|
|
16255
|
+
}
|
|
16256
|
+
function extractGrepSymbol(cmd) {
|
|
16257
|
+
if (/(?:^|\s)-[a-zA-Z]*[cvlLoZ]/.test(cmd)) return null;
|
|
16258
|
+
const quoted = cmd.match(/(?:^|\s)["']([^"']+)["'](?:\s|$)/);
|
|
16259
|
+
if (quoted) {
|
|
16260
|
+
const term = quoted[1];
|
|
16261
|
+
return isSymbolLike(term) ? term : null;
|
|
16262
|
+
}
|
|
16263
|
+
const tokens = cmd.split(/\s+/).slice(1);
|
|
16264
|
+
for (const tok of tokens) {
|
|
16265
|
+
if (tok.startsWith("-")) continue;
|
|
16266
|
+
if (tok.startsWith("/")) continue;
|
|
16267
|
+
if (tok.startsWith("./") || tok.startsWith("../")) continue;
|
|
16268
|
+
if (tok.includes("/")) continue;
|
|
16269
|
+
if (tok === "." || tok === "..") continue;
|
|
16270
|
+
return isSymbolLike(tok) ? tok : null;
|
|
16271
|
+
}
|
|
16272
|
+
return null;
|
|
16273
|
+
}
|
|
16274
|
+
function rewriteCommand(cmd) {
|
|
16275
|
+
try {
|
|
16276
|
+
const trimmed = cmd.trim();
|
|
16277
|
+
if (!trimmed) return null;
|
|
16278
|
+
if (trimmed.startsWith("code-intel ") || trimmed === "code-intel") return null;
|
|
16279
|
+
if (/(?:&&|\|\||;|\|)/.test(trimmed)) return null;
|
|
16280
|
+
if (/^grep\s/.test(trimmed)) {
|
|
16281
|
+
const sym = extractGrepSymbol(trimmed);
|
|
16282
|
+
if (sym) return `code-intel search "${sym}"`;
|
|
16283
|
+
return null;
|
|
16284
|
+
}
|
|
16285
|
+
if (/^rg\s/.test(trimmed)) {
|
|
16286
|
+
if (/\s--files(?:\s|$)/.test(trimmed)) return null;
|
|
16287
|
+
if (/\s--files-with-matches(?:\s|$)/.test(trimmed)) return null;
|
|
16288
|
+
if (/\s--type-not\b/.test(trimmed)) return null;
|
|
16289
|
+
const sym = extractGrepSymbol(trimmed);
|
|
16290
|
+
if (sym) return `code-intel search "${sym}"`;
|
|
16291
|
+
return null;
|
|
16292
|
+
}
|
|
16293
|
+
if (/^cat\s/.test(trimmed)) {
|
|
16294
|
+
const m = trimmed.match(/^cat\s+(\S+)$/);
|
|
16295
|
+
if (!m) return null;
|
|
16296
|
+
const filePath = m[1];
|
|
16297
|
+
if (filePath === "-") return null;
|
|
16298
|
+
if (filePath.startsWith(">")) return null;
|
|
16299
|
+
if (!isSourceFile(filePath)) return null;
|
|
16300
|
+
return `code-intel inspect ${fileStem(filePath)}`;
|
|
16301
|
+
}
|
|
16302
|
+
if (/^(?:head|tail)\s/.test(trimmed)) {
|
|
16303
|
+
if (/\s-f\b/.test(trimmed)) return null;
|
|
16304
|
+
const m = trimmed.match(
|
|
16305
|
+
/^(?:head|tail)\s+(?:-\d+\s+|-n\s+\d+\s+|--lines=\d+\s+)?(\S+)$/
|
|
16306
|
+
);
|
|
16307
|
+
if (!m) return null;
|
|
16308
|
+
const filePath = m[1];
|
|
16309
|
+
if (!isSourceFile(filePath)) return null;
|
|
16310
|
+
return `code-intel inspect ${fileStem(filePath)}`;
|
|
16311
|
+
}
|
|
16312
|
+
return null;
|
|
16313
|
+
} catch {
|
|
16314
|
+
return null;
|
|
16315
|
+
}
|
|
16316
|
+
}
|
|
16317
|
+
function runRewrite(cmd) {
|
|
16318
|
+
const rewritten = rewriteCommand(cmd.trim());
|
|
16319
|
+
if (rewritten === null) {
|
|
16320
|
+
process2.exit(1);
|
|
16321
|
+
}
|
|
16322
|
+
process2.stdout.write(rewritten);
|
|
16323
|
+
process2.exit(0);
|
|
16324
|
+
}
|
|
16325
|
+
function runClaudeHook() {
|
|
16326
|
+
process2.on("uncaughtException", () => process2.exit(0));
|
|
16327
|
+
process2.on("unhandledRejection", () => process2.exit(0));
|
|
16328
|
+
let input = "";
|
|
16329
|
+
process2.stdin.setEncoding("utf-8");
|
|
16330
|
+
process2.stdin.on("data", (chunk) => {
|
|
16331
|
+
input += chunk;
|
|
16332
|
+
});
|
|
16333
|
+
process2.stdin.on("end", () => {
|
|
16334
|
+
try {
|
|
16335
|
+
if (!input.trim()) {
|
|
16336
|
+
process2.exit(0);
|
|
16337
|
+
}
|
|
16338
|
+
const parsed = JSON.parse(input);
|
|
16339
|
+
const cmd = parsed?.tool_input?.command;
|
|
16340
|
+
if (typeof cmd !== "string" || !cmd.trim()) {
|
|
16341
|
+
process2.exit(0);
|
|
16342
|
+
}
|
|
16343
|
+
const rewritten = rewriteCommand(cmd);
|
|
16344
|
+
if (rewritten === null || rewritten === cmd) {
|
|
16345
|
+
process2.exit(0);
|
|
16346
|
+
}
|
|
16347
|
+
const response = {
|
|
16348
|
+
hookSpecificOutput: {
|
|
16349
|
+
hookEventName: "PreToolUse",
|
|
16350
|
+
permissionDecision: "allow",
|
|
16351
|
+
permissionDecisionReason: "code-intel: semantic search replaces grep/cat",
|
|
16352
|
+
updatedInput: {
|
|
16353
|
+
...parsed.tool_input,
|
|
16354
|
+
command: rewritten
|
|
16355
|
+
}
|
|
16356
|
+
}
|
|
16357
|
+
};
|
|
16358
|
+
process2.stdout.write(JSON.stringify(response));
|
|
16359
|
+
process2.exit(0);
|
|
16360
|
+
} catch {
|
|
16361
|
+
process2.exit(0);
|
|
16362
|
+
}
|
|
16363
|
+
});
|
|
16364
|
+
process2.stdin.on("error", () => {
|
|
16365
|
+
process2.exit(0);
|
|
16366
|
+
});
|
|
16367
|
+
}
|
|
16050
16368
|
|
|
16051
16369
|
// src/cli/main.ts
|
|
16052
16370
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
16053
16371
|
var __dirname2 = dirname(__filename$1);
|
|
16054
16372
|
var _pkg = JSON.parse(readFileSync(join(__dirname2, "../../package.json"), "utf-8"));
|
|
16055
|
-
|
|
16373
|
+
var IS_HOOK_MODE = process.argv[2] === "hook";
|
|
16374
|
+
if (!IS_HOOK_MODE) {
|
|
16375
|
+
initTracing();
|
|
16376
|
+
}
|
|
16056
16377
|
var debugMode = process.argv.includes("--debug");
|
|
16057
|
-
{
|
|
16378
|
+
if (!IS_HOOK_MODE) {
|
|
16058
16379
|
const checks = runPrerequisiteChecks();
|
|
16059
16380
|
for (const c of checks) {
|
|
16060
16381
|
const icon = c.level === "error" ? "\u2717" : "\u26A0";
|
|
@@ -16062,20 +16383,28 @@ var debugMode = process.argv.includes("--debug");
|
|
|
16062
16383
|
`);
|
|
16063
16384
|
}
|
|
16064
16385
|
}
|
|
16065
|
-
if (!configExists()) {
|
|
16386
|
+
if (!IS_HOOK_MODE && !configExists()) {
|
|
16066
16387
|
process.stderr.write(
|
|
16067
16388
|
" \u2139 No config found. Run `code-intel init` to set up your environment.\n"
|
|
16068
16389
|
);
|
|
16069
16390
|
}
|
|
16070
16391
|
process.on("uncaughtException", (err) => {
|
|
16392
|
+
if (IS_HOOK_MODE) {
|
|
16393
|
+
process.exit(0);
|
|
16394
|
+
}
|
|
16071
16395
|
process.stderr.write(formatCliError(err, debugMode));
|
|
16072
16396
|
process.exit(1);
|
|
16073
16397
|
});
|
|
16074
16398
|
process.on("unhandledRejection", (err) => {
|
|
16399
|
+
if (IS_HOOK_MODE) {
|
|
16400
|
+
process.exit(0);
|
|
16401
|
+
}
|
|
16075
16402
|
process.stderr.write(formatCliError(err, debugMode));
|
|
16076
16403
|
process.exit(1);
|
|
16077
16404
|
});
|
|
16078
|
-
|
|
16405
|
+
if (!IS_HOOK_MODE) {
|
|
16406
|
+
backgroundVersionCheck(_pkg.version);
|
|
16407
|
+
}
|
|
16079
16408
|
var program = new Command();
|
|
16080
16409
|
var BANNER = `
|
|
16081
16410
|
\u25C8 Code Intelligence Platform v${_pkg.version}
|
|
@@ -16125,6 +16454,8 @@ program.name("code-intel").description("Code Intelligence Platform \u2014 Static
|
|
|
16125
16454
|
\u2502 server \u2502
|
|
16126
16455
|
\u2502 code-intel mcp [path] Launch the MCP stdio server consumed by AI-enabled editors \u2502
|
|
16127
16456
|
\u2502 code-intel serve [path] --port <n> Start the HTTP API and serve the interactive web UI (default :4747) \u2502
|
|
16457
|
+
\u2502 code-intel serve --detach Start the server in the background (frees the terminal) \u2502
|
|
16458
|
+
\u2502 code-intel stop [path] Stop a background server started with --detach \u2502
|
|
16128
16459
|
\u2502 \u2502
|
|
16129
16460
|
\u2502 registry \u2502
|
|
16130
16461
|
\u2502 code-intel list Display all repositories that have been indexed \u2502
|
|
@@ -16272,7 +16603,9 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
16272
16603
|
provider: options.llmProvider ?? "ollama",
|
|
16273
16604
|
model: options.llmModel,
|
|
16274
16605
|
batchSize: options.llmBatchSize,
|
|
16275
|
-
maxNodesPerRun: options.llmMaxNodes
|
|
16606
|
+
maxNodesPerRun: options.llmMaxNodes,
|
|
16607
|
+
baseUrl: options.llmBaseUrl,
|
|
16608
|
+
apiKey: options.llmApiKey
|
|
16276
16609
|
} : void 0,
|
|
16277
16610
|
onProgress: options?.silent ? void 0 : (phase, msg) => {
|
|
16278
16611
|
if (!options?.silent) {
|
|
@@ -16706,6 +17039,396 @@ program.command("completion").description("Generate shell completion scripts (ba
|
|
|
16706
17039
|
}
|
|
16707
17040
|
process.stdout.write(generateCompletion(shell));
|
|
16708
17041
|
});
|
|
17042
|
+
var CODE_INTEL_HOOK_CMD = "code-intel-hook claude";
|
|
17043
|
+
var CODE_INTEL_CURSOR_HOOK_CMD = "code-intel-hook cursor";
|
|
17044
|
+
var CODE_INTEL_GEMINI_HOOK_CMD = "code-intel-hook gemini";
|
|
17045
|
+
function hookAlreadyPresent(root) {
|
|
17046
|
+
const entries = root?.hooks?.PreToolUse;
|
|
17047
|
+
if (!Array.isArray(entries)) return false;
|
|
17048
|
+
return entries.some(
|
|
17049
|
+
(entry) => Array.isArray(entry?.hooks) && entry.hooks.some(
|
|
17050
|
+
(h) => typeof h?.command === "string" && (h.command.includes("code-intel-hook") || h.command.includes("code-intel hook"))
|
|
17051
|
+
)
|
|
17052
|
+
);
|
|
17053
|
+
}
|
|
17054
|
+
function insertHookEntry(root) {
|
|
17055
|
+
const existingHooks = root.hooks ?? {};
|
|
17056
|
+
const existingPreToolUse = Array.isArray(
|
|
17057
|
+
existingHooks.PreToolUse
|
|
17058
|
+
) ? existingHooks.PreToolUse : [];
|
|
17059
|
+
const newEntry = {
|
|
17060
|
+
matcher: "Bash",
|
|
17061
|
+
hooks: [{ type: "command", command: CODE_INTEL_HOOK_CMD }]
|
|
17062
|
+
};
|
|
17063
|
+
return {
|
|
17064
|
+
...root,
|
|
17065
|
+
hooks: {
|
|
17066
|
+
...existingHooks,
|
|
17067
|
+
PreToolUse: [newEntry, ...existingPreToolUse]
|
|
17068
|
+
}
|
|
17069
|
+
};
|
|
17070
|
+
}
|
|
17071
|
+
function installClaudeHook() {
|
|
17072
|
+
const settingsPath = path39.join(os18.homedir(), ".claude", "settings.json");
|
|
17073
|
+
let root = {};
|
|
17074
|
+
if (fs39.existsSync(settingsPath)) {
|
|
17075
|
+
try {
|
|
17076
|
+
const raw = fs39.readFileSync(settingsPath, "utf-8");
|
|
17077
|
+
root = JSON.parse(raw);
|
|
17078
|
+
} catch (err) {
|
|
17079
|
+
return {
|
|
17080
|
+
result: "skipped",
|
|
17081
|
+
reason: `Could not parse ~/.claude/settings.json: ${err instanceof Error ? err.message : String(err)}`
|
|
17082
|
+
};
|
|
17083
|
+
}
|
|
17084
|
+
}
|
|
17085
|
+
if (hookAlreadyPresent(root)) {
|
|
17086
|
+
return { result: "already-present" };
|
|
17087
|
+
}
|
|
17088
|
+
const updated = insertHookEntry(root);
|
|
17089
|
+
if (fs39.existsSync(settingsPath)) {
|
|
17090
|
+
try {
|
|
17091
|
+
fs39.copyFileSync(settingsPath, `${settingsPath}.bak`);
|
|
17092
|
+
} catch {
|
|
17093
|
+
logger_default.warn(" \u26A0 Could not create settings.json backup \u2014 proceeding anyway");
|
|
17094
|
+
}
|
|
17095
|
+
}
|
|
17096
|
+
try {
|
|
17097
|
+
fs39.mkdirSync(path39.dirname(settingsPath), { recursive: true });
|
|
17098
|
+
const tmp = `${settingsPath}.tmp.${Date.now()}`;
|
|
17099
|
+
fs39.writeFileSync(tmp, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
17100
|
+
fs39.renameSync(tmp, settingsPath);
|
|
17101
|
+
} catch (err) {
|
|
17102
|
+
return {
|
|
17103
|
+
result: "skipped",
|
|
17104
|
+
reason: `Could not write ~/.claude/settings.json: ${err instanceof Error ? err.message : String(err)}`
|
|
17105
|
+
};
|
|
17106
|
+
}
|
|
17107
|
+
return { result: "installed" };
|
|
17108
|
+
}
|
|
17109
|
+
var COPILOT_HOOK_JSON_CONTENT = JSON.stringify({
|
|
17110
|
+
hooks: {
|
|
17111
|
+
PreToolUse: [
|
|
17112
|
+
{ type: "command", command: CODE_INTEL_HOOK_CMD.replace("claude", "copilot"), cwd: ".", timeout: 5 }
|
|
17113
|
+
]
|
|
17114
|
+
}
|
|
17115
|
+
}, null, 2) + "\n";
|
|
17116
|
+
function installCopilotHook(workspaceRoot = ".") {
|
|
17117
|
+
const githubDir = path39.join(workspaceRoot, ".github");
|
|
17118
|
+
const hooksDir = path39.join(githubDir, "hooks");
|
|
17119
|
+
const hookFile = path39.join(hooksDir, "code-intel-rewrite.json");
|
|
17120
|
+
try {
|
|
17121
|
+
fs39.mkdirSync(hooksDir, { recursive: true });
|
|
17122
|
+
if (fs39.existsSync(hookFile)) {
|
|
17123
|
+
try {
|
|
17124
|
+
const existing = JSON.parse(fs39.readFileSync(hookFile, "utf-8"));
|
|
17125
|
+
const hooks = existing?.hooks?.PreToolUse;
|
|
17126
|
+
if (Array.isArray(hooks) && hooks.some(
|
|
17127
|
+
(h) => typeof h?.command === "string" && h.command.includes("code-intel")
|
|
17128
|
+
)) {
|
|
17129
|
+
return { result: "already-present" };
|
|
17130
|
+
}
|
|
17131
|
+
} catch {
|
|
17132
|
+
}
|
|
17133
|
+
}
|
|
17134
|
+
const tmp = `${hookFile}.tmp.${Date.now()}`;
|
|
17135
|
+
fs39.writeFileSync(tmp, COPILOT_HOOK_JSON_CONTENT, "utf-8");
|
|
17136
|
+
fs39.renameSync(tmp, hookFile);
|
|
17137
|
+
return { result: "installed" };
|
|
17138
|
+
} catch (err) {
|
|
17139
|
+
return { result: "skipped", reason: `Could not write .github/hooks/code-intel-rewrite.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
17140
|
+
}
|
|
17141
|
+
}
|
|
17142
|
+
var OPENCODE_PLUGIN_CONTENT = `import type { Plugin } from "@opencode-ai/plugin"
|
|
17143
|
+
import { execSync } from "node:child_process"
|
|
17144
|
+
|
|
17145
|
+
// code-intel OpenCode plugin \u2014 rewrites symbol-discovery commands to semantic equivalents.
|
|
17146
|
+
// Requires: code-intel installed and in PATH.
|
|
17147
|
+
// To change rewrite rules, edit hook-rewriter.ts in the code-intel source.
|
|
17148
|
+
|
|
17149
|
+
let codeIntelAvailable: boolean | null = null
|
|
17150
|
+
|
|
17151
|
+
function checkCodeIntel(): boolean {
|
|
17152
|
+
if (codeIntelAvailable !== null) return codeIntelAvailable
|
|
17153
|
+
try {
|
|
17154
|
+
execSync("which code-intel", { stdio: "ignore" })
|
|
17155
|
+
codeIntelAvailable = true
|
|
17156
|
+
} catch {
|
|
17157
|
+
codeIntelAvailable = false
|
|
17158
|
+
}
|
|
17159
|
+
return codeIntelAvailable
|
|
17160
|
+
}
|
|
17161
|
+
|
|
17162
|
+
function tryRewrite(command: string): string | null {
|
|
17163
|
+
try {
|
|
17164
|
+
const result = execSync(\`code-intel rewrite \${JSON.stringify(command)}\`, {
|
|
17165
|
+
encoding: "utf-8",
|
|
17166
|
+
timeout: 2000,
|
|
17167
|
+
}).trim()
|
|
17168
|
+
return result && result !== command ? result : null
|
|
17169
|
+
} catch {
|
|
17170
|
+
return null
|
|
17171
|
+
}
|
|
17172
|
+
}
|
|
17173
|
+
|
|
17174
|
+
export const CodeIntelOpenCodePlugin: Plugin = async ({ $ }) => {
|
|
17175
|
+
if (!checkCodeIntel()) {
|
|
17176
|
+
console.warn("[code-intel] code-intel binary not found in PATH \u2014 plugin disabled")
|
|
17177
|
+
return {}
|
|
17178
|
+
}
|
|
17179
|
+
return {
|
|
17180
|
+
"tool.execute.before": async (input, output) => {
|
|
17181
|
+
const tool = String(input?.tool ?? "").toLowerCase()
|
|
17182
|
+
if (tool !== "bash" && tool !== "shell") return
|
|
17183
|
+
const args = output?.args
|
|
17184
|
+
if (!args || typeof args !== "object") return
|
|
17185
|
+
const command = (args as Record<string, unknown>).command
|
|
17186
|
+
if (typeof command !== "string" || !command) return
|
|
17187
|
+
try {
|
|
17188
|
+
const rewritten = tryRewrite(command)
|
|
17189
|
+
if (rewritten) {
|
|
17190
|
+
;(args as Record<string, unknown>).command = rewritten
|
|
17191
|
+
}
|
|
17192
|
+
} catch {
|
|
17193
|
+
// non-blocking
|
|
17194
|
+
}
|
|
17195
|
+
},
|
|
17196
|
+
}
|
|
17197
|
+
}
|
|
17198
|
+
`;
|
|
17199
|
+
function installOpenCodePlugin() {
|
|
17200
|
+
const pluginDir = path39.join(os18.homedir(), ".config", "opencode", "plugins");
|
|
17201
|
+
const pluginFile = path39.join(pluginDir, "code-intel.ts");
|
|
17202
|
+
const opencodeCfg = path39.join(os18.homedir(), ".config", "opencode");
|
|
17203
|
+
if (!fs39.existsSync(opencodeCfg)) {
|
|
17204
|
+
return { result: "skipped", reason: "OpenCode not installed (~/.config/opencode not found)" };
|
|
17205
|
+
}
|
|
17206
|
+
if (fs39.existsSync(pluginFile)) {
|
|
17207
|
+
return { result: "already-present" };
|
|
17208
|
+
}
|
|
17209
|
+
try {
|
|
17210
|
+
fs39.mkdirSync(pluginDir, { recursive: true });
|
|
17211
|
+
const tmp = `${pluginFile}.tmp.${Date.now()}`;
|
|
17212
|
+
fs39.writeFileSync(tmp, OPENCODE_PLUGIN_CONTENT, "utf-8");
|
|
17213
|
+
fs39.renameSync(tmp, pluginFile);
|
|
17214
|
+
return { result: "installed" };
|
|
17215
|
+
} catch (err) {
|
|
17216
|
+
return { result: "skipped", reason: `Could not write plugin: ${err instanceof Error ? err.message : String(err)}` };
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17219
|
+
var OPENCLAW_PLUGIN_CONTENT = `import { execSync } from "node:child_process";
|
|
17220
|
+
|
|
17221
|
+
// code-intel OpenClaw plugin \u2014 rewrites symbol-discovery commands to semantic equivalents.
|
|
17222
|
+
// Requires: code-intel installed and in PATH.
|
|
17223
|
+
// To change rewrite rules, edit hook-rewriter.ts in the code-intel source.
|
|
17224
|
+
|
|
17225
|
+
let codeIntelAvailable: boolean | null = null;
|
|
17226
|
+
|
|
17227
|
+
function checkCodeIntel(): boolean {
|
|
17228
|
+
if (codeIntelAvailable !== null) return codeIntelAvailable;
|
|
17229
|
+
try {
|
|
17230
|
+
execSync("which code-intel", { stdio: "ignore" });
|
|
17231
|
+
codeIntelAvailable = true;
|
|
17232
|
+
} catch {
|
|
17233
|
+
codeIntelAvailable = false;
|
|
17234
|
+
}
|
|
17235
|
+
return codeIntelAvailable;
|
|
17236
|
+
}
|
|
17237
|
+
|
|
17238
|
+
function tryRewrite(command: string): string | null {
|
|
17239
|
+
try {
|
|
17240
|
+
const result = execSync(\`code-intel rewrite \${JSON.stringify(command)}\`, {
|
|
17241
|
+
encoding: "utf-8",
|
|
17242
|
+
timeout: 2000,
|
|
17243
|
+
}).trim();
|
|
17244
|
+
return result && result !== command ? result : null;
|
|
17245
|
+
} catch {
|
|
17246
|
+
return null;
|
|
17247
|
+
}
|
|
17248
|
+
}
|
|
17249
|
+
|
|
17250
|
+
export default function register(api: any) {
|
|
17251
|
+
const pluginConfig = api.config ?? {};
|
|
17252
|
+
const enabled = pluginConfig.enabled !== false;
|
|
17253
|
+
const verbose = pluginConfig.verbose === true;
|
|
17254
|
+
|
|
17255
|
+
if (!enabled) return;
|
|
17256
|
+
|
|
17257
|
+
if (!checkCodeIntel()) {
|
|
17258
|
+
console.warn("[code-intel] code-intel binary not found in PATH \u2014 plugin disabled");
|
|
17259
|
+
return;
|
|
17260
|
+
}
|
|
17261
|
+
|
|
17262
|
+
api.on(
|
|
17263
|
+
"before_tool_call",
|
|
17264
|
+
(event: { toolName: string; params: Record<string, unknown> }) => {
|
|
17265
|
+
if (event.toolName !== "exec") return;
|
|
17266
|
+
const command = event.params?.command;
|
|
17267
|
+
if (typeof command !== "string") return;
|
|
17268
|
+
const rewritten = tryRewrite(command);
|
|
17269
|
+
if (!rewritten) return;
|
|
17270
|
+
if (verbose) console.log(\`[code-intel] \${command} -> \${rewritten}\`);
|
|
17271
|
+
return { params: { ...event.params, command: rewritten } };
|
|
17272
|
+
},
|
|
17273
|
+
{ priority: 10 }
|
|
17274
|
+
);
|
|
17275
|
+
|
|
17276
|
+
if (verbose) console.log("[code-intel] OpenClaw plugin registered");
|
|
17277
|
+
}
|
|
17278
|
+
`;
|
|
17279
|
+
function installOpenClawPlugin() {
|
|
17280
|
+
const openclawDir = path39.join(os18.homedir(), ".openclaw");
|
|
17281
|
+
if (!fs39.existsSync(openclawDir)) {
|
|
17282
|
+
return { result: "skipped", reason: "OpenClaw not installed (~/.openclaw directory not found)" };
|
|
17283
|
+
}
|
|
17284
|
+
const pluginDir = path39.join(openclawDir, "extensions", "code-intel");
|
|
17285
|
+
const pluginFile = path39.join(pluginDir, "index.ts");
|
|
17286
|
+
if (fs39.existsSync(pluginFile)) {
|
|
17287
|
+
return { result: "already-present" };
|
|
17288
|
+
}
|
|
17289
|
+
try {
|
|
17290
|
+
fs39.mkdirSync(pluginDir, { recursive: true });
|
|
17291
|
+
const tmp = `${pluginFile}.tmp.${Date.now()}`;
|
|
17292
|
+
fs39.writeFileSync(tmp, OPENCLAW_PLUGIN_CONTENT, "utf-8");
|
|
17293
|
+
fs39.renameSync(tmp, pluginFile);
|
|
17294
|
+
return { result: "installed" };
|
|
17295
|
+
} catch (err) {
|
|
17296
|
+
return { result: "skipped", reason: `Could not write plugin: ${err instanceof Error ? err.message : String(err)}` };
|
|
17297
|
+
}
|
|
17298
|
+
}
|
|
17299
|
+
var CODE_INTEL_RULES_CONTENT = `# Code Intelligence \u2014 Tool Policy
|
|
17300
|
+
|
|
17301
|
+
## MANDATORY: Use code-intel before any code action
|
|
17302
|
+
|
|
17303
|
+
**FORBIDDEN** (never use for symbol/code discovery): \`grep\`, \`rg\`, \`find\`, \`cat\`, \`sed\`, \`ls\`
|
|
17304
|
+
|
|
17305
|
+
**REQUIRED workflow**:
|
|
17306
|
+
\`\`\`bash
|
|
17307
|
+
code-intel search "<concept>" # replaces grep/rg/find
|
|
17308
|
+
code-intel inspect <symbol> # replaces cat + manual reading
|
|
17309
|
+
code-intel impact <symbol> # replaces manual caller tracing
|
|
17310
|
+
\`\`\`
|
|
17311
|
+
|
|
17312
|
+
Rules:
|
|
17313
|
+
- NEVER open a file cold \u2014 always \`code-intel search\` first
|
|
17314
|
+
- NEVER use grep for symbols \u2014 use \`code-intel search\` instead
|
|
17315
|
+
- If impact shows \u2265 5 callers \u2192 warn user: HIGH blast radius
|
|
17316
|
+
`;
|
|
17317
|
+
function installRulesFile(filePath, agentName) {
|
|
17318
|
+
try {
|
|
17319
|
+
const dir = path39.dirname(filePath);
|
|
17320
|
+
if (!fs39.existsSync(dir)) {
|
|
17321
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
17322
|
+
}
|
|
17323
|
+
if (fs39.existsSync(filePath)) {
|
|
17324
|
+
const existing = fs39.readFileSync(filePath, "utf-8");
|
|
17325
|
+
if (existing.includes("code-intel search") || existing.includes("code-intel inspect")) {
|
|
17326
|
+
return { result: "already-present" };
|
|
17327
|
+
}
|
|
17328
|
+
fs39.appendFileSync(filePath, "\n---\n\n" + CODE_INTEL_RULES_CONTENT);
|
|
17329
|
+
return { result: "installed" };
|
|
17330
|
+
}
|
|
17331
|
+
fs39.writeFileSync(filePath, CODE_INTEL_RULES_CONTENT, "utf-8");
|
|
17332
|
+
return { result: "installed" };
|
|
17333
|
+
} catch (err) {
|
|
17334
|
+
return { result: "skipped", reason: `Could not write ${agentName} rules: ${err instanceof Error ? err.message : String(err)}` };
|
|
17335
|
+
}
|
|
17336
|
+
}
|
|
17337
|
+
function cursorHookAlreadyPresent(root) {
|
|
17338
|
+
const hooks = root?.hooks;
|
|
17339
|
+
const preToolUse = hooks?.preToolUse;
|
|
17340
|
+
if (!Array.isArray(preToolUse)) return false;
|
|
17341
|
+
return preToolUse.some((entry) => {
|
|
17342
|
+
const cmd = entry?.command;
|
|
17343
|
+
return typeof cmd === "string" && (cmd.includes("code-intel-hook") || cmd.includes("code-intel hook"));
|
|
17344
|
+
});
|
|
17345
|
+
}
|
|
17346
|
+
function installCursorHook() {
|
|
17347
|
+
const cursorDir = path39.join(os18.homedir(), ".cursor");
|
|
17348
|
+
const hooksPath = path39.join(cursorDir, "hooks.json");
|
|
17349
|
+
let root = { version: 1 };
|
|
17350
|
+
if (fs39.existsSync(hooksPath)) {
|
|
17351
|
+
try {
|
|
17352
|
+
const raw = fs39.readFileSync(hooksPath, "utf-8");
|
|
17353
|
+
if (raw.trim()) root = JSON.parse(raw);
|
|
17354
|
+
} catch (err) {
|
|
17355
|
+
return { result: "skipped", reason: `Could not parse ~/.cursor/hooks.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
17356
|
+
}
|
|
17357
|
+
} else if (!fs39.existsSync(cursorDir)) {
|
|
17358
|
+
return { result: "skipped", reason: "Cursor not installed (~/.cursor directory not found)" };
|
|
17359
|
+
}
|
|
17360
|
+
if (cursorHookAlreadyPresent(root)) return { result: "already-present" };
|
|
17361
|
+
if (fs39.existsSync(hooksPath)) {
|
|
17362
|
+
try {
|
|
17363
|
+
fs39.copyFileSync(hooksPath, `${hooksPath}.bak`);
|
|
17364
|
+
} catch {
|
|
17365
|
+
}
|
|
17366
|
+
}
|
|
17367
|
+
const existingHooks = root.hooks ?? {};
|
|
17368
|
+
const existingPreToolUse = Array.isArray(existingHooks.preToolUse) ? existingHooks.preToolUse : [];
|
|
17369
|
+
const newEntry = { command: CODE_INTEL_CURSOR_HOOK_CMD, matcher: "Shell" };
|
|
17370
|
+
const updated = { ...root, hooks: { ...existingHooks, preToolUse: [newEntry, ...existingPreToolUse] } };
|
|
17371
|
+
try {
|
|
17372
|
+
fs39.mkdirSync(cursorDir, { recursive: true });
|
|
17373
|
+
const tmp = `${hooksPath}.tmp.${Date.now()}`;
|
|
17374
|
+
fs39.writeFileSync(tmp, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
17375
|
+
fs39.renameSync(tmp, hooksPath);
|
|
17376
|
+
} catch (err) {
|
|
17377
|
+
return { result: "skipped", reason: `Could not write ~/.cursor/hooks.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
17378
|
+
}
|
|
17379
|
+
return { result: "installed" };
|
|
17380
|
+
}
|
|
17381
|
+
function geminiHookAlreadyPresent(root) {
|
|
17382
|
+
const hooks = root?.hooks;
|
|
17383
|
+
const beforeTool = hooks?.BeforeTool;
|
|
17384
|
+
if (!Array.isArray(beforeTool)) return false;
|
|
17385
|
+
return beforeTool.some((entry) => {
|
|
17386
|
+
const entryHooks = entry?.hooks;
|
|
17387
|
+
if (!Array.isArray(entryHooks)) return false;
|
|
17388
|
+
return entryHooks.some((h) => {
|
|
17389
|
+
const cmd = h?.command;
|
|
17390
|
+
return typeof cmd === "string" && (cmd.includes("code-intel-hook") || cmd.includes("code-intel hook"));
|
|
17391
|
+
});
|
|
17392
|
+
});
|
|
17393
|
+
}
|
|
17394
|
+
function installGeminiHook() {
|
|
17395
|
+
const geminiDir = path39.join(os18.homedir(), ".gemini");
|
|
17396
|
+
const settingsPath = path39.join(geminiDir, "settings.json");
|
|
17397
|
+
if (!fs39.existsSync(geminiDir)) {
|
|
17398
|
+
return { result: "skipped", reason: "Gemini CLI not installed (~/.gemini directory not found)" };
|
|
17399
|
+
}
|
|
17400
|
+
let root = {};
|
|
17401
|
+
if (fs39.existsSync(settingsPath)) {
|
|
17402
|
+
try {
|
|
17403
|
+
const raw = fs39.readFileSync(settingsPath, "utf-8");
|
|
17404
|
+
if (raw.trim()) root = JSON.parse(raw);
|
|
17405
|
+
} catch (err) {
|
|
17406
|
+
return { result: "skipped", reason: `Could not parse ~/.gemini/settings.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
17407
|
+
}
|
|
17408
|
+
}
|
|
17409
|
+
if (geminiHookAlreadyPresent(root)) return { result: "already-present" };
|
|
17410
|
+
if (fs39.existsSync(settingsPath)) {
|
|
17411
|
+
try {
|
|
17412
|
+
fs39.copyFileSync(settingsPath, `${settingsPath}.bak`);
|
|
17413
|
+
} catch {
|
|
17414
|
+
}
|
|
17415
|
+
}
|
|
17416
|
+
const existingHooks = root.hooks ?? {};
|
|
17417
|
+
const existingBeforeTool = Array.isArray(existingHooks.BeforeTool) ? existingHooks.BeforeTool : [];
|
|
17418
|
+
const newEntry = {
|
|
17419
|
+
matcher: "run_shell_command",
|
|
17420
|
+
hooks: [{ type: "command", command: CODE_INTEL_GEMINI_HOOK_CMD }]
|
|
17421
|
+
};
|
|
17422
|
+
const updated = { ...root, hooks: { ...existingHooks, BeforeTool: [newEntry, ...existingBeforeTool] } };
|
|
17423
|
+
try {
|
|
17424
|
+
const tmp = `${settingsPath}.tmp.${Date.now()}`;
|
|
17425
|
+
fs39.writeFileSync(tmp, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
17426
|
+
fs39.renameSync(tmp, settingsPath);
|
|
17427
|
+
} catch (err) {
|
|
17428
|
+
return { result: "skipped", reason: `Could not write ~/.gemini/settings.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
17429
|
+
}
|
|
17430
|
+
return { result: "installed" };
|
|
17431
|
+
}
|
|
16709
17432
|
program.command("setup").description("Configure MCP server for your editors (one-time setup)").option("--completion", "Auto-install shell completion for the detected shell").addHelpText("after", `
|
|
16710
17433
|
Configure the code-intel MCP server for Claude Desktop, VS Code, or any
|
|
16711
17434
|
editor that supports the Model Context Protocol.
|
|
@@ -16761,8 +17484,129 @@ program.command("setup").description("Configure MCP server for your editors (one
|
|
|
16761
17484
|
console.log(" " + JSON.stringify({ servers: { "code-intel": { type: "stdio", command: "npx", args: ["code-intel", "mcp", "."] } } }, null, 2).split("\n").join("\n "));
|
|
16762
17485
|
console.log('\n To verify in VS Code: open Command Palette \u2192 "MCP: List Servers" and confirm code-intel is Running.');
|
|
16763
17486
|
console.log("\n Next: run `code-intel analyze` inside your project to build the knowledge graph.\n");
|
|
17487
|
+
const hookResult = installClaudeHook();
|
|
17488
|
+
if (hookResult.result === "installed") {
|
|
17489
|
+
const backupPath = path39.join(os18.homedir(), ".claude", "settings.json.bak");
|
|
17490
|
+
console.log("\n \u2705 Claude Code hook installed");
|
|
17491
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17492
|
+
if (fs39.existsSync(backupPath)) {
|
|
17493
|
+
console.log(` Backup: ${backupPath}`);
|
|
17494
|
+
}
|
|
17495
|
+
console.log("\n \u21BA Restart Claude Code for the hook to take effect.");
|
|
17496
|
+
} else if (hookResult.result === "already-present") {
|
|
17497
|
+
console.log("\n \u2705 Claude Code hook already installed");
|
|
17498
|
+
} else {
|
|
17499
|
+
logger_default.warn(`
|
|
17500
|
+
\u26A0 Claude Code hook skipped: ${hookResult.reason}`);
|
|
17501
|
+
console.log("\n Add manually to ~/.claude/settings.json under hooks.PreToolUse[]:");
|
|
17502
|
+
console.log(" (insert as the FIRST entry so it runs before any existing hooks)\n");
|
|
17503
|
+
console.log(" " + JSON.stringify({
|
|
17504
|
+
matcher: "Bash",
|
|
17505
|
+
hooks: [{ type: "command", command: CODE_INTEL_HOOK_CMD }]
|
|
17506
|
+
}, null, 2).split("\n").join("\n "));
|
|
17507
|
+
}
|
|
17508
|
+
const cursorResult = installCursorHook();
|
|
17509
|
+
if (cursorResult.result === "installed") {
|
|
17510
|
+
console.log("\n \u2705 Cursor hook installed");
|
|
17511
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17512
|
+
} else if (cursorResult.result === "already-present") {
|
|
17513
|
+
console.log("\n \u2705 Cursor hook already installed");
|
|
17514
|
+
} else {
|
|
17515
|
+
console.log(`
|
|
17516
|
+
\u2139 Cursor hook skipped: ${cursorResult.reason}`);
|
|
17517
|
+
}
|
|
17518
|
+
const geminiResult = installGeminiHook();
|
|
17519
|
+
if (geminiResult.result === "installed") {
|
|
17520
|
+
console.log("\n \u2705 Gemini CLI hook installed");
|
|
17521
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17522
|
+
} else if (geminiResult.result === "already-present") {
|
|
17523
|
+
console.log("\n \u2705 Gemini CLI hook already installed");
|
|
17524
|
+
} else {
|
|
17525
|
+
console.log(`
|
|
17526
|
+
\u2139 Gemini CLI hook skipped: ${geminiResult.reason}`);
|
|
17527
|
+
}
|
|
17528
|
+
const copilotResult = installCopilotHook(process.cwd());
|
|
17529
|
+
if (copilotResult.result === "installed") {
|
|
17530
|
+
console.log("\n \u2705 GitHub Copilot hook installed (.github/hooks/code-intel-rewrite.json)");
|
|
17531
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17532
|
+
} else if (copilotResult.result === "already-present") {
|
|
17533
|
+
console.log("\n \u2705 GitHub Copilot hook already installed");
|
|
17534
|
+
} else {
|
|
17535
|
+
console.log(`
|
|
17536
|
+
\u2139 GitHub Copilot hook skipped: ${copilotResult.reason}`);
|
|
17537
|
+
}
|
|
17538
|
+
const openCodeResult = installOpenCodePlugin();
|
|
17539
|
+
if (openCodeResult.result === "installed") {
|
|
17540
|
+
console.log("\n \u2705 OpenCode plugin installed (~/.config/opencode/plugins/code-intel.ts)");
|
|
17541
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17542
|
+
} else if (openCodeResult.result === "already-present") {
|
|
17543
|
+
console.log("\n \u2705 OpenCode plugin already installed");
|
|
17544
|
+
} else {
|
|
17545
|
+
console.log(`
|
|
17546
|
+
\u2139 OpenCode plugin skipped: ${openCodeResult.reason}`);
|
|
17547
|
+
}
|
|
17548
|
+
const openClawResult = installOpenClawPlugin();
|
|
17549
|
+
if (openClawResult.result === "installed") {
|
|
17550
|
+
console.log("\n \u2705 OpenClaw plugin installed (~/.openclaw/extensions/code-intel/)");
|
|
17551
|
+
console.log(" grep/rg/cat on source files \u2192 code-intel search/inspect");
|
|
17552
|
+
} else if (openClawResult.result === "already-present") {
|
|
17553
|
+
console.log("\n \u2705 OpenClaw plugin already installed");
|
|
17554
|
+
} else {
|
|
17555
|
+
console.log(`
|
|
17556
|
+
\u2139 OpenClaw plugin skipped: ${openClawResult.reason}`);
|
|
17557
|
+
}
|
|
17558
|
+
console.log("\n \u2500\u2500\u2500 Prompt-level agents (rules files) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
17559
|
+
const cwd = process.cwd();
|
|
17560
|
+
const clineResult = installRulesFile(path39.join(cwd, ".clinerules"), "Cline");
|
|
17561
|
+
if (clineResult.result === "installed") {
|
|
17562
|
+
console.log(" \u2705 Cline/Roo Code rules installed (.clinerules)");
|
|
17563
|
+
} else if (clineResult.result === "already-present") {
|
|
17564
|
+
console.log(" \u2705 Cline/Roo Code rules already present (.clinerules)");
|
|
17565
|
+
} else {
|
|
17566
|
+
console.log(` \u2139 Cline/Roo Code rules skipped: ${clineResult.reason}`);
|
|
17567
|
+
}
|
|
17568
|
+
const windsurfResult = installRulesFile(path39.join(cwd, ".windsurfrules"), "Windsurf");
|
|
17569
|
+
if (windsurfResult.result === "installed") {
|
|
17570
|
+
console.log(" \u2705 Windsurf rules installed (.windsurfrules)");
|
|
17571
|
+
} else if (windsurfResult.result === "already-present") {
|
|
17572
|
+
console.log(" \u2705 Windsurf rules already present (.windsurfrules)");
|
|
17573
|
+
} else {
|
|
17574
|
+
console.log(` \u2139 Windsurf rules skipped: ${windsurfResult.reason}`);
|
|
17575
|
+
}
|
|
17576
|
+
const kiloResult = installRulesFile(
|
|
17577
|
+
path39.join(cwd, ".kilocode", "rules", "code-intel-rules.md"),
|
|
17578
|
+
"Kilo Code"
|
|
17579
|
+
);
|
|
17580
|
+
if (kiloResult.result === "installed") {
|
|
17581
|
+
console.log(" \u2705 Kilo Code rules installed (.kilocode/rules/code-intel-rules.md)");
|
|
17582
|
+
} else if (kiloResult.result === "already-present") {
|
|
17583
|
+
console.log(" \u2705 Kilo Code rules already present");
|
|
17584
|
+
} else {
|
|
17585
|
+
console.log(` \u2139 Kilo Code rules skipped: ${kiloResult.reason}`);
|
|
17586
|
+
}
|
|
17587
|
+
const antigravityResult = installRulesFile(
|
|
17588
|
+
path39.join(cwd, ".agents", "rules", "code-intel-rules.md"),
|
|
17589
|
+
"Antigravity"
|
|
17590
|
+
);
|
|
17591
|
+
if (antigravityResult.result === "installed") {
|
|
17592
|
+
console.log(" \u2705 Google Antigravity rules installed (.agents/rules/code-intel-rules.md)");
|
|
17593
|
+
} else if (antigravityResult.result === "already-present") {
|
|
17594
|
+
console.log(" \u2705 Google Antigravity rules already present");
|
|
17595
|
+
} else {
|
|
17596
|
+
console.log(` \u2139 Google Antigravity rules skipped: ${antigravityResult.reason}`);
|
|
17597
|
+
}
|
|
17598
|
+
const codexResult = installRulesFile(path39.join(cwd, "AGENTS.md"), "Codex");
|
|
17599
|
+
if (codexResult.result === "installed") {
|
|
17600
|
+
console.log(" \u2705 Codex CLI rules appended (AGENTS.md)");
|
|
17601
|
+
} else if (codexResult.result === "already-present") {
|
|
17602
|
+
console.log(" \u2705 Codex CLI rules already present (AGENTS.md)");
|
|
17603
|
+
} else {
|
|
17604
|
+
console.log(` \u2139 Codex CLI rules skipped: ${codexResult.reason}`);
|
|
17605
|
+
}
|
|
17606
|
+
console.log("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
17607
|
+
console.log("\n \u25C8 Setup complete. Run `code-intel analyze` to build the knowledge graph.\n");
|
|
16764
17608
|
});
|
|
16765
|
-
program.command("analyze").description("Index a repository and build the knowledge graph").argument("[path]", "Path to the repository (default: current directory)", ".").option("--force", "Force full re-index, ignoring cached data").option("--incremental", "Only re-parse files changed since last analysis (git diff or mtime)").option("--parallel", "Use worker threads for parse + resolve phases (faster on multi-core)").option("--skills", "Generate .claude/skills/ SKILL.md files from detected clusters").option("--embeddings", "Build vector embeddings for semantic search (slower, recommended)").option("--skip-embeddings", "Skip embedding generation (faster, text-search only)").option("--skip-agents-md", "Preserve any custom edits inside AGENTS.md / CLAUDE.md").option("--skip-git", "Allow indexing directories that are not Git repositories").option("--verbose", "Log every file skipped due to missing parser support").option("--summarize", "Generate AI summaries for function/class/method/interface nodes (opt-in)").option("--llm-provider <provider>", "LLM provider for --summarize: openai | anthropic | ollama (default: ollama)").option("--llm-model <model>", "LLM model name (e.g. gpt-4o-mini, claude-haiku-4-5, llama3)").option("--llm-batch-size <n>", "Concurrent LLM calls per batch (default: 20)", "20").option("--llm-max-nodes <n>", "Max nodes to summarize per run (cost guard)").option("--no-group-sync", "Skip automatic group sync after analysis").option("--dry-run", "Preview files that would be scanned + estimated time; no DB write").option("--max-memory <MB>", "Limit graph memory (MB); spill node content to free RAM when exceeded").option("--profile", "Write per-phase profiling data to .code-intel/profile.json").addHelpText("after", `
|
|
17609
|
+
program.command("analyze").description("Index a repository and build the knowledge graph").argument("[path]", "Path to the repository (default: current directory)", ".").option("--force", "Force full re-index, ignoring cached data").option("--incremental", "Only re-parse files changed since last analysis (git diff or mtime)").option("--parallel", "Use worker threads for parse + resolve phases (faster on multi-core)").option("--skills", "Generate .claude/skills/ SKILL.md files from detected clusters").option("--embeddings", "Build vector embeddings for semantic search (slower, recommended)").option("--skip-embeddings", "Skip embedding generation (faster, text-search only)").option("--skip-agents-md", "Preserve any custom edits inside AGENTS.md / CLAUDE.md").option("--skip-git", "Allow indexing directories that are not Git repositories").option("--verbose", "Log every file skipped due to missing parser support").option("--summarize", "Generate AI summaries for function/class/method/interface nodes (opt-in)").option("--llm-provider <provider>", "LLM provider for --summarize: openai | anthropic | ollama | custom (default: ollama)").option("--llm-model <model>", "LLM model name (e.g. gpt-4o-mini, claude-haiku-4-5, llama3, mistral)").option("--llm-base-url <url>", "Base URL for custom OpenAI-compatible API (e.g. http://localhost:1234/v1)").option("--llm-api-key <key>", "API key/token for the LLM provider (custom or override)").option("--llm-batch-size <n>", "Concurrent LLM calls per batch (default: 20)", "20").option("--llm-max-nodes <n>", "Max nodes to summarize per run (cost guard)").option("--no-group-sync", "Skip automatic group sync after analysis").option("--dry-run", "Preview files that would be scanned + estimated time; no DB write").option("--max-memory <MB>", "Limit graph memory (MB); spill node content to free RAM when exceeded").option("--profile", "Write per-phase profiling data to .code-intel/profile.json").addHelpText("after", `
|
|
16766
17610
|
Parses your source code with tree-sitter, builds a Knowledge Graph of
|
|
16767
17611
|
symbols and their relationships, persists it to .code-intel/graph.db,
|
|
16768
17612
|
and auto-generates AGENTS.md + CLAUDE.md context blocks.
|
|
@@ -16782,6 +17626,7 @@ program.command("analyze").description("Index a repository and build the knowled
|
|
|
16782
17626
|
$ code-intel analyze --summarize Generate AI summaries (uses Ollama by default)
|
|
16783
17627
|
$ code-intel analyze --summarize --llm-provider openai --llm-model gpt-4o-mini
|
|
16784
17628
|
$ code-intel analyze --summarize --llm-provider anthropic --llm-max-nodes 500
|
|
17629
|
+
$ code-intel analyze --summarize --llm-provider custom --llm-base-url http://localhost:1234/v1 --llm-model mistral --llm-api-key mytoken
|
|
16785
17630
|
$ code-intel analyze --dry-run Preview files that would be scanned
|
|
16786
17631
|
`).action(async (targetPath, opts) => {
|
|
16787
17632
|
if (opts.dryRun) {
|
|
@@ -16828,6 +17673,8 @@ program.command("analyze").description("Index a repository and build the knowled
|
|
|
16828
17673
|
const v = parseInt(opts.llmMaxNodes ?? "", 10);
|
|
16829
17674
|
return Number.isFinite(v) && v >= 1 ? v : void 0;
|
|
16830
17675
|
})(),
|
|
17676
|
+
llmBaseUrl: opts.llmBaseUrl,
|
|
17677
|
+
llmApiKey: opts.llmApiKey,
|
|
16831
17678
|
maxMemoryMB: (() => {
|
|
16832
17679
|
const v = parseInt(opts.maxMemory ?? "", 10);
|
|
16833
17680
|
return Number.isFinite(v) && v >= 1 ? v : void 0;
|
|
@@ -16867,11 +17714,18 @@ program.command("mcp").description("Start MCP server over stdio \u2014 exposes a
|
|
|
16867
17714
|
await startMcpStdio(graph, name, root);
|
|
16868
17715
|
}
|
|
16869
17716
|
});
|
|
16870
|
-
program.command("serve").description("Start the local HTTP server + web UI for graph exploration").argument("[path]", "Path to analyze (default: current directory)", ".").option("-p, --port <port>", "Port to listen on", "4747").option("--force", "Force re-analysis even if an index already exists").addHelpText("after", `
|
|
17717
|
+
program.command("serve").description("Start the local HTTP server + web UI for graph exploration").argument("[path]", "Path to analyze (default: current directory)", ".").option("-p, --port <port>", "Port to listen on", "4747").option("--force", "Force re-analysis even if an index already exists").option("-d, --detach", "Run server in background (detached); PID written to .code-intel/serve.pid").addHelpText("after", `
|
|
16871
17718
|
If a .code-intel/graph.db index already exists for the path, the server
|
|
16872
17719
|
loads the persisted graph directly and starts immediately \u2014 no re-analysis.
|
|
16873
17720
|
Use --force to discard the existing index and re-analyze from scratch.
|
|
16874
17721
|
|
|
17722
|
+
Background mode:
|
|
17723
|
+
$ code-intel serve --detach # start in background
|
|
17724
|
+
$ code-intel serve --detach --port 8080
|
|
17725
|
+
$ code-intel stop # stop the background server
|
|
17726
|
+
|
|
17727
|
+
PID file: <path>/.code-intel/serve.pid
|
|
17728
|
+
|
|
16875
17729
|
The web UI offers:
|
|
16876
17730
|
\xB7 Force-directed Knowledge Graph with color-coded node types
|
|
16877
17731
|
\xB7 BM25 text search + optional semantic (vector) search
|
|
@@ -16884,6 +17738,7 @@ program.command("serve").description("Start the local HTTP server + web UI for g
|
|
|
16884
17738
|
$ code-intel serve ./my-project
|
|
16885
17739
|
$ code-intel serve --port 8080
|
|
16886
17740
|
$ code-intel serve --force
|
|
17741
|
+
$ code-intel serve --detach
|
|
16887
17742
|
`).action(async (targetPath, options) => {
|
|
16888
17743
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16889
17744
|
const repoName = path39.basename(workspaceRoot);
|
|
@@ -16892,6 +17747,44 @@ program.command("serve").description("Start the local HTTP server + web UI for g
|
|
|
16892
17747
|
console.error(` \u2717 Path does not exist: ${workspaceRoot}`);
|
|
16893
17748
|
process.exit(1);
|
|
16894
17749
|
}
|
|
17750
|
+
if (options.detach) {
|
|
17751
|
+
const { spawn } = await import('child_process');
|
|
17752
|
+
const pidDir = path39.join(workspaceRoot, ".code-intel");
|
|
17753
|
+
const pidFile = path39.join(pidDir, "serve.pid");
|
|
17754
|
+
if (fs39.existsSync(pidFile)) {
|
|
17755
|
+
const existingPid = parseInt(fs39.readFileSync(pidFile, "utf-8").trim(), 10);
|
|
17756
|
+
try {
|
|
17757
|
+
process.kill(existingPid, 0);
|
|
17758
|
+
console.log(` \u26A0 Server already running (PID ${existingPid}). Stop it first with: code-intel stop`);
|
|
17759
|
+
process.exit(0);
|
|
17760
|
+
} catch {
|
|
17761
|
+
fs39.unlinkSync(pidFile);
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
const selfArgs = ["serve", targetPath, "--port", options.port];
|
|
17765
|
+
if (options.force) selfArgs.push("--force");
|
|
17766
|
+
const logDir = path39.join(os18.homedir(), ".code-intel", "logs");
|
|
17767
|
+
fs39.mkdirSync(logDir, { recursive: true });
|
|
17768
|
+
const logFile = path39.join(logDir, `serve-${Date.now()}.log`);
|
|
17769
|
+
const logFd = fs39.openSync(logFile, "a");
|
|
17770
|
+
const child = spawn(process.execPath, [process.argv[1], ...selfArgs], {
|
|
17771
|
+
detached: true,
|
|
17772
|
+
stdio: ["ignore", logFd, logFd],
|
|
17773
|
+
env: process.env
|
|
17774
|
+
});
|
|
17775
|
+
child.unref();
|
|
17776
|
+
fs39.mkdirSync(pidDir, { recursive: true });
|
|
17777
|
+
fs39.writeFileSync(pidFile, String(child.pid), "utf-8");
|
|
17778
|
+
console.log(` \u2705 Server started in background`);
|
|
17779
|
+
console.log(` PID : ${child.pid}`);
|
|
17780
|
+
console.log(` Port : ${options.port}`);
|
|
17781
|
+
console.log(` URL : http://localhost:${options.port}`);
|
|
17782
|
+
console.log(` Log : ${logFile}`);
|
|
17783
|
+
console.log(` PID file : ${pidFile}`);
|
|
17784
|
+
console.log(`
|
|
17785
|
+
Stop with: code-intel stop`);
|
|
17786
|
+
process.exit(0);
|
|
17787
|
+
}
|
|
16895
17788
|
const existingIndex = !options.force && fs39.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
|
|
16896
17789
|
if (existingIndex) {
|
|
16897
17790
|
const meta = loadMetadata(workspaceRoot);
|
|
@@ -16911,10 +17804,81 @@ program.command("serve").description("Start the local HTTP server + web UI for g
|
|
|
16911
17804
|
await startHttpServer(lazyGraph, repoName, parseInt(options.port, 10), workspaceRoot);
|
|
16912
17805
|
}
|
|
16913
17806
|
} else {
|
|
16914
|
-
|
|
16915
|
-
|
|
17807
|
+
console.log(` \u2139 No index found for: ${workspaceRoot}`);
|
|
17808
|
+
const registry = loadRegistry();
|
|
17809
|
+
const available = registry.filter((r) => fs39.existsSync(getDbPath(r.path)) && loadMetadata(r.path) !== null).sort((a, b) => new Date(b.indexedAt).getTime() - new Date(a.indexedAt).getTime());
|
|
17810
|
+
if (available.length > 0) {
|
|
17811
|
+
const fallback = available[0];
|
|
17812
|
+
console.log(` \u25C8 Falling back to most recently indexed repo: ${fallback.path}`);
|
|
17813
|
+
console.log(` (${fallback.stats.nodes} nodes \xB7 ${fallback.stats.edges} edges \xB7 indexed ${fallback.indexedAt})`);
|
|
17814
|
+
console.log(` \u2139 To index this folder run: code-intel analyze
|
|
17815
|
+
`);
|
|
17816
|
+
const fallbackMeta = loadMetadata(fallback.path);
|
|
17817
|
+
const fallbackDbPath = getDbPath(fallback.path);
|
|
17818
|
+
if (fallbackMeta.parser === "regex" || fallbackMeta.parser === void 0) {
|
|
17819
|
+
console.log(` \u26A0 Fallback index was built with regex parser. Starting with empty graph.`);
|
|
17820
|
+
const emptyGraph = createKnowledgeGraph();
|
|
17821
|
+
await startHttpServer(emptyGraph, fallback.name, parseInt(options.port, 10), fallback.path);
|
|
17822
|
+
} else {
|
|
17823
|
+
const lazyGraph = new LazyKnowledgeGraph();
|
|
17824
|
+
const db = new DbManager(fallbackDbPath, true);
|
|
17825
|
+
await db.init();
|
|
17826
|
+
await lazyGraph.init(db, fallbackMeta.stats.nodes, fallbackMeta.stats.edges);
|
|
17827
|
+
setImmediate(() => lazyGraph.warmTopNodes(500).catch(() => {
|
|
17828
|
+
}));
|
|
17829
|
+
await startHttpServer(lazyGraph, fallback.name, parseInt(options.port, 10), fallback.path);
|
|
17830
|
+
}
|
|
17831
|
+
if (available.length > 1) {
|
|
17832
|
+
console.log(`
|
|
17833
|
+
Other indexed repos available (switch via web UI or re-run serve with path):`);
|
|
17834
|
+
for (const r of available.slice(1)) {
|
|
17835
|
+
console.log(` code-intel serve ${r.path}`);
|
|
17836
|
+
}
|
|
17837
|
+
}
|
|
17838
|
+
} else {
|
|
17839
|
+
console.log(` \u2139 No indexed repositories found. Starting server with empty graph.`);
|
|
17840
|
+
console.log(` Run \`code-intel analyze\` to index a repository, then reload the UI.
|
|
17841
|
+
`);
|
|
17842
|
+
const emptyGraph = createKnowledgeGraph();
|
|
17843
|
+
await startHttpServer(emptyGraph, repoName, parseInt(options.port, 10), workspaceRoot);
|
|
17844
|
+
}
|
|
17845
|
+
}
|
|
17846
|
+
});
|
|
17847
|
+
program.command("stop").description("Stop a background server started with `code-intel serve --detach`").argument("[path]", "Project path whose server to stop (default: current directory)", ".").addHelpText("after", `
|
|
17848
|
+
Reads the PID from <path>/.code-intel/serve.pid and sends SIGTERM.
|
|
17849
|
+
The PID file is removed automatically after a successful stop.
|
|
17850
|
+
|
|
17851
|
+
Examples:
|
|
17852
|
+
$ code-intel stop
|
|
17853
|
+
$ code-intel stop ./my-project
|
|
17854
|
+
`).action((targetPath) => {
|
|
17855
|
+
const workspaceRoot = path39.resolve(targetPath);
|
|
17856
|
+
const pidFile = path39.join(workspaceRoot, ".code-intel", "serve.pid");
|
|
17857
|
+
if (!fs39.existsSync(pidFile)) {
|
|
17858
|
+
console.log(` \u2139 No background server found for: ${workspaceRoot}`);
|
|
17859
|
+
console.log(` (PID file not present: ${pidFile})`);
|
|
17860
|
+
process.exit(0);
|
|
17861
|
+
}
|
|
17862
|
+
const pid = parseInt(fs39.readFileSync(pidFile, "utf-8").trim(), 10);
|
|
17863
|
+
if (isNaN(pid)) {
|
|
17864
|
+
console.error(` \u2717 Invalid PID file: ${pidFile}`);
|
|
17865
|
+
fs39.unlinkSync(pidFile);
|
|
16916
17866
|
process.exit(1);
|
|
16917
17867
|
}
|
|
17868
|
+
try {
|
|
17869
|
+
process.kill(pid, "SIGTERM");
|
|
17870
|
+
fs39.unlinkSync(pidFile);
|
|
17871
|
+
console.log(` \u2705 Stopped server (PID ${pid})`);
|
|
17872
|
+
} catch (err) {
|
|
17873
|
+
const code = err.code;
|
|
17874
|
+
if (code === "ESRCH") {
|
|
17875
|
+
fs39.unlinkSync(pidFile);
|
|
17876
|
+
console.log(` \u2139 Server (PID ${pid}) was not running. Stale PID file removed.`);
|
|
17877
|
+
} else {
|
|
17878
|
+
console.error(` \u2717 Failed to stop server (PID ${pid}): ${err.message}`);
|
|
17879
|
+
process.exit(1);
|
|
17880
|
+
}
|
|
17881
|
+
}
|
|
16918
17882
|
});
|
|
16919
17883
|
program.command("watch").description("Start HTTP server + file watcher (auto-reindex on file changes)").argument("[path]", "Path to watch (default: current directory)", ".").option("-p, --port <port>", "Port to listen on", "4747").option("--force", "Force re-analysis even if an index already exists").addHelpText("after", `
|
|
16920
17884
|
Starts the HTTP server and automatically re-indexes changed files.
|
|
@@ -18079,7 +19043,7 @@ program.command("migrate").description("Manage database schema migrations").opti
|
|
|
18079
19043
|
$ code-intel migrate
|
|
18080
19044
|
$ code-intel migrate --rollback
|
|
18081
19045
|
`).action((opts) => {
|
|
18082
|
-
const dbPath = opts.db ?? path39.join(
|
|
19046
|
+
const dbPath = opts.db ?? path39.join(os18.homedir(), ".code-intel", "users.db");
|
|
18083
19047
|
if (!fs39.existsSync(dbPath)) {
|
|
18084
19048
|
console.error(`
|
|
18085
19049
|
\u2717 Database not found: ${dbPath}
|
|
@@ -18195,7 +19159,7 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
|
|
|
18195
19159
|
}
|
|
18196
19160
|
try {
|
|
18197
19161
|
const tokens = await pollDeviceFlow3(config, deviceResponse);
|
|
18198
|
-
const tokenPath = path39.join(
|
|
19162
|
+
const tokenPath = path39.join(os18.homedir(), ".code-intel", "oidc-token.json");
|
|
18199
19163
|
const tokenData = {
|
|
18200
19164
|
accessToken: tokens.accessToken,
|
|
18201
19165
|
refreshToken: tokens.refreshToken,
|
|
@@ -18216,7 +19180,7 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
|
|
|
18216
19180
|
}
|
|
18217
19181
|
});
|
|
18218
19182
|
authCmd.command("status").description("Show the current OIDC authentication status").action(() => {
|
|
18219
|
-
const tokenPath = path39.join(
|
|
19183
|
+
const tokenPath = path39.join(os18.homedir(), ".code-intel", "oidc-token.json");
|
|
18220
19184
|
if (!fs39.existsSync(tokenPath)) {
|
|
18221
19185
|
console.log("\n Not authenticated via OIDC. Run: code-intel auth login\n");
|
|
18222
19186
|
return;
|
|
@@ -18234,7 +19198,7 @@ authCmd.command("status").description("Show the current OIDC authentication stat
|
|
|
18234
19198
|
}
|
|
18235
19199
|
});
|
|
18236
19200
|
authCmd.command("logout").description("Remove locally stored OIDC token").action(() => {
|
|
18237
|
-
const tokenPath = path39.join(
|
|
19201
|
+
const tokenPath = path39.join(os18.homedir(), ".code-intel", "oidc-token.json");
|
|
18238
19202
|
if (fs39.existsSync(tokenPath)) {
|
|
18239
19203
|
fs39.unlinkSync(tokenPath);
|
|
18240
19204
|
console.log("\n \u2705 OIDC token removed. You are now logged out.\n");
|
|
@@ -18390,7 +19354,7 @@ ${err instanceof Error ? err.message : err}
|
|
|
18390
19354
|
});
|
|
18391
19355
|
(function ensurePermissions() {
|
|
18392
19356
|
try {
|
|
18393
|
-
const dir = path39.join(
|
|
19357
|
+
const dir = path39.join(os18.homedir(), ".code-intel");
|
|
18394
19358
|
secureMkdir(dir);
|
|
18395
19359
|
tightenDbFiles(dir);
|
|
18396
19360
|
} catch {
|
|
@@ -19195,6 +20159,35 @@ program.command("context").description("Build a token-efficient context document
|
|
|
19195
20159
|
console.log("");
|
|
19196
20160
|
}
|
|
19197
20161
|
});
|
|
20162
|
+
{
|
|
20163
|
+
const rewriteCmd = new Command("rewrite").description("Rewrite a shell command to its code-intel equivalent (used by hooks)").argument("<cmd>", "Raw shell command to check").addHelpText("after", `
|
|
20164
|
+
Exit codes:
|
|
20165
|
+
0 Rewrite found \u2014 rewritten command printed to stdout
|
|
20166
|
+
1 No code-intel equivalent \u2014 pass through unchanged
|
|
20167
|
+
|
|
20168
|
+
Examples:
|
|
20169
|
+
$ code-intel rewrite 'grep "AuthService" src/'
|
|
20170
|
+
code-intel search "AuthService"
|
|
20171
|
+
|
|
20172
|
+
$ code-intel rewrite 'git status'
|
|
20173
|
+
(no output, exit 1)
|
|
20174
|
+
`).action((cmd) => {
|
|
20175
|
+
runRewrite(cmd);
|
|
20176
|
+
});
|
|
20177
|
+
program.addCommand(rewriteCmd, { hidden: true });
|
|
20178
|
+
}
|
|
20179
|
+
{
|
|
20180
|
+
const hookCmd = new Command("hook").description("Run as a PreToolUse hook for an AI agent (reads/writes JSON on stdin/stdout)").argument("<agent>", "Agent type: claude").action((agent) => {
|
|
20181
|
+
if (agent === "claude") {
|
|
20182
|
+
runClaudeHook();
|
|
20183
|
+
} else {
|
|
20184
|
+
process.stderr.write(`[code-intel] Unknown hook agent: ${agent}
|
|
20185
|
+
`);
|
|
20186
|
+
process.exit(0);
|
|
20187
|
+
}
|
|
20188
|
+
});
|
|
20189
|
+
program.addCommand(hookCmd, { hidden: true });
|
|
20190
|
+
}
|
|
19198
20191
|
program.parse();
|
|
19199
20192
|
//# sourceMappingURL=main.js.map
|
|
19200
20193
|
//# sourceMappingURL=main.js.map
|