@dotdotgod/cli 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -2
- package/src/cli/usage.mjs +10 -0
- package/src/commands/embedding.mjs +33 -0
- package/src/core.mjs +4 -0
- package/src/query/embedder.mjs +2 -1
- package/src/query/embedding-runtime.mjs +61 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdotgod/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "CLI for dotdotgod validation, local documentation search, and graph indexing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Elastic-2.0",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@huggingface/transformers": "^4.2.0",
|
|
33
32
|
"leiden-ts": "^0.1.0"
|
|
34
33
|
},
|
|
35
34
|
"homepage": "https://github.com/dotdotgod/dotdotgod-kit/tree/main/packages/cli#readme",
|
package/src/cli/usage.mjs
CHANGED
|
@@ -36,6 +36,14 @@ Create dotdotgod.config.json from a bundled or user template. Without --template
|
|
|
36
36
|
dotdotgod query <root> <query> [--limit n] [--json]
|
|
37
37
|
|
|
38
38
|
Search shared project documentation with the resolved local or remote embedding provider.`;
|
|
39
|
+
case 'embedding':
|
|
40
|
+
return `Usage:
|
|
41
|
+
dotdotgod embedding status [<root>] [--json]
|
|
42
|
+
dotdotgod embedding install [<root>] --confirm [--json]`;
|
|
43
|
+
case 'embedding status':
|
|
44
|
+
return `Usage:\n dotdotgod embedding status [<root>] [--json]\n\nInspect the optional persistent local embedding runtime without network access.`;
|
|
45
|
+
case 'embedding install':
|
|
46
|
+
return `Usage:\n dotdotgod embedding install [<root>] --confirm [--json]\n\nAfter user approval, install the fixed optional runtime persistently. Uses network access and dependency install scripts; the model may download on first query.`;
|
|
39
47
|
case 'map':
|
|
40
48
|
return `Usage:
|
|
41
49
|
dotdotgod map <root> [--depth <positive-integer>] [--json]
|
|
@@ -78,6 +86,7 @@ Commands:
|
|
|
78
86
|
init Initialize project memory files.
|
|
79
87
|
config Inspect or initialize project configuration.
|
|
80
88
|
query Search project documentation.
|
|
89
|
+
embedding Inspect or install the optional local embedding runtime.
|
|
81
90
|
map Render the project documentation map.
|
|
82
91
|
resolve Resolve a project reference.
|
|
83
92
|
expand Expand references in a prompt.
|
|
@@ -137,6 +146,7 @@ export function helpCommandFromArgs(args) {
|
|
|
137
146
|
if (nonHelp[0] === 'graph' && nonHelp[1]) return `graph ${nonHelp[1]}`;
|
|
138
147
|
if (nonHelp[0] === 'traceability' && nonHelp[1]) return `traceability ${nonHelp[1]}`;
|
|
139
148
|
if (nonHelp[0] === 'config' && nonHelp[1] === 'init') return 'config init';
|
|
149
|
+
if (nonHelp[0] === 'embedding' && nonHelp[1]) return `embedding ${nonHelp[1]}`;
|
|
140
150
|
return nonHelp[0] ?? 'root';
|
|
141
151
|
}
|
|
142
152
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { usage } from '../cli/usage.mjs';
|
|
3
|
+
import { resolveEmbeddingProfile } from '../query/embedding-config.mjs';
|
|
4
|
+
import { embeddingRuntimeStatus, installEmbeddingRuntime } from '../query/embedding-runtime.mjs';
|
|
5
|
+
|
|
6
|
+
function parse(argv) {
|
|
7
|
+
const [action = 'status', ...rest] = argv;
|
|
8
|
+
let root = '.'; let json = false; let confirm = false;
|
|
9
|
+
for (const arg of rest) {
|
|
10
|
+
if (arg === '--json') json = true;
|
|
11
|
+
else if (arg === '--confirm') confirm = true;
|
|
12
|
+
else if (!arg.startsWith('-') && root === '.') root = arg;
|
|
13
|
+
else usage(`Unknown embedding option: ${arg}`, `embedding ${action}`);
|
|
14
|
+
}
|
|
15
|
+
return { action, root: resolve(root), json, confirm };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function runEmbedding(argv, options = {}) {
|
|
19
|
+
const parsed = parse(argv);
|
|
20
|
+
if (!['status', 'install'].includes(parsed.action)) usage(`Unknown embedding command: ${parsed.action}`, 'embedding');
|
|
21
|
+
const resolved = resolveEmbeddingProfile(parsed.root, options);
|
|
22
|
+
if (resolved.profile.provider !== 'local') {
|
|
23
|
+
const result = { ok: true, command: `embedding ${parsed.action}`, provider: resolved.profile.provider, required: false, installed: false, installAvailable: false };
|
|
24
|
+
console.log(parsed.json ? JSON.stringify(result, null, 2) : 'Local embedding runtime is not required for the configured remote provider.');
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
if (parsed.action === 'install' && !parsed.confirm) usage('embedding install requires --confirm after user approval.', 'embedding install');
|
|
28
|
+
const runtimeOptions = { home: options.home };
|
|
29
|
+
const result = parsed.action === 'status' ? embeddingRuntimeStatus(runtimeOptions) : installEmbeddingRuntime({ ...runtimeOptions, confirm: true, spawnImpl: options.spawnImpl, npmCommand: options.npmCommand });
|
|
30
|
+
const payload = { ...result, command: `embedding ${parsed.action}`, provider: 'local', required: true, ...(parsed.action === 'install' ? { disclosure: 'Installation uses network access and dependency install scripts; the configured model may download on first query.' } : {}) };
|
|
31
|
+
console.log(parsed.json ? JSON.stringify(payload, null, 2) : payload.installed ? `Embedding runtime installed (${payload.packageVersion}).` : 'Embedding runtime is not installed.');
|
|
32
|
+
return payload;
|
|
33
|
+
}
|
package/src/core.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { runValidate } from './validate/run.mjs';
|
|
|
4
4
|
import { runIndex, runStatus } from './index/cache.mjs';
|
|
5
5
|
import { runConfig } from './commands/config.mjs';
|
|
6
6
|
import { runQuery } from './commands/query.mjs';
|
|
7
|
+
import { runEmbedding } from './commands/embedding.mjs';
|
|
7
8
|
import { runMap } from './commands/map.mjs';
|
|
8
9
|
import { runResolve, runExpand } from './reference/resolve.mjs';
|
|
9
10
|
import { runTraceability } from './commands/traceability.mjs';
|
|
@@ -33,6 +34,8 @@ export { formatCompactImpactOutput, formatYmlGraphImpactError, formatYmlImpactOu
|
|
|
33
34
|
export { runConfig } from './commands/config.mjs';
|
|
34
35
|
export { runMap } from './commands/map.mjs';
|
|
35
36
|
export { buildVectorIndex, cosineScore, parseQueryOptions, queryDocumentation, rankVectorFiles, runQuery } from './commands/query.mjs';
|
|
37
|
+
export { runEmbedding } from './commands/embedding.mjs';
|
|
38
|
+
export { EmbeddingRuntimeMissingError, embeddingRuntimeRoot, embeddingRuntimeStatus, installEmbeddingRuntime, resolvePersistentTransformers } from './query/embedding-runtime.mjs';
|
|
36
39
|
export { chunkMarkdown, collectDocumentationChunks, collectDocumentationMarkdown, textFingerprint } from './query/chunks.mjs';
|
|
37
40
|
export { readVectorCache, VECTOR_DIMENSIONS, VECTOR_MODEL, VECTOR_SCHEMA_VERSION, profileFingerprint, vectorCachePaths, writeVectorCache } from './query/store.mjs';
|
|
38
41
|
export { DEFAULT_EMBEDDING_PROFILE, EMBEDDING_PROVIDERS, embeddingProfileIdentity, resolveEmbeddingProfile, sanitizeEmbeddingProfile, validateEmbeddingProfile } from './query/embedding-config.mjs';
|
|
@@ -54,6 +57,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
54
57
|
else if (command === 'config') runConfig(args);
|
|
55
58
|
else if (command === 'status') runStatus(args);
|
|
56
59
|
else if (command === 'query') await runQuery(args);
|
|
60
|
+
else if (command === 'embedding') runEmbedding(args);
|
|
57
61
|
else if (command === 'map') runMap(args);
|
|
58
62
|
else if (command === 'resolve') runResolve(args);
|
|
59
63
|
else if (command === 'expand') runExpand(args);
|
package/src/query/embedder.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { embeddingProfileIdentity, resolveEmbeddingProfile } from './embedding-config.mjs';
|
|
2
|
+
import { importTransformers } from './embedding-runtime.mjs';
|
|
2
3
|
|
|
3
4
|
const localPipelines = new Map();
|
|
4
5
|
|
|
@@ -11,7 +12,7 @@ function normalize(row) {
|
|
|
11
12
|
|
|
12
13
|
async function localEmbedder(profile) {
|
|
13
14
|
if (!localPipelines.has(profile.model)) {
|
|
14
|
-
localPipelines.set(profile.model,
|
|
15
|
+
localPipelines.set(profile.model, importTransformers().then(async ({ env, pipeline }) => {
|
|
15
16
|
env.allowLocalModels = true;
|
|
16
17
|
return pipeline('feature-extraction', profile.model);
|
|
17
18
|
}));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
export const EMBEDDING_RUNTIME_PACKAGE = '@huggingface/transformers';
|
|
9
|
+
export const EMBEDDING_RUNTIME_VERSION = '4.2.0';
|
|
10
|
+
|
|
11
|
+
export class EmbeddingRuntimeMissingError extends Error {
|
|
12
|
+
constructor() { super('Optional local embedding runtime is not installed.'); this.name = 'EmbeddingRuntimeMissingError'; this.code = 'EMBEDDING_RUNTIME_MISSING'; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function embeddingRuntimeRoot(options = {}) { return join(options.home ?? homedir(), '.dotdotgod', 'runtime', 'embedding'); }
|
|
16
|
+
|
|
17
|
+
export function resolvePersistentTransformers(options = {}) {
|
|
18
|
+
const root = embeddingRuntimeRoot(options);
|
|
19
|
+
try {
|
|
20
|
+
const require = createRequire(join(root, 'package.json'));
|
|
21
|
+
return require.resolve(EMBEDDING_RUNTIME_PACKAGE);
|
|
22
|
+
} catch { throw new EmbeddingRuntimeMissingError(); }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function embeddingRuntimeStatus(options = {}) {
|
|
26
|
+
const root = embeddingRuntimeRoot(options);
|
|
27
|
+
try {
|
|
28
|
+
const require = createRequire(join(root, 'package.json'));
|
|
29
|
+
const packagePath = require.resolve(`${EMBEDDING_RUNTIME_PACKAGE}/package.json`);
|
|
30
|
+
const version = JSON.parse(readFileSync(packagePath, 'utf8')).version;
|
|
31
|
+
return { ok: true, installed: true, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: version, location: root };
|
|
32
|
+
} catch {
|
|
33
|
+
return { ok: true, installed: false, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: null, location: root };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function installEmbeddingRuntime(options = {}) {
|
|
38
|
+
if (options.confirm !== true) throw new Error('Embedding runtime installation requires explicit confirmation.');
|
|
39
|
+
const root = embeddingRuntimeRoot(options);
|
|
40
|
+
const lock = join(root, '.installing');
|
|
41
|
+
if (existsSync(lock)) throw new Error('Embedding runtime installation is already in progress.');
|
|
42
|
+
mkdirSync(root, { recursive: true });
|
|
43
|
+
writeFileSync(lock, `${process.pid}\n`, { flag: 'wx' });
|
|
44
|
+
try {
|
|
45
|
+
const spawn = options.spawnImpl ?? spawnSync;
|
|
46
|
+
const result = spawn(options.npmCommand ?? 'npm', ['install', '--prefix', root, '--save-exact', '--omit=dev', '--no-audit', '--no-fund', `${EMBEDDING_RUNTIME_PACKAGE}@${EMBEDDING_RUNTIME_VERSION}`], { encoding: 'utf8', timeout: options.timeoutMs ?? 300_000, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
47
|
+
if (result.error || result.status !== 0) throw new Error(result.error?.code === 'ENOENT' ? 'npm is unavailable.' : 'Embedding runtime installation failed.');
|
|
48
|
+
const status = embeddingRuntimeStatus({ home: options.home });
|
|
49
|
+
if (!status.installed) throw new Error('Embedding runtime installation could not be verified.');
|
|
50
|
+
return { ...status, command: 'embedding install', modelDownloadPending: true };
|
|
51
|
+
} finally { rmSync(lock, { force: true }); }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function importTransformers(options = {}) {
|
|
55
|
+
try { return await import(EMBEDDING_RUNTIME_PACKAGE); }
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error?.code !== 'ERR_MODULE_NOT_FOUND') throw error;
|
|
58
|
+
const path = resolvePersistentTransformers(options);
|
|
59
|
+
return import(pathToFileURL(path).href);
|
|
60
|
+
}
|
|
61
|
+
}
|