vigthoria-cli 1.9.9 → 1.9.19

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.
Files changed (52) hide show
  1. package/README.md +5 -5
  2. package/dist/commands/auth.js +48 -65
  3. package/dist/commands/bridge.js +12 -19
  4. package/dist/commands/cancel.js +15 -22
  5. package/dist/commands/chat.d.ts +11 -0
  6. package/dist/commands/chat.js +404 -248
  7. package/dist/commands/config.js +31 -71
  8. package/dist/commands/deploy.js +83 -123
  9. package/dist/commands/device.d.ts +35 -0
  10. package/dist/commands/device.js +239 -0
  11. package/dist/commands/edit.js +32 -39
  12. package/dist/commands/explain.js +18 -25
  13. package/dist/commands/fork.js +22 -27
  14. package/dist/commands/generate.js +37 -44
  15. package/dist/commands/history.js +20 -25
  16. package/dist/commands/hub.js +95 -102
  17. package/dist/commands/index.js +41 -46
  18. package/dist/commands/legion.d.ts +1 -0
  19. package/dist/commands/legion.js +162 -209
  20. package/dist/commands/preview.js +60 -98
  21. package/dist/commands/replay.js +27 -32
  22. package/dist/commands/repo.js +103 -141
  23. package/dist/commands/review.js +29 -36
  24. package/dist/commands/security.js +5 -12
  25. package/dist/commands/update.js +15 -49
  26. package/dist/commands/workflow.d.ts +8 -1
  27. package/dist/commands/workflow.js +53 -19
  28. package/dist/index.js +409 -234
  29. package/dist/utils/api.d.ts +5 -0
  30. package/dist/utils/api.js +398 -176
  31. package/dist/utils/bridge-client.js +11 -52
  32. package/dist/utils/cli-state.d.ts +54 -0
  33. package/dist/utils/cli-state.js +185 -0
  34. package/dist/utils/config.d.ts +5 -0
  35. package/dist/utils/config.js +35 -14
  36. package/dist/utils/context-ranker.js +15 -21
  37. package/dist/utils/files.js +5 -42
  38. package/dist/utils/logger.js +42 -50
  39. package/dist/utils/post-write-validator.js +22 -29
  40. package/dist/utils/project-memory.d.ts +56 -0
  41. package/dist/utils/project-memory.js +289 -0
  42. package/dist/utils/session.d.ts +29 -3
  43. package/dist/utils/session.js +137 -85
  44. package/dist/utils/task-display.js +13 -20
  45. package/dist/utils/tools.d.ts +19 -0
  46. package/dist/utils/tools.js +84 -87
  47. package/dist/utils/workspace-cache.js +18 -26
  48. package/dist/utils/workspace-stream.js +26 -64
  49. package/install.ps1 +14 -0
  50. package/package.json +5 -3
  51. package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
  52. package/scripts/release/validate-no-go-gates.sh +2 -2
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Vigthoria CLI → DevTools Bridge Telemetry Client
4
3
  *
@@ -12,57 +11,18 @@
12
11
  * - Opt-in via --bridge <url> flag
13
12
  * - Sensitive data (API keys, tokens) is never transmitted
14
13
  */
