gitnexus 1.6.9 → 1.6.10-rc.10
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 +126 -41
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.js +106 -3
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +5 -0
- package/dist/cli/doctor.js +41 -3
- package/dist/cli/editor-targets.d.ts +17 -2
- package/dist/cli/editor-targets.js +44 -0
- package/dist/cli/embeddings.d.ts +12 -0
- package/dist/cli/embeddings.js +49 -0
- package/dist/cli/help-i18n.js +4 -0
- package/dist/cli/i18n/en.d.ts +5 -1
- package/dist/cli/i18n/en.js +5 -1
- package/dist/cli/i18n/resources.d.ts +9 -1
- package/dist/cli/i18n/zh-CN.d.ts +4 -0
- package/dist/cli/i18n/zh-CN.js +5 -1
- package/dist/cli/index.js +12 -1
- package/dist/cli/setup.js +204 -28
- package/dist/cli/uninstall.js +70 -16
- package/dist/core/embeddings/embedder.js +16 -2
- package/dist/core/embeddings/http-client.d.ts +28 -1
- package/dist/core/embeddings/http-client.js +107 -15
- package/dist/core/embeddings/node-module-compat.d.ts +23 -0
- package/dist/core/embeddings/node-module-compat.js +23 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +6 -3
- package/dist/core/embeddings/onnxruntime-node-resolver.js +31 -5
- package/dist/core/embeddings/runtime-install.d.ts +119 -0
- package/dist/core/embeddings/runtime-install.js +372 -0
- package/dist/core/embeddings/runtime-support.d.ts +48 -17
- package/dist/core/embeddings/runtime-support.js +96 -0
- package/dist/core/group/extractors/http-patterns/python.js +246 -108
- package/dist/core/group/extractors/manifest-extractor.js +4 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +9 -8
- package/dist/core/ingestion/cobol-processor.js +27 -26
- package/dist/core/ingestion/community-processor.d.ts +51 -1
- package/dist/core/ingestion/community-processor.js +316 -63
- package/dist/core/ingestion/emit-references.js +3 -2
- package/dist/core/ingestion/markdown-processor.js +3 -2
- package/dist/core/ingestion/parsing-processor.d.ts +3 -1
- package/dist/core/ingestion/parsing-processor.js +4 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +42 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +83 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.js +131 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.d.ts +75 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.js +299 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +12 -1
- package/dist/core/ingestion/utils/line-base.d.ts +20 -0
- package/dist/core/ingestion/utils/line-base.js +20 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +21 -0
- package/dist/core/ingestion/utils/symbol-labels.js +45 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +34 -0
- package/dist/core/ingestion/workers/parse-worker.js +39 -4
- package/dist/core/ingestion/workers/result-merge.js +4 -0
- package/dist/core/lbug/csv-generator.js +8 -2
- package/dist/core/lbug/extension-load-error.d.ts +67 -0
- package/dist/core/lbug/extension-load-error.js +320 -0
- package/dist/core/lbug/extension-loader.d.ts +17 -2
- package/dist/core/lbug/extension-loader.js +38 -13
- package/dist/core/lbug/lbug-adapter.js +7 -16
- package/dist/core/lbug/native-check.d.ts +22 -0
- package/dist/core/lbug/native-check.js +66 -0
- package/dist/core/lbug/pool-adapter.js +10 -1
- package/dist/core/lbug/sidecar-recovery.d.ts +32 -0
- package/dist/core/lbug/sidecar-recovery.js +91 -7
- package/dist/core/platform/capabilities.js +27 -1
- package/dist/core/run-analyze.js +33 -6
- package/dist/core/search/fts-indexes.d.ts +7 -0
- package/dist/core/search/fts-indexes.js +37 -0
- package/dist/mcp/core/embedder.js +16 -2
- package/dist/mcp/local/line-display.d.ts +22 -0
- package/dist/mcp/local/line-display.js +3 -0
- package/dist/mcp/local/local-backend.d.ts +14 -0
- package/dist/mcp/local/local-backend.js +63 -19
- package/dist/mcp/local/pdg-impact.d.ts +5 -3
- package/dist/mcp/local/pdg-impact.js +5 -2
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/server/api.js +2 -2
- package/dist/storage/parse-cache.js +1 -1
- package/dist/storage/repo-manager.d.ts +6 -1
- package/dist/storage/repo-manager.js +6 -1
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +50 -3
- package/hooks/claude/gitnexus-hook.cjs +59 -9
- package/package.json +5 -3
- package/scripts/cross-platform-tests.ts +26 -0
- package/scripts/ensure-fts.ts +32 -0
- package/scripts/install-duckdb-extension.mjs +85 -24
- package/scripts/run-cross-platform.ts +46 -6
- package/scripts/shard-arg.ts +30 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface EmbeddingsInstallOptions {
|
|
2
|
+
cuda?: boolean;
|
|
3
|
+
force?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `gitnexus embeddings install [--cuda] [--force]` — fetch the optional local
|
|
7
|
+
* embedding stack on demand (#2370). Goes through the user's npm registry
|
|
8
|
+
* config (mirrors/proxies apply); with --cuda it additionally runs
|
|
9
|
+
* onnxruntime-node's postinstall to download the CUDA GPU binaries from NuGet
|
|
10
|
+
* (set GLOBAL_AGENT_HTTPS_PROXY behind a proxy).
|
|
11
|
+
*/
|
|
12
|
+
export declare const embeddingsInstallCommand: (options?: EmbeddingsInstallOptions) => Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cliError, cliInfo, cliWarn } from './cli-message.js';
|
|
2
|
+
import { getEmbeddingRuntimeDir, getEmbeddingStackSpecs, installEmbeddingRuntime, isPrefixRuntimeLoadable, resolveEmbeddingRuntime, } from '../core/embeddings/runtime-install.js';
|
|
3
|
+
import { localEmbeddingPrefixUnloadableMessage } from '../core/embeddings/runtime-support.js';
|
|
4
|
+
/**
|
|
5
|
+
* `gitnexus embeddings install [--cuda] [--force]` — fetch the optional local
|
|
6
|
+
* embedding stack on demand (#2370). Goes through the user's npm registry
|
|
7
|
+
* config (mirrors/proxies apply); with --cuda it additionally runs
|
|
8
|
+
* onnxruntime-node's postinstall to download the CUDA GPU binaries from NuGet
|
|
9
|
+
* (set GLOBAL_AGENT_HTTPS_PROXY behind a proxy).
|
|
10
|
+
*/
|
|
11
|
+
export const embeddingsInstallCommand = async (options = {}) => {
|
|
12
|
+
const resolved = resolveEmbeddingRuntime();
|
|
13
|
+
if (resolved?.source === 'package' && !options.force) {
|
|
14
|
+
cliInfo('The embedding stack is already installed with gitnexus itself — nothing to do.\n' +
|
|
15
|
+
'(Use --force to install a copy into the runtime prefix anyway.)');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const specs = Object.entries(getEmbeddingStackSpecs())
|
|
19
|
+
.map(([name, spec]) => `${name}@${spec}`)
|
|
20
|
+
.join(', ');
|
|
21
|
+
cliInfo(`Installing ${specs} into ${getEmbeddingRuntimeDir()} …`);
|
|
22
|
+
cliInfo(options.cuda
|
|
23
|
+
? 'CUDA mode: onnxruntime-node will download GPU binaries from NuGet ' +
|
|
24
|
+
'(set GLOBAL_AGENT_HTTPS_PROXY=<proxy-url> behind a proxy).'
|
|
25
|
+
: 'CPU mode: install scripts are skipped — only your npm registry is contacted.');
|
|
26
|
+
try {
|
|
27
|
+
await installEmbeddingRuntime({ cuda: options.cuda, onOutput: (line) => cliInfo(` ${line}`) });
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
cliError(`${err instanceof Error ? err.message : String(err)}\n`, {
|
|
31
|
+
recoveryHint: 'local-embedding-stack-missing',
|
|
32
|
+
});
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const postInstall = resolveEmbeddingRuntime();
|
|
37
|
+
if (postInstall === null) {
|
|
38
|
+
cliInfo('✗ Install completed but the stack still does not resolve — check the output above.');
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (postInstall.source === 'runtime-prefix' && !isPrefixRuntimeLoadable()) {
|
|
43
|
+
// The packages are in the prefix, but this Node has no module.registerHooks
|
|
44
|
+
// to load them — don't claim readiness the loader can't honour.
|
|
45
|
+
cliWarn(`${localEmbeddingPrefixUnloadableMessage()}\n`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
cliInfo('✓ Embedding runtime installed. `gitnexus analyze --embeddings` is ready.');
|
|
49
|
+
};
|
package/dist/cli/help-i18n.js
CHANGED
|
@@ -17,6 +17,8 @@ const COMMAND_DESCRIPTION_KEYS = {
|
|
|
17
17
|
list: 'help.command.list.description',
|
|
18
18
|
status: 'help.command.status.description',
|
|
19
19
|
doctor: 'help.command.doctor.description',
|
|
20
|
+
embeddings: 'help.command.embeddings.description',
|
|
21
|
+
'embeddings install': 'help.command.embeddings.install.description',
|
|
20
22
|
clean: 'help.command.clean.description',
|
|
21
23
|
remove: 'help.command.remove.description',
|
|
22
24
|
wiki: 'help.command.wiki.description',
|
|
@@ -143,6 +145,8 @@ const OPTION_DESCRIPTION_KEYS = {
|
|
|
143
145
|
'eval-server|-p, --port <port>': 'help.option.port',
|
|
144
146
|
'eval-server|--host <host>': 'help.option.evalServer.host',
|
|
145
147
|
'eval-server|--idle-timeout <seconds>': 'help.option.evalServer.idleTimeout',
|
|
148
|
+
'embeddings install|--cuda': 'help.option.embeddings.install.cuda',
|
|
149
|
+
'embeddings install|--force': 'help.option.embeddings.install.force',
|
|
146
150
|
'group create|--force': 'help.option.group.create.force',
|
|
147
151
|
'group sync|--skip-embeddings': 'help.option.group.sync.skipEmbeddings',
|
|
148
152
|
'group sync|--exact-only': 'help.option.group.sync.exactOnly',
|
package/dist/cli/i18n/en.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ export declare const en: {
|
|
|
108
108
|
readonly 'help.command.help.description': "display help for command";
|
|
109
109
|
readonly 'help.option.help': "display help for command";
|
|
110
110
|
readonly 'help.option.version': "output the version number";
|
|
111
|
-
readonly 'help.command.setup.description': "One-time setup: configure MCP for Cursor, Claude Code, OpenCode, Codex";
|
|
111
|
+
readonly 'help.command.setup.description': "One-time setup: configure MCP for Cursor, Claude Code, Antigravity, OpenCode, CodeBuddy, Qoder, Codex";
|
|
112
112
|
readonly 'help.command.uninstall.description': "Reverse `setup`: remove GitNexus MCP entries, skills, and hooks from all detected editors";
|
|
113
113
|
readonly 'help.command.analyze.description': "Index a repository (full analysis)";
|
|
114
114
|
readonly 'help.command.index.description': "Register an existing .gitnexus/ folder into the global registry (no re-analysis needed)";
|
|
@@ -117,6 +117,8 @@ export declare const en: {
|
|
|
117
117
|
readonly 'help.command.list.description': "List all indexed repositories";
|
|
118
118
|
readonly 'help.command.status.description': "Show index status for current repo";
|
|
119
119
|
readonly 'help.command.doctor.description': "Show runtime platform capabilities and embedding configuration";
|
|
120
|
+
readonly 'help.command.embeddings.description': "Manage the on-demand local embedding runtime";
|
|
121
|
+
readonly 'help.command.embeddings.install.description': "Install the local embedding stack (@huggingface/transformers + onnxruntime-node) on demand. Heals installs where npm skipped the optional packages (e.g. behind an HTTP proxy, #2370). Downloads only from your configured npm registry — mirrors and proxies apply.";
|
|
120
122
|
readonly 'help.command.clean.description': "Delete GitNexus index for current repo";
|
|
121
123
|
readonly 'help.command.remove.description': "Delete the GitNexus index for a registered repo (by alias, name, or absolute path). Unlike `clean`, does not require being inside the repo. Idempotent on unknown targets.";
|
|
122
124
|
readonly 'help.command.wiki.description': "Generate repository wiki from knowledge graph";
|
|
@@ -220,6 +222,8 @@ export declare const en: {
|
|
|
220
222
|
readonly 'help.option.check.cycles': "Detect circular imports and fail when any are found";
|
|
221
223
|
readonly 'help.option.evalServer.host': "Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)";
|
|
222
224
|
readonly 'help.option.evalServer.idleTimeout': "Auto-shutdown after N seconds idle (0 = disabled)";
|
|
225
|
+
readonly 'help.option.embeddings.install.cuda': "Also download the CUDA GPU binaries (runs onnxruntime-node's NuGet postinstall; set GLOBAL_AGENT_HTTPS_PROXY behind a proxy)";
|
|
226
|
+
readonly 'help.option.embeddings.install.force': "Install into the runtime prefix even when the stack already resolves";
|
|
223
227
|
readonly 'help.option.group.create.force': "Overwrite existing group";
|
|
224
228
|
readonly 'help.option.group.sync.skipEmbeddings': "Exact + BM25 only (no embedding fallback)";
|
|
225
229
|
readonly 'help.option.group.sync.exactOnly': "Exact match only";
|
package/dist/cli/i18n/en.js
CHANGED
|
@@ -108,7 +108,7 @@ export const en = {
|
|
|
108
108
|
'help.command.help.description': 'display help for command',
|
|
109
109
|
'help.option.help': 'display help for command',
|
|
110
110
|
'help.option.version': 'output the version number',
|
|
111
|
-
'help.command.setup.description': 'One-time setup: configure MCP for Cursor, Claude Code, OpenCode, Codex',
|
|
111
|
+
'help.command.setup.description': 'One-time setup: configure MCP for Cursor, Claude Code, Antigravity, OpenCode, CodeBuddy, Qoder, Codex',
|
|
112
112
|
'help.command.uninstall.description': 'Reverse `setup`: remove GitNexus MCP entries, skills, and hooks from all detected editors',
|
|
113
113
|
'help.command.analyze.description': 'Index a repository (full analysis)',
|
|
114
114
|
'help.command.index.description': 'Register an existing .gitnexus/ folder into the global registry (no re-analysis needed)',
|
|
@@ -117,6 +117,8 @@ export const en = {
|
|
|
117
117
|
'help.command.list.description': 'List all indexed repositories',
|
|
118
118
|
'help.command.status.description': 'Show index status for current repo',
|
|
119
119
|
'help.command.doctor.description': 'Show runtime platform capabilities and embedding configuration',
|
|
120
|
+
'help.command.embeddings.description': 'Manage the on-demand local embedding runtime',
|
|
121
|
+
'help.command.embeddings.install.description': 'Install the local embedding stack (@huggingface/transformers + onnxruntime-node) on demand. Heals installs where npm skipped the optional packages (e.g. behind an HTTP proxy, #2370). Downloads only from your configured npm registry — mirrors and proxies apply.',
|
|
120
122
|
'help.command.clean.description': 'Delete GitNexus index for current repo',
|
|
121
123
|
'help.command.remove.description': 'Delete the GitNexus index for a registered repo (by alias, name, or absolute path). Unlike `clean`, does not require being inside the repo. Idempotent on unknown targets.',
|
|
122
124
|
'help.command.wiki.description': 'Generate repository wiki from knowledge graph',
|
|
@@ -220,6 +222,8 @@ export const en = {
|
|
|
220
222
|
'help.option.check.cycles': 'Detect circular imports and fail when any are found',
|
|
221
223
|
'help.option.evalServer.host': 'Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)',
|
|
222
224
|
'help.option.evalServer.idleTimeout': 'Auto-shutdown after N seconds idle (0 = disabled)',
|
|
225
|
+
'help.option.embeddings.install.cuda': "Also download the CUDA GPU binaries (runs onnxruntime-node's NuGet postinstall; set GLOBAL_AGENT_HTTPS_PROXY behind a proxy)",
|
|
226
|
+
'help.option.embeddings.install.force': 'Install into the runtime prefix even when the stack already resolves',
|
|
223
227
|
'help.option.group.create.force': 'Overwrite existing group',
|
|
224
228
|
'help.option.group.sync.skipEmbeddings': 'Exact + BM25 only (no embedding fallback)',
|
|
225
229
|
'help.option.group.sync.exactOnly': 'Exact match only',
|
|
@@ -109,7 +109,7 @@ export declare const cliResources: {
|
|
|
109
109
|
readonly 'help.command.help.description': "display help for command";
|
|
110
110
|
readonly 'help.option.help': "display help for command";
|
|
111
111
|
readonly 'help.option.version': "output the version number";
|
|
112
|
-
readonly 'help.command.setup.description': "One-time setup: configure MCP for Cursor, Claude Code, OpenCode, Codex";
|
|
112
|
+
readonly 'help.command.setup.description': "One-time setup: configure MCP for Cursor, Claude Code, Antigravity, OpenCode, CodeBuddy, Qoder, Codex";
|
|
113
113
|
readonly 'help.command.uninstall.description': "Reverse `setup`: remove GitNexus MCP entries, skills, and hooks from all detected editors";
|
|
114
114
|
readonly 'help.command.analyze.description': "Index a repository (full analysis)";
|
|
115
115
|
readonly 'help.command.index.description': "Register an existing .gitnexus/ folder into the global registry (no re-analysis needed)";
|
|
@@ -118,6 +118,8 @@ export declare const cliResources: {
|
|
|
118
118
|
readonly 'help.command.list.description': "List all indexed repositories";
|
|
119
119
|
readonly 'help.command.status.description': "Show index status for current repo";
|
|
120
120
|
readonly 'help.command.doctor.description': "Show runtime platform capabilities and embedding configuration";
|
|
121
|
+
readonly 'help.command.embeddings.description': "Manage the on-demand local embedding runtime";
|
|
122
|
+
readonly 'help.command.embeddings.install.description': "Install the local embedding stack (@huggingface/transformers + onnxruntime-node) on demand. Heals installs where npm skipped the optional packages (e.g. behind an HTTP proxy, #2370). Downloads only from your configured npm registry — mirrors and proxies apply.";
|
|
121
123
|
readonly 'help.command.clean.description': "Delete GitNexus index for current repo";
|
|
122
124
|
readonly 'help.command.remove.description': "Delete the GitNexus index for a registered repo (by alias, name, or absolute path). Unlike `clean`, does not require being inside the repo. Idempotent on unknown targets.";
|
|
123
125
|
readonly 'help.command.wiki.description': "Generate repository wiki from knowledge graph";
|
|
@@ -221,6 +223,8 @@ export declare const cliResources: {
|
|
|
221
223
|
readonly 'help.option.check.cycles': "Detect circular imports and fail when any are found";
|
|
222
224
|
readonly 'help.option.evalServer.host': "Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)";
|
|
223
225
|
readonly 'help.option.evalServer.idleTimeout': "Auto-shutdown after N seconds idle (0 = disabled)";
|
|
226
|
+
readonly 'help.option.embeddings.install.cuda': "Also download the CUDA GPU binaries (runs onnxruntime-node's NuGet postinstall; set GLOBAL_AGENT_HTTPS_PROXY behind a proxy)";
|
|
227
|
+
readonly 'help.option.embeddings.install.force': "Install into the runtime prefix even when the stack already resolves";
|
|
224
228
|
readonly 'help.option.group.create.force': "Overwrite existing group";
|
|
225
229
|
readonly 'help.option.group.sync.skipEmbeddings': "Exact + BM25 only (no embedding fallback)";
|
|
226
230
|
readonly 'help.option.group.sync.exactOnly': "Exact match only";
|
|
@@ -360,6 +364,8 @@ export declare const cliResources: {
|
|
|
360
364
|
'help.command.list.description': string;
|
|
361
365
|
'help.command.status.description': string;
|
|
362
366
|
'help.command.doctor.description': string;
|
|
367
|
+
'help.command.embeddings.description': string;
|
|
368
|
+
'help.command.embeddings.install.description': string;
|
|
363
369
|
'help.command.clean.description': string;
|
|
364
370
|
'help.command.remove.description': string;
|
|
365
371
|
'help.command.wiki.description': string;
|
|
@@ -463,6 +469,8 @@ export declare const cliResources: {
|
|
|
463
469
|
'help.option.check.cycles': string;
|
|
464
470
|
'help.option.evalServer.host': string;
|
|
465
471
|
'help.option.evalServer.idleTimeout': string;
|
|
472
|
+
'help.option.embeddings.install.cuda': string;
|
|
473
|
+
'help.option.embeddings.install.force': string;
|
|
466
474
|
'help.option.group.create.force': string;
|
|
467
475
|
'help.option.group.sync.skipEmbeddings': string;
|
|
468
476
|
'help.option.group.sync.exactOnly': string;
|
package/dist/cli/i18n/zh-CN.d.ts
CHANGED
|
@@ -117,6 +117,8 @@ export declare const zhCN: {
|
|
|
117
117
|
'help.command.list.description': string;
|
|
118
118
|
'help.command.status.description': string;
|
|
119
119
|
'help.command.doctor.description': string;
|
|
120
|
+
'help.command.embeddings.description': string;
|
|
121
|
+
'help.command.embeddings.install.description': string;
|
|
120
122
|
'help.command.clean.description': string;
|
|
121
123
|
'help.command.remove.description': string;
|
|
122
124
|
'help.command.wiki.description': string;
|
|
@@ -220,6 +222,8 @@ export declare const zhCN: {
|
|
|
220
222
|
'help.option.check.cycles': string;
|
|
221
223
|
'help.option.evalServer.host': string;
|
|
222
224
|
'help.option.evalServer.idleTimeout': string;
|
|
225
|
+
'help.option.embeddings.install.cuda': string;
|
|
226
|
+
'help.option.embeddings.install.force': string;
|
|
223
227
|
'help.option.group.create.force': string;
|
|
224
228
|
'help.option.group.sync.skipEmbeddings': string;
|
|
225
229
|
'help.option.group.sync.exactOnly': string;
|
package/dist/cli/i18n/zh-CN.js
CHANGED
|
@@ -108,7 +108,7 @@ export const zhCN = {
|
|
|
108
108
|
'help.command.help.description': '显示命令帮助',
|
|
109
109
|
'help.option.help': '显示命令帮助',
|
|
110
110
|
'help.option.version': '输出版本号',
|
|
111
|
-
'help.command.setup.description': '一次性设置:为 Cursor、Claude Code、OpenCode、Codex 配置 MCP',
|
|
111
|
+
'help.command.setup.description': '一次性设置:为 Cursor、Claude Code、Antigravity、OpenCode、CodeBuddy、Qoder、Codex 配置 MCP',
|
|
112
112
|
'help.command.uninstall.description': '撤销 `setup`:从所有检测到的编辑器中移除 GitNexus 的 MCP 配置、技能和钩子',
|
|
113
113
|
'help.command.analyze.description': '索引仓库(完整分析)',
|
|
114
114
|
'help.command.index.description': '将现有 .gitnexus/ 文件夹注册到全局注册表(无需重新分析)',
|
|
@@ -117,6 +117,8 @@ export const zhCN = {
|
|
|
117
117
|
'help.command.list.description': '列出所有已索引仓库',
|
|
118
118
|
'help.command.status.description': '显示当前仓库的索引状态',
|
|
119
119
|
'help.command.doctor.description': '显示运行平台能力和嵌入配置',
|
|
120
|
+
'help.command.embeddings.description': '管理按需安装的本地嵌入运行时',
|
|
121
|
+
'help.command.embeddings.install.description': '按需安装本地嵌入组件(@huggingface/transformers + onnxruntime-node)。修复 npm 跳过可选包的安装(例如在 HTTP 代理后,#2370)。仅从你配置的 npm registry 下载 — 镜像和代理均生效。',
|
|
120
122
|
'help.command.clean.description': '删除当前仓库的 GitNexus 索引',
|
|
121
123
|
'help.command.remove.description': '删除已注册仓库的 GitNexus 索引(按别名、名称或绝对路径)。与 `clean` 不同,不要求位于仓库内;未知目标会幂等处理。',
|
|
122
124
|
'help.command.wiki.description': '从知识图谱生成仓库 Wiki',
|
|
@@ -220,6 +222,8 @@ export const zhCN = {
|
|
|
220
222
|
'help.option.check.cycles': '检测循环导入,并在发现循环时失败',
|
|
221
223
|
'help.option.evalServer.host': '绑定地址(默认:127.0.0.1;用 0.0.0.0 暴露到所有网卡)',
|
|
222
224
|
'help.option.evalServer.idleTimeout': '空闲 N 秒后自动关闭(0 = 禁用)',
|
|
225
|
+
'help.option.embeddings.install.cuda': '同时下载 CUDA GPU 二进制文件(运行 onnxruntime-node 的 NuGet postinstall;代理后请设置 GLOBAL_AGENT_HTTPS_PROXY)',
|
|
226
|
+
'help.option.embeddings.install.force': '即使嵌入组件已可解析,也强制安装到运行时目录',
|
|
223
227
|
'help.option.group.create.force': '覆盖现有仓库组',
|
|
224
228
|
'help.option.group.sync.skipEmbeddings': '仅使用 exact + BM25(不使用嵌入回退)',
|
|
225
229
|
'help.option.group.sync.exactOnly': '仅精确匹配',
|
package/dist/cli/index.js
CHANGED
|
@@ -17,7 +17,7 @@ function collectCodingAgents(value, previous) {
|
|
|
17
17
|
program.name('gitnexus').description('GitNexus local CLI and MCP server').version(pkg.version);
|
|
18
18
|
program
|
|
19
19
|
.command('setup')
|
|
20
|
-
.description('One-time setup: configure MCP for Cursor, Claude Code, Antigravity, OpenCode, Codex')
|
|
20
|
+
.description('One-time setup: configure MCP for Cursor, Claude Code, Antigravity, OpenCode, CodeBuddy, Qoder, Codex')
|
|
21
21
|
.option('-c, --coding-agent <agents>', 'Configure only these coding agents (comma-separated or repeatable)', collectCodingAgents)
|
|
22
22
|
.action(createLazyAction(() => import('./setup.js'), 'setupCommand'));
|
|
23
23
|
program
|
|
@@ -153,6 +153,17 @@ program
|
|
|
153
153
|
.command('doctor')
|
|
154
154
|
.description('Show runtime platform capabilities and embedding configuration')
|
|
155
155
|
.action(createLazyAction(() => import('./doctor.js'), 'doctorCommand'));
|
|
156
|
+
program
|
|
157
|
+
.command('embeddings')
|
|
158
|
+
.description('Manage the on-demand local embedding runtime')
|
|
159
|
+
.command('install')
|
|
160
|
+
.description('Install the local embedding stack (@huggingface/transformers + onnxruntime-node) on demand. ' +
|
|
161
|
+
'Heals installs where npm skipped the optional packages (e.g. behind an HTTP proxy, #2370). ' +
|
|
162
|
+
'Downloads only from your configured npm registry — mirrors and proxies apply.')
|
|
163
|
+
.option('--cuda', "Also download the CUDA GPU binaries (runs onnxruntime-node's NuGet postinstall; " +
|
|
164
|
+
'set GLOBAL_AGENT_HTTPS_PROXY behind a proxy)')
|
|
165
|
+
.option('--force', 'Install into the runtime prefix even when the stack already resolves')
|
|
166
|
+
.action(createLazyAction(() => import('./embeddings.js'), 'embeddingsInstallCommand'));
|
|
156
167
|
program
|
|
157
168
|
.command('clean')
|
|
158
169
|
.description('Delete GitNexus index for current repo')
|
package/dist/cli/setup.js
CHANGED
|
@@ -14,7 +14,7 @@ import { promisify } from 'util';
|
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { parseTree, modify, applyEdits, parse as parseJsonc } from 'jsonc-parser';
|
|
16
16
|
import { getGlobalDir } from '../storage/repo-manager.js';
|
|
17
|
-
import { getEditorTargets, mcpTarget, skillTarget, hookTarget, detectIndentation, } from './editor-targets.js';
|
|
17
|
+
import { getEditorTargets, mcpTarget, skillTarget, hookTarget, detectIndentation, isEnoent, } from './editor-targets.js';
|
|
18
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
19
|
const __dirname = path.dirname(__filename);
|
|
20
20
|
const execFileAsync = promisify(execFile);
|
|
@@ -66,6 +66,8 @@ const CODING_AGENT_IDS = {
|
|
|
66
66
|
claude: 'claude',
|
|
67
67
|
antigravity: 'antigravity',
|
|
68
68
|
opencode: 'opencode',
|
|
69
|
+
codebuddy: 'codebuddy',
|
|
70
|
+
qoder: 'qoder',
|
|
69
71
|
codex: 'codex',
|
|
70
72
|
};
|
|
71
73
|
const SUPPORTED_CODING_AGENTS = Object.values(CODING_AGENT_IDS);
|
|
@@ -173,7 +175,13 @@ async function mergeJsoncFile(filePath, keyPath, value) {
|
|
|
173
175
|
try {
|
|
174
176
|
raw = await fs.readFile(filePath, 'utf-8');
|
|
175
177
|
}
|
|
176
|
-
catch {
|
|
178
|
+
catch (err) {
|
|
179
|
+
// Only an absent file means "start fresh". Any other read failure (EACCES,
|
|
180
|
+
// EIO, cloud-placeholder faults) must not be treated as empty — the write
|
|
181
|
+
// below would replace the user's existing config with a gitnexus-only
|
|
182
|
+
// document and report success. Rethrow into the per-editor catch instead.
|
|
183
|
+
if (!isEnoent(err))
|
|
184
|
+
throw err;
|
|
177
185
|
raw = '';
|
|
178
186
|
}
|
|
179
187
|
if (raw.trim().length === 0) {
|
|
@@ -207,6 +215,35 @@ async function dirExists(dirPath) {
|
|
|
207
215
|
return false;
|
|
208
216
|
}
|
|
209
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Detection probe: is there a non-empty regular file at this path?
|
|
220
|
+
* Swallows ALL errors (like dirExists) — detection gates run outside the
|
|
221
|
+
* per-editor try blocks, so a rethrowing probe would abort setup for every
|
|
222
|
+
* remaining editor. Size > 0 keeps detection aligned with the config-chain
|
|
223
|
+
* resolver: an empty config file is not evidence of an install, and treating
|
|
224
|
+
* it as one would route the write to a fresh file whose mkdir manufactures
|
|
225
|
+
* the editor's directory.
|
|
226
|
+
*/
|
|
227
|
+
async function isNonEmptyFile(filePath) {
|
|
228
|
+
try {
|
|
229
|
+
const stat = await fs.stat(filePath);
|
|
230
|
+
return stat.isFile() && stat.size > 0;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Detection probe: does any file in the target's MCP config chain look like an
|
|
238
|
+
* install trace? Always walks [file, ...legacyFiles] so an editor gaining
|
|
239
|
+
* legacyFiles later is automatically covered (CodeBuddy and Qoder share this —
|
|
240
|
+
* a per-editor copy is how the root-config-only detection gap crept in, see
|
|
241
|
+
* PR #2368 review I4).
|
|
242
|
+
*/
|
|
243
|
+
async function anyChainConfigFile(target) {
|
|
244
|
+
const hits = await Promise.all([target.file, ...(target.legacyFiles ?? [])].map(isNonEmptyFile));
|
|
245
|
+
return hits.includes(true);
|
|
246
|
+
}
|
|
210
247
|
// ─── Editor-specific setup ─────────────────────────────────────────
|
|
211
248
|
async function setupCursor(result) {
|
|
212
249
|
const cursorDir = path.join(os.homedir(), '.cursor');
|
|
@@ -287,7 +324,12 @@ async function mergeHooksJsonc(filePath, entries) {
|
|
|
287
324
|
try {
|
|
288
325
|
raw = await fs.readFile(filePath, 'utf-8');
|
|
289
326
|
}
|
|
290
|
-
catch {
|
|
327
|
+
catch (err) {
|
|
328
|
+
// Same contract as mergeJsoncFile: an unreadable (non-ENOENT) settings
|
|
329
|
+
// file must not be rewritten as hooks-only — that would destroy every
|
|
330
|
+
// user setting in it. Rethrow into the hook installer's catch.
|
|
331
|
+
if (!isEnoent(err))
|
|
332
|
+
throw err;
|
|
291
333
|
raw = '';
|
|
292
334
|
}
|
|
293
335
|
if (raw.trim().length === 0) {
|
|
@@ -365,20 +407,26 @@ export async function copyHookHelpers(srcDir, destDir, label, result) {
|
|
|
365
407
|
return failedRequired;
|
|
366
408
|
}
|
|
367
409
|
/**
|
|
368
|
-
* Install GitNexus hooks
|
|
369
|
-
*
|
|
370
|
-
*
|
|
410
|
+
* Install GitNexus hooks for editors that use Claude Code's hooks schema.
|
|
411
|
+
*
|
|
412
|
+
* Claude Code registers hooks in ~/.claude/settings.json; Codex uses a
|
|
413
|
+
* dedicated ~/.codex/hooks.json with the identical {hooks: {Event: [...]}}
|
|
414
|
+
* JSON shape, stdin payload, and hookSpecificOutput response contract
|
|
415
|
+
* (https://developers.openai.com/codex/hooks), so both runtimes share this
|
|
416
|
+
* installer and the same bundled adapter script. Merges hook config without
|
|
417
|
+
* overwriting existing hooks, preserving comments and formatting.
|
|
371
418
|
*/
|
|
372
|
-
async function
|
|
373
|
-
const
|
|
374
|
-
|
|
419
|
+
async function installClaudeSchemaHooks(result, id) {
|
|
420
|
+
const hookCfg = hookTarget(id);
|
|
421
|
+
const settingsPath = hookCfg.settingsFile;
|
|
422
|
+
const label = `${hookCfg.label} hooks`;
|
|
423
|
+
// Gate on the editor's own config dir (~/.claude, ~/.codex) existing.
|
|
424
|
+
if (!(await dirExists(path.dirname(settingsPath))))
|
|
375
425
|
return;
|
|
376
|
-
const claudeHook = hookTarget('claude');
|
|
377
|
-
const settingsPath = claudeHook.settingsFile;
|
|
378
426
|
// Source hooks bundled within the gitnexus package (hooks/claude/)
|
|
379
427
|
const pluginHooksPath = path.join(__dirname, '..', '..', 'hooks', 'claude');
|
|
380
|
-
// Copy unified hook script to
|
|
381
|
-
const destHooksDir =
|
|
428
|
+
// Copy unified hook script to the editor's hooks/gitnexus/ dir
|
|
429
|
+
const destHooksDir = hookCfg.scriptDir;
|
|
382
430
|
try {
|
|
383
431
|
await fs.mkdir(destHooksDir, { recursive: true });
|
|
384
432
|
const src = path.join(pluginHooksPath, 'gitnexus-hook.cjs');
|
|
@@ -389,7 +437,7 @@ async function installClaudeCodeHooks(result) {
|
|
|
389
437
|
const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
|
|
390
438
|
const jsonCli = JSON.stringify(normalizedCli);
|
|
391
439
|
if (!content.includes(CLI_PATH_SOURCE_LITERAL)) {
|
|
392
|
-
result.errors.push(
|
|
440
|
+
result.errors.push(`${label}: gitnexus-hook.cjs no longer contains the cliPath literal to patch — the installed hook may fail to resolve the CLI. Update CLI_PATH_SOURCE_LITERAL in setup.ts.`);
|
|
393
441
|
}
|
|
394
442
|
content = content.replace(CLI_PATH_SOURCE_LITERAL, `let cliPath = ${jsonCli};`);
|
|
395
443
|
await fs.writeFile(dest, content, 'utf-8');
|
|
@@ -404,12 +452,12 @@ async function installClaudeCodeHooks(result) {
|
|
|
404
452
|
await fs.access(dest);
|
|
405
453
|
}
|
|
406
454
|
catch {
|
|
407
|
-
result.errors.push(
|
|
455
|
+
result.errors.push(`${label}: adapter script was not installed — skipping hook registration`);
|
|
408
456
|
return;
|
|
409
457
|
}
|
|
410
|
-
const failedRequired = await copyHookHelpers(pluginHooksPath, destHooksDir,
|
|
458
|
+
const failedRequired = await copyHookHelpers(pluginHooksPath, destHooksDir, label, result);
|
|
411
459
|
if (failedRequired.length > 0) {
|
|
412
|
-
result.errors.push(
|
|
460
|
+
result.errors.push(`${label}: required helper(s) ${failedRequired.join(', ')} failed to copy — skipping hook registration`);
|
|
413
461
|
return;
|
|
414
462
|
}
|
|
415
463
|
const hookPath = path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/');
|
|
@@ -425,9 +473,10 @@ async function installClaudeCodeHooks(result) {
|
|
|
425
473
|
}
|
|
426
474
|
})();
|
|
427
475
|
const hookEntries = [];
|
|
428
|
-
// NOTE: SessionStart hooks are broken on Windows (Claude Code bug #23576)
|
|
429
|
-
// Session context is delivered via
|
|
430
|
-
|
|
476
|
+
// NOTE: SessionStart hooks are broken on Windows (Claude Code bug #23576),
|
|
477
|
+
// and Codex reads AGENTS.md natively. Session context is delivered via
|
|
478
|
+
// CLAUDE.md / AGENTS.md / skills instead.
|
|
479
|
+
if (!hasGitnexusHook(parsed?.hooks, 'PreToolUse', hookCfg.needle)) {
|
|
431
480
|
hookEntries.push({
|
|
432
481
|
eventName: 'PreToolUse',
|
|
433
482
|
value: {
|
|
@@ -443,7 +492,7 @@ async function installClaudeCodeHooks(result) {
|
|
|
443
492
|
},
|
|
444
493
|
});
|
|
445
494
|
}
|
|
446
|
-
if (!hasGitnexusHook(parsed?.hooks, 'PostToolUse',
|
|
495
|
+
if (!hasGitnexusHook(parsed?.hooks, 'PostToolUse', hookCfg.needle)) {
|
|
447
496
|
hookEntries.push({
|
|
448
497
|
eventName: 'PostToolUse',
|
|
449
498
|
value: {
|
|
@@ -460,19 +509,19 @@ async function installClaudeCodeHooks(result) {
|
|
|
460
509
|
});
|
|
461
510
|
}
|
|
462
511
|
if (hookEntries.length === 0) {
|
|
463
|
-
result.configured.push(
|
|
512
|
+
result.configured.push(`${label} (already configured)`);
|
|
464
513
|
return;
|
|
465
514
|
}
|
|
466
515
|
const ok = await mergeHooksJsonc(settingsPath, hookEntries);
|
|
467
516
|
if (ok) {
|
|
468
|
-
result.configured.push(
|
|
517
|
+
result.configured.push(`${label} (PreToolUse, PostToolUse)`);
|
|
469
518
|
}
|
|
470
519
|
else {
|
|
471
|
-
result.errors.push(
|
|
520
|
+
result.errors.push(`${label}: ${path.basename(settingsPath)} is corrupt — skipping to preserve existing content`);
|
|
472
521
|
}
|
|
473
522
|
}
|
|
474
523
|
catch (err) {
|
|
475
|
-
result.errors.push(
|
|
524
|
+
result.errors.push(`${label}: ${err.message}`);
|
|
476
525
|
}
|
|
477
526
|
}
|
|
478
527
|
// ─── Antigravity (Google) ──────────────────────────────────────────
|
|
@@ -660,6 +709,118 @@ async function setupOpenCode(result) {
|
|
|
660
709
|
result.errors.push(`OpenCode: ${err.message}`);
|
|
661
710
|
}
|
|
662
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Resolve which config file in a target's [file, ...legacyFiles] priority
|
|
714
|
+
* chain setup should write into: the first that exists, else the recommended
|
|
715
|
+
* `file`. CodeBuddy reads only the first existing file in its chain, so
|
|
716
|
+
* creating the recommended file above a populated deprecated one would shadow
|
|
717
|
+
* the user's existing MCP servers.
|
|
718
|
+
*/
|
|
719
|
+
async function resolveMcpConfigFile(target) {
|
|
720
|
+
for (const candidate of [target.file, ...(target.legacyFiles ?? [])]) {
|
|
721
|
+
try {
|
|
722
|
+
const stat = await fs.stat(candidate);
|
|
723
|
+
// Non-empty regular files only: a 0-byte recommended file must not
|
|
724
|
+
// shadow a populated deprecated one (mergeJsoncFile treats empty as a
|
|
725
|
+
// fresh document anyway), and directories are never config candidates.
|
|
726
|
+
if (stat.isFile() && stat.size > 0)
|
|
727
|
+
return candidate;
|
|
728
|
+
}
|
|
729
|
+
catch (err) {
|
|
730
|
+
// ENOENT = candidate absent — try the next one. Anything else (EACCES
|
|
731
|
+
// on the file or a parent) is surfaced: silently skipping could route
|
|
732
|
+
// the write to a lower-priority file the editor never reads.
|
|
733
|
+
if (!isEnoent(err))
|
|
734
|
+
throw err;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return target.file;
|
|
738
|
+
}
|
|
739
|
+
async function setupCodeBuddy(result) {
|
|
740
|
+
const codebuddyDir = path.join(os.homedir(), '.codebuddy');
|
|
741
|
+
const target = mcpTarget('codebuddy');
|
|
742
|
+
// Installed = the config dir exists OR any registered MCP config file does.
|
|
743
|
+
// A user whose only trace is a root-level config (e.g. a legacy
|
|
744
|
+
// ~/.codebuddy.json) still gets configured — uninstall already handles that
|
|
745
|
+
// shape, so setup skipping it was an asymmetry (PR #2368 review I4).
|
|
746
|
+
if (!(await dirExists(codebuddyDir)) && !(await anyChainConfigFile(target))) {
|
|
747
|
+
result.skipped.push('CodeBuddy (not installed)');
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
const configFile = await resolveMcpConfigFile(target);
|
|
752
|
+
const ok = await mergeJsoncFile(configFile, target.keyPath, getMcpEntry());
|
|
753
|
+
if (ok) {
|
|
754
|
+
result.configured.push('CodeBuddy');
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
result.errors.push(`CodeBuddy: ${path.basename(configFile)} is corrupt — skipping to preserve existing content`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
catch (err) {
|
|
761
|
+
result.errors.push(`CodeBuddy: ${err instanceof Error ? err.message : String(err)}`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
async function setupQoder(result) {
|
|
765
|
+
const qoderDir = path.join(os.homedir(), '.qoder');
|
|
766
|
+
const target = mcpTarget('qoder');
|
|
767
|
+
const { file: mcpPath, keyPath } = target;
|
|
768
|
+
// Same chain-aware detection as CodeBuddy: ~/.qoder.json alone counts.
|
|
769
|
+
if (!(await dirExists(qoderDir)) && !(await anyChainConfigFile(target))) {
|
|
770
|
+
result.skipped.push('Qoder (not installed)');
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
try {
|
|
774
|
+
const ok = await mergeJsoncFile(mcpPath, keyPath, getMcpEntry());
|
|
775
|
+
if (ok) {
|
|
776
|
+
result.configured.push('Qoder');
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
result.errors.push('Qoder: .qoder.json is corrupt — skipping to preserve existing content');
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
catch (err) {
|
|
783
|
+
result.errors.push(`Qoder: ${err instanceof Error ? err.message : String(err)}`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Install global CodeBuddy skills to ~/.codebuddy/skills/
|
|
788
|
+
* (https://www.codebuddy.ai/docs/cli/skills — same SKILL.md layout as Claude Code).
|
|
789
|
+
*/
|
|
790
|
+
async function installCodeBuddySkills(result) {
|
|
791
|
+
const codebuddyDir = path.join(os.homedir(), '.codebuddy');
|
|
792
|
+
if (!(await dirExists(codebuddyDir)))
|
|
793
|
+
return;
|
|
794
|
+
const skillsDir = skillTarget('codebuddy').dir;
|
|
795
|
+
try {
|
|
796
|
+
const installed = await installSkillsTo(skillsDir);
|
|
797
|
+
if (installed.length > 0) {
|
|
798
|
+
result.configured.push(`CodeBuddy skills (${installed.length} skills → ~/.codebuddy/skills/)`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
catch (err) {
|
|
802
|
+
result.errors.push(`CodeBuddy skills: ${err instanceof Error ? err.message : String(err)}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Install global Qoder skills to ~/.qoder/skills/
|
|
807
|
+
* (https://docs.qoder.com/extensions/skills — same SKILL.md layout as Claude Code).
|
|
808
|
+
*/
|
|
809
|
+
async function installQoderSkills(result) {
|
|
810
|
+
const qoderDir = path.join(os.homedir(), '.qoder');
|
|
811
|
+
if (!(await dirExists(qoderDir)))
|
|
812
|
+
return;
|
|
813
|
+
const skillsDir = skillTarget('qoder').dir;
|
|
814
|
+
try {
|
|
815
|
+
const installed = await installSkillsTo(skillsDir);
|
|
816
|
+
if (installed.length > 0) {
|
|
817
|
+
result.configured.push(`Qoder skills (${installed.length} skills → ~/.qoder/skills/)`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
catch (err) {
|
|
821
|
+
result.errors.push(`Qoder skills: ${err instanceof Error ? err.message : String(err)}`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
663
824
|
/**
|
|
664
825
|
* Build a TOML section for Codex MCP config (~/.codex/config.toml).
|
|
665
826
|
*/
|
|
@@ -677,7 +838,12 @@ async function upsertCodexConfigToml(configPath) {
|
|
|
677
838
|
try {
|
|
678
839
|
existing = await fs.readFile(configPath, 'utf-8');
|
|
679
840
|
}
|
|
680
|
-
catch {
|
|
841
|
+
catch (err) {
|
|
842
|
+
// TOML variant of the mergeJsoncFile contract: treating a non-ENOENT read
|
|
843
|
+
// failure as an empty config would rewrite config.toml with only the
|
|
844
|
+
// gitnexus section. Rethrow into setupCodex's catch.
|
|
845
|
+
if (!isEnoent(err))
|
|
846
|
+
throw err;
|
|
681
847
|
existing = '';
|
|
682
848
|
}
|
|
683
849
|
if (existing.includes(`[${getEditorTargets().codex.tomlSection}]`)) {
|
|
@@ -885,12 +1051,16 @@ export const setupCommand = async (options) => {
|
|
|
885
1051
|
await setupAntigravity(result);
|
|
886
1052
|
if (selected.has('opencode'))
|
|
887
1053
|
await setupOpenCode(result);
|
|
1054
|
+
if (selected.has('codebuddy'))
|
|
1055
|
+
await setupCodeBuddy(result);
|
|
1056
|
+
if (selected.has('qoder'))
|
|
1057
|
+
await setupQoder(result);
|
|
888
1058
|
if (selected.has('codex'))
|
|
889
1059
|
await setupCodex(result);
|
|
890
1060
|
// Install global skills for platforms that support them
|
|
891
1061
|
if (selected.has('claude')) {
|
|
892
1062
|
await installClaudeCodeSkills(result);
|
|
893
|
-
await
|
|
1063
|
+
await installClaudeSchemaHooks(result, 'claude');
|
|
894
1064
|
}
|
|
895
1065
|
if (selected.has('antigravity')) {
|
|
896
1066
|
await installAntigravitySkills(result);
|
|
@@ -900,8 +1070,14 @@ export const setupCommand = async (options) => {
|
|
|
900
1070
|
await installCursorSkills(result);
|
|
901
1071
|
if (selected.has('opencode'))
|
|
902
1072
|
await installOpenCodeSkills(result);
|
|
903
|
-
if (selected.has('
|
|
1073
|
+
if (selected.has('codebuddy'))
|
|
1074
|
+
await installCodeBuddySkills(result);
|
|
1075
|
+
if (selected.has('qoder'))
|
|
1076
|
+
await installQoderSkills(result);
|
|
1077
|
+
if (selected.has('codex')) {
|
|
904
1078
|
await installCodexSkills(result);
|
|
1079
|
+
await installClaudeSchemaHooks(result, 'codex');
|
|
1080
|
+
}
|
|
905
1081
|
// Print results
|
|
906
1082
|
if (result.configured.length > 0) {
|
|
907
1083
|
console.log(' Configured:');
|