15
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
- if (k2 === undefined) k2 = k;
17
- var desc = Object.getOwnPropertyDescriptor(m, k);
18
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
- desc = { enumerable: true, get: function() { return m[k]; } };
20
- }
21
- Object.defineProperty(o, k2, desc);
22
- }) : (function(o, m, k, k2) {
23
- if (k2 === undefined) k2 = k;
24
- o[k2] = m[k];
25
- }));
26
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
- Object.defineProperty(o, "default", { enumerable: true, value: v });
28
- }) : function(o, v) {
29
- o["default"] = v;
30
- });
31
- var __importStar = (this && this.__importStar) || (function () {
32
- var ownKeys = function(o) {
33
- ownKeys = Object.getOwnPropertyNames || function (o) {
34
- var ar = [];
35
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
- return ar;
37
- };
38
- return ownKeys(o);
39
- };
40
- return function (mod) {
41
- if (mod && mod.__esModule) return mod;
42
- var result = {};
43
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
- __setModuleDefault(result, mod);
45
- return result;
46
- };
47
- })();
48
- var __importDefault = (this && this.__importDefault) || function (mod) {
49
- return (mod && mod.__esModule) ? mod : { "default": mod };
50
- };
51
- Object.defineProperty(exports, "__esModule", { value: true });
52
- exports.BridgeClient = void 0;
53
- exports.getBridgeClient = getBridgeClient;
54
- const ws_1 = __importDefault(require("ws"));
55
- const os = __importStar(require("os"));
56
- const chalk_1 = __importDefault(require("chalk"));
57
- const logger_js_1 = require("./logger.js");
14
+ import WebSocket from 'ws';
15
+ import * as os from 'os';
16
+ import chalk from 'chalk';
17
+ import { CH } from './logger.js';
58
18
  // ── Singleton accessor ───────────────────────────────────────────────
59
19
  let _instance = null;
60
20
  /** Get the active bridge client (may be null if --bridge was not used). */
61
- function getBridgeClient() {
21
+ export function getBridgeClient() {
62
22
  return _instance;
63
23
  }
64
24
  // ── BridgeClient ─────────────────────────────────────────────────────
65
- class BridgeClient {
25
+ export class BridgeClient {
66
26
  ws = null;
67
27
  url;
68
28
  apiKey;
@@ -93,7 +53,7 @@ class BridgeClient {
93
53
  return;
94
54
  return new Promise((resolve) => {
95
55
  try {
96
- this.ws = new ws_1.default(this.url, { handshakeTimeout: 8000 });
56
+ this.ws = new WebSocket(this.url, { handshakeTimeout: 8000 });
97
57
  this.ws.on('open', () => {
98
58
  this.connected = true;
99
59
  this.reconnectDelay = 2000;
@@ -137,7 +97,7 @@ class BridgeClient {
137
97
  this.stopHeartbeat();
138
98
  this.scheduleReconnect();
139
99
  if (process.env.DEBUG || process.env.VIGTHORIA_DEBUG) {
140
- console.log(chalk_1.default.yellow(`${logger_js_1.CH.warn} Bridge: connection failed, will retry in background.`));
100
+ console.log(chalk.yellow(`${CH.warn} Bridge: connection failed, will retry in background.`));
141
101
  }
142
102
  resolve(); // resolve even on failure – must never block CLI
143
103
  });
@@ -225,7 +185,7 @@ class BridgeClient {
225
185
  clientId: this.clientId,
226
186
  };
227
187
  const json = JSON.stringify(event);
228
- if (this.connected && this.ws?.readyState === ws_1.default.OPEN) {
188
+ if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
229
189
  try {
230
190
  this.ws.send(json);
231
191
  }
@@ -238,7 +198,7 @@ class BridgeClient {
238
198
  }
239
199
  }
240
200
  sendRaw(obj) {
241
- if (this.ws?.readyState === ws_1.default.OPEN) {
201
+ if (this.ws?.readyState === WebSocket.OPEN) {
242
202
  this.ws.send(JSON.stringify(obj));
243
203
  }
244
204
  }
@@ -249,7 +209,7 @@ class BridgeClient {
249
209
  this.queue.push(json);
250
210
  }
251
211
  flushQueue() {
252
- while (this.queue.length > 0 && this.ws?.readyState === ws_1.default.OPEN) {
212
+ while (this.queue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
253
213
  const msg = this.queue.shift();
254
214
  try {
255
215
  this.ws.send(msg);
@@ -280,4 +240,3 @@ class BridgeClient {
280
240
  }
281
241
  }
282
242
  }
283
- exports.BridgeClient = BridgeClient;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Vigthoria CLI — shared local-state helpers.
3
+ *
4
+ * Single source for:
5
+ * • Offline-mode detection (VIGTHORIA_OFFLINE=1) — suppresses every
6
+ * background network call (update checks, gateway preflights, etc.).
7
+ * • Telemetry opt-out (VIGTHORIA_TELEMETRY_DISABLED=1).
8
+ * • Update-check cache, so `npm view` is not invoked on every CLI start.
9
+ * • Cross-platform safe file mode enforcement for tokens / sessions.
10
+ *
11
+ * No external network calls — pure local helpers. Safe to import from
12
+ * any command module.
13
+ */
14
+ /** 24h is plenty for an update-check ping. */
15
+ export declare const UPDATE_CHECK_TTL_MS: number;
16
+ /** 60s gateway-preflight cache so back-to-back CLI invocations stay fast. */
17
+ export declare const GATEWAY_PREFLIGHT_TTL_MS: number;
18
+ export interface CliStateShape {
19
+ updateCheck?: {
20
+ checkedAt: number;
21
+ latestVersion: string;
22
+ notifiedFor?: string;
23
+ };
24
+ gatewayPreflight?: {
25
+ checkedAt: number;
26
+ valid: boolean;
27
+ };
28
+ /** Last-seen version — used to render release notes on upgrade. */
29
+ lastSeenVersion?: string;
30
+ }
31
+ export declare function ensureStateDir(): void;
32
+ /** Best-effort lock-down: `0o600` on POSIX, no-op on Windows (NTFS ACL ≠ chmod). */
33
+ export declare function secureFileMode(filePath: string): void;
34
+ /** Atomic write helper — write to temp then rename, never half-written JSON. */
35
+ export declare function atomicWriteJson(targetPath: string, value: unknown, mode?: number): void;
36
+ export declare function loadCliState(): CliStateShape;
37
+ export declare function saveCliState(state: CliStateShape): void;
38
+ export declare function getCliStateFile(): string;
39
+ /**
40
+ * Offline mode — set VIGTHORIA_OFFLINE=1 to skip every non-essential
41
+ * outbound HTTP call. Useful for restricted networks, air-gapped runs,
42
+ * and deterministic CI.
43
+ */
44
+ export declare function isOfflineMode(): boolean;
45
+ /** Telemetry opt-out (also implied when offline mode is on). */
46
+ export declare function isTelemetryDisabled(): boolean;
47
+ /** When true the startup update check is suppressed (env override or offline). */
48
+ export declare function isUpdateCheckSuppressed(): boolean;
49
+ /** Cached read for the latest known version — null when stale or absent. */
50
+ export declare function readCachedLatestVersion(maxAgeMs?: number): string | null;
51
+ export declare function writeCachedLatestVersion(latestVersion: string, notifiedFor?: string): void;
52
+ export declare function readGatewayPreflightCache(maxAgeMs?: number): boolean | null;
53
+ export declare function writeGatewayPreflightCache(valid: boolean): void;
54
+ export declare function isSafeNpmPackageSpec(spec: string): boolean;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Vigthoria CLI — shared local-state helpers.
3
+ *
4
+ * Single source for:
5
+ * • Offline-mode detection (VIGTHORIA_OFFLINE=1) — suppresses every
6
+ * background network call (update checks, gateway preflights, etc.).
7
+ * • Telemetry opt-out (VIGTHORIA_TELEMETRY_DISABLED=1).
8
+ * • Update-check cache, so `npm view` is not invoked on every CLI start.
9
+ * • Cross-platform safe file mode enforcement for tokens / sessions.
10
+ *
11
+ * No external network calls — pure local helpers. Safe to import from
12
+ * any command module.
13
+ */
14
+ import * as fs from 'fs';
15
+ import * as os from 'os';
16
+ import * as path from 'path';
17
+ const STATE_DIR = path.join(os.homedir(), '.vigthoria');
18
+ const STATE_FILE = path.join(STATE_DIR, 'cli-state.json');
19
+ /** 24h is plenty for an update-check ping. */
20
+ export const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
21
+ /** 60s gateway-preflight cache so back-to-back CLI invocations stay fast. */
22
+ export const GATEWAY_PREFLIGHT_TTL_MS = 60 * 1000;
23
+ function readStateUnchecked() {
24
+ try {
25
+ if (!fs.existsSync(STATE_FILE)) {
26
+ return {};
27
+ }
28
+ const text = fs.readFileSync(STATE_FILE, 'utf8');
29
+ if (!text.trim()) {
30
+ return {};
31
+ }
32
+ const parsed = JSON.parse(text);
33
+ return parsed && typeof parsed === 'object' ? parsed : {};
34
+ }
35
+ catch {
36
+ // Corrupt state file should never crash the CLI — treat as empty.
37
+ return {};
38
+ }
39
+ }
40
+ export function ensureStateDir() {
41
+ try {
42
+ if (!fs.existsSync(STATE_DIR)) {
43
+ fs.mkdirSync(STATE_DIR, { recursive: true, mode: 0o700 });
44
+ }
45
+ if (process.platform !== 'win32') {
46
+ try {
47
+ fs.chmodSync(STATE_DIR, 0o700);
48
+ }
49
+ catch { /* best-effort */ }
50
+ }
51
+ }
52
+ catch {
53
+ // Best-effort — caller will see write failure later if it matters.
54
+ }
55
+ }
56
+ /** Best-effort lock-down: `0o600` on POSIX, no-op on Windows (NTFS ACL ≠ chmod). */
57
+ export function secureFileMode(filePath) {
58
+ if (process.platform === 'win32') {
59
+ return;
60
+ }
61
+ try {
62
+ fs.chmodSync(filePath, 0o600);
63
+ }
64
+ catch {
65
+ // File may not exist yet, or filesystem may not support chmod.
66
+ }
67
+ }
68
+ /** Atomic write helper — write to temp then rename, never half-written JSON. */
69
+ export function atomicWriteJson(targetPath, value, mode = 0o600) {
70
+ ensureStateDir();
71
+ const parent = path.dirname(targetPath);
72
+ try {
73
+ if (!fs.existsSync(parent))
74
+ fs.mkdirSync(parent, { recursive: true });
75
+ }
76
+ catch { /* ignore */ }
77
+ const tmpPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
78
+ fs.writeFileSync(tmpPath, JSON.stringify(value, null, 2), 'utf8');
79
+ if (process.platform !== 'win32') {
80
+ try {
81
+ fs.chmodSync(tmpPath, mode);
82
+ }
83
+ catch { /* ignore */ }
84
+ }
85
+ fs.renameSync(tmpPath, targetPath);
86
+ }
87
+ export function loadCliState() {
88
+ return readStateUnchecked();
89
+ }
90
+ export function saveCliState(state) {
91
+ atomicWriteJson(STATE_FILE, state);
92
+ }
93
+ export function getCliStateFile() {
94
+ return STATE_FILE;
95
+ }
96
+ /**
97
+ * Offline mode — set VIGTHORIA_OFFLINE=1 to skip every non-essential
98
+ * outbound HTTP call. Useful for restricted networks, air-gapped runs,
99
+ * and deterministic CI.
100
+ */
101
+ export function isOfflineMode() {
102
+ const flag = String(process.env.VIGTHORIA_OFFLINE || '').trim().toLowerCase();
103
+ return flag === '1' || flag === 'true' || flag === 'yes';
104
+ }
105
+ /** Telemetry opt-out (also implied when offline mode is on). */
106
+ export function isTelemetryDisabled() {
107
+ if (isOfflineMode())
108
+ return true;
109
+ const flag = String(process.env.VIGTHORIA_TELEMETRY_DISABLED || '').trim().toLowerCase();
110
+ return flag === '1' || flag === 'true' || flag === 'yes';
111
+ }
112
+ /** When true the startup update check is suppressed (env override or offline). */
113
+ export function isUpdateCheckSuppressed() {
114
+ if (isOfflineMode())
115
+ return true;
116
+ const flag = String(process.env.VIGTHORIA_NO_UPDATE_CHECK || '').trim();
117
+ return flag === '1';
118
+ }
119
+ /** Cached read for the latest known version — null when stale or absent. */
120
+ export function readCachedLatestVersion(maxAgeMs = UPDATE_CHECK_TTL_MS) {
121
+ const state = readStateUnchecked();
122
+ const entry = state.updateCheck;
123
+ if (!entry || !entry.latestVersion || typeof entry.checkedAt !== 'number') {
124
+ return null;
125
+ }
126
+ if (Date.now() - entry.checkedAt > maxAgeMs) {
127
+ return null;
128
+ }
129
+ return entry.latestVersion;
130
+ }
131
+ export function writeCachedLatestVersion(latestVersion, notifiedFor) {
132
+ try {
133
+ const state = readStateUnchecked();
134
+ state.updateCheck = {
135
+ checkedAt: Date.now(),
136
+ latestVersion,
137
+ notifiedFor: notifiedFor || state.updateCheck?.notifiedFor,
138
+ };
139
+ saveCliState(state);
140
+ }
141
+ catch {
142
+ // Caching is a nice-to-have, never block CLI on write failure.
143
+ }
144
+ }
145
+ export function readGatewayPreflightCache(maxAgeMs = GATEWAY_PREFLIGHT_TTL_MS) {
146
+ const state = readStateUnchecked();
147
+ const entry = state.gatewayPreflight;
148
+ if (!entry || typeof entry.checkedAt !== 'number' || typeof entry.valid !== 'boolean') {
149
+ return null;
150
+ }
151
+ if (Date.now() - entry.checkedAt > maxAgeMs) {
152
+ return null;
153
+ }
154
+ return entry.valid;
155
+ }
156
+ export function writeGatewayPreflightCache(valid) {
157
+ try {
158
+ const state = readStateUnchecked();
159
+ state.gatewayPreflight = { checkedAt: Date.now(), valid };
160
+ saveCliState(state);
161
+ }
162
+ catch {
163
+ // Best-effort.
164
+ }
165
+ }
166
+ /**
167
+ * Validate an npm package spec used by `vigthoria update --from <spec>`.
168
+ * Returns `true` when the spec is acceptable. Rejects whitespace and
169
+ * shell metacharacters so the spec can never break out of `execFile` /
170
+ * a Windows `shell:true` fallback into command injection.
171
+ */
172
+ const NPM_SPEC_ALLOWLIST = /^[A-Za-z0-9_./@:+\\=-]+$/;
173
+ const DISALLOWED_SHELL_CHARS = /[;&|`$<>(){}\[\]"'\s]/;
174
+ export function isSafeNpmPackageSpec(spec) {
175
+ const candidate = String(spec || '').trim();
176
+ if (!candidate)
177
+ return false;
178
+ if (candidate.length > 512)
179
+ return false;
180
+ if (DISALLOWED_SHELL_CHARS.test(candidate))
181
+ return false;
182
+ if (!NPM_SPEC_ALLOWLIST.test(candidate))
183
+ return false;
184
+ return true;
185
+ }
@@ -40,6 +40,11 @@ export declare class Config {
40
40
  private static readonly OPERATOR_PLANS;
41
41
  private static readonly CLOUD_PLANS;
42
42
  constructor();
43
+ /**
44
+ * Re-apply secure POSIX permissions to the on-disk config file. Safe
45
+ * to call repeatedly; no-op on Windows.
46
+ */
47
+ secureOnDisk(): void;
43
48
  get<K extends keyof VigthoriaCLIConfig>(key: K): VigthoriaCLIConfig[K];
44
49
  set<K extends keyof VigthoriaCLIConfig>(key: K, value: VigthoriaCLIConfig[K]): void;
45
50
  getAll(): VigthoriaCLIConfig;
@@ -1,13 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * Configuration Manager for Vigthoria CLI
4
3
  */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
7
- };
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Config = void 0;
10
- const conf_1 = __importDefault(require("conf"));
4
+ import Conf from 'conf';
5
+ import { secureFileMode } from './cli-state.js';
11
6
  const defaultConfig = {
12
7
  apiUrl: 'https://coder.vigthoria.io',
13
8
  modelsApiUrl: 'https://api.vigthoria.io', // Direct AI Models API - no proxying through Coder
@@ -45,14 +40,20 @@ const defaultConfig = {
45
40
  ],
46
41
  },
47
42
  };
48
- class Config {
43
+ export class Config {
49
44
  store;
50
- static OPERATOR_PLANS = new Set(['pro', 'professional', 'ultra', 'enterprise', 'admin', 'master_admin']);
45
+ static OPERATOR_PLANS = new Set(['enterprise', 'admin', 'master_admin']);
51
46
  static CLOUD_PLANS = new Set(['pro', 'professional', 'ultra', 'enterprise', 'master_admin', 'admin']);
52
47
  constructor() {
53
- this.store = new conf_1.default({
48
+ // `Conf` defaults to 0o644 on POSIX which would leave auth tokens
49
+ // readable by every local user. We post-process the file in
50
+ // `secureOnDisk()` to enforce 0o600 ownership. The `fileMode`
51
+ // option exists on newer `conf` releases — supply it via a cast so
52
+ // older type-defs do not reject the field.
53
+ this.store = new Conf({
54
54
  projectName: 'vigthoria-cli',
55
55
  defaults: defaultConfig,
56
+ ...{ fileMode: 0o600 },
56
57
  schema: {
57
58
  apiUrl: { type: 'string' },
58
59
  modelsApiUrl: { type: 'string' },
@@ -90,6 +91,23 @@ class Config {
90
91
  },
91
92
  },
92
93
  });
94
+ // Defence-in-depth: re-apply 0600 every time we instantiate Config so
95
+ // that an older install which created the file with 0644 gets locked
96
+ // down on next startup.
97
+ try {
98
+ secureFileMode(this.store.path);
99
+ }
100
+ catch { /* best-effort */ }
101
+ }
102
+ /**
103
+ * Re-apply secure POSIX permissions to the on-disk config file. Safe
104
+ * to call repeatedly; no-op on Windows.
105
+ */
106
+ secureOnDisk() {
107
+ try {
108
+ secureFileMode(this.store.path);
109
+ }
110
+ catch { /* best-effort */ }
93
111
  }
94
112
  get(key) {
95
113
  return this.store.get(key);
@@ -136,6 +154,10 @@ class Config {
136
154
  }
137
155
  this.store.set('userId', data.userId);
138
156
  this.store.set('email', data.email);
157
+ // Tighten permissions whenever the token rotates so a brand-new
158
+ // install (which may have created the file under a previous default
159
+ // umask) becomes 0600 immediately after first login.
160
+ this.secureOnDisk();
139
161
  }
140
162
  clearAuth() {
141
163
  this.store.set('authToken', null);
@@ -166,9 +188,9 @@ class Config {
166
188
  { id: 'agent', name: 'Vigthoria Agent GPU', description: 'Blackwell autonomous agent workflow', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
167
189
  { id: 'code', name: 'Vigthoria v3 Code 35B', description: 'Native 35B coding model on Blackwell', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
168
190
  { id: 'code-35b', name: 'Vigthoria v3 Code 35B', description: 'Same flagship model as code', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
169
- { id: 'code-9b', name: 'Vigthoria C1 Code 9B', description: 'Efficient coding specialist for quick tasks', tier: 'local', backendModel: 'vigthoria_c1_m' },
170
- { id: 'balanced', name: 'Vigthoria Master 7.6B', description: 'Balanced general-purpose local model', tier: 'local', backendModel: 'vigthoria_master' },
171
- { id: 'balanced-4b', name: 'Vigthoria v3 Balanced 4B', description: 'Efficient 4B general-purpose model (Qwen3.5-4B based)', tier: 'local', backendModel: 'vigthoria-balanced-4b' },
191
+ { id: 'code-9b', name: 'Vigthoria v3 Code 9B', description: 'Efficient coding specialist for quick tasks', tier: 'local', backendModel: 'vigthoria-v3-code-9b' },
192
+ { id: 'balanced', name: 'Vigthoria v3 Balanced 4B', description: 'Balanced general-purpose local model', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
193
+ { id: 'balanced-4b', name: 'Vigthoria v3 Balanced 4B', description: 'Efficient 4B general-purpose model (Qwen3.5-4B based)', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
172
194
  ];
173
195
  // ═══════════════════════════════════════════════════════════════
174
196
  // VIGTHORIA CLOUD - Premium cloud models (Pro subscription)
@@ -202,4 +224,3 @@ class Config {
202
224
  return this.hasOperatorAccess() && this.hasCloudAccess() && this.isComplexTask(prompt);
203
225
  }
204
226
  }
205
- exports.Config = Config;
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Vigthoria CLI — Semantic Context Ranker
4
3
  *
@@ -8,13 +7,8 @@
8
7
  *
9
8
  * No external dependencies — uses only Node.js built-ins.
10
9
  */
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.buildSemanticContext = buildSemanticContext;
16
- const node_fs_1 = __importDefault(require("node:fs"));
17
- const node_path_1 = __importDefault(require("node:path"));
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
18
12
  const IGNORED_DIRS = new Set([
19
13
  '.git', 'node_modules', 'dist', '.next', '__pycache__',
20
14
  '.vigthoria', 'coverage', '.cache', '.turbo', 'build', 'out',
@@ -45,8 +39,8 @@ function extractKeywords(prompt) {
45
39
  function scoreFile(filePath, keywords) {
46
40
  if (keywords.length === 0)
47
41
  return 0;
48
- const filename = node_path_1.default.basename(filePath).toLowerCase();
49
- const parentDir = node_path_1.default.basename(node_path_1.default.dirname(filePath)).toLowerCase();
42
+ const filename = path.basename(filePath).toLowerCase();
43
+ const parentDir = path.basename(path.dirname(filePath)).toLowerCase();
50
44
  let score = 0;
51
45
  for (const kw of keywords) {
52
46
  // Filename match (high weight)
@@ -57,7 +51,7 @@ function scoreFile(filePath, keywords) {
57
51
  score += 6;
58
52
  }
59
53
  try {
60
- const content = node_fs_1.default.readFileSync(filePath, 'utf-8').slice(0, 300000);
54
+ const content = fs.readFileSync(filePath, 'utf-8').slice(0, 300000);
61
55
  const lower = content.toLowerCase();
62
56
  for (const kw of keywords) {
63
57
  const escaped = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -73,14 +67,14 @@ function scoreFile(filePath, keywords) {
73
67
  function walkWorkspace(dir, maxFiles = 800) {
74
68
  const results = [];
75
69
  const stack = [dir];
76
- const workspaceRoot = node_path_1.default.resolve(dir);
70
+ const workspaceRoot = path.resolve(dir);
77
71
  while (stack.length > 0 && results.length < maxFiles) {
78
72
  const current = stack.pop();
79
73
  if (!current)
80
74
  continue;
81
75
  let entries;
82
76
  try {
83
- entries = node_fs_1.default.readdirSync(current, { withFileTypes: true });
77
+ entries = fs.readdirSync(current, { withFileTypes: true });
84
78
  }
85
79
  catch {
86
80
  continue;
@@ -91,12 +85,12 @@ function walkWorkspace(dir, maxFiles = 800) {
91
85
  break;
92
86
  if (IGNORED_DIRS.has(entry.name))
93
87
  continue;
94
- const fullPath = node_path_1.default.join(current, entry.name);
88
+ const fullPath = path.join(current, entry.name);
95
89
  // Security: skip symlinks that escape workspace root (fixes 8.2)
96
90
  if (entry.isSymbolicLink()) {
97
91
  try {
98
- const real = node_fs_1.default.realpathSync(fullPath);
99
- if (!real.startsWith(workspaceRoot + node_path_1.default.sep) && real !== workspaceRoot)
92
+ const real = fs.realpathSync(fullPath);
93
+ if (!real.startsWith(workspaceRoot + path.sep) && real !== workspaceRoot)
100
94
  continue;
101
95
  }
102
96
  catch {
@@ -106,7 +100,7 @@ function walkWorkspace(dir, maxFiles = 800) {
106
100
  if (entry.isDirectory()) {
107
101
  stack.push(fullPath);
108
102
  }
109
- else if (entry.isFile() && TEXT_EXTENSIONS.has(node_path_1.default.extname(entry.name).toLowerCase())) {
103
+ else if (entry.isFile() && TEXT_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
110
104
  results.push(fullPath);
111
105
  }
112
106
  }
@@ -117,8 +111,8 @@ function walkWorkspace(dir, maxFiles = 800) {
117
111
  * Score and rank workspace files by relevance to the given prompt.
118
112
  * Returns the top N files with path, score, and content snippet.
119
113
  */
120
- function buildSemanticContext(workspacePath, prompt, topN = 12) {
121
- if (!workspacePath || !node_fs_1.default.existsSync(workspacePath)) {
114
+ export function buildSemanticContext(workspacePath, prompt, topN = 12) {
115
+ if (!workspacePath || !fs.existsSync(workspacePath)) {
122
116
  return { topFiles: [], totalFilesScanned: 0, keywords: [] };
123
117
  }
124
118
  const keywords = extractKeywords(prompt);
@@ -134,11 +128,11 @@ function buildSemanticContext(workspacePath, prompt, topN = 12) {
134
128
  const topFiles = scored.map(({ filePath, score }) => {
135
129
  let snippet = '';
136
130
  try {
137
- snippet = node_fs_1.default.readFileSync(filePath, 'utf-8').slice(0, 600);
131
+ snippet = fs.readFileSync(filePath, 'utf-8').slice(0, 600);
138
132
  }
139
133
  catch { /* ignore */ }
140
134
  return {
141
- path: node_path_1.default.relative(workspacePath, filePath).replace(/\\/g, '/'),
135
+ path: path.relative(workspacePath, filePath).replace(/\\/g, '/'),
142
136
  score,
143
137
  snippet,
144
138
  };
@@ -1,46 +1,10 @@
1
- "use strict";
2
1
  /**
3
2
  * File utilities for Vigthoria CLI
4
3
  */
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
- Object.defineProperty(o, "default", { enumerable: true, value: v });
18
- }) : function(o, v) {
19
- o["default"] = v;
20
- });
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.FileUtils = void 0;
40
- const fs = __importStar(require("fs"));
41
- const path = __importStar(require("path"));
42
- const glob_1 = require("glob");
43
- class FileUtils {
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { glob } from 'glob';
7
+ export class FileUtils {
44
8
  projectRoot;
45
9
  ignorePatterns;
46
10
  constructor(projectRoot, ignorePatterns = []) {
@@ -130,7 +94,7 @@ class FileUtils {
130
94
  if (!fs.existsSync(this.projectRoot)) {
131
95
  return [];
132
96
  }
133
- const files = await (0, glob_1.glob)(pattern, {
97
+ const files = await glob(pattern, {
134
98
  cwd: this.projectRoot,
135
99
  ignore: this.ignorePatterns,
136
100
  nodir: true,
@@ -275,4 +239,3 @@ class FileUtils {
275
239
  return { added, removed };
276
240
  }
277
241
  }
278
- exports.FileUtils = FileUtils;