vigthoria-cli 1.10.47 → 1.10.48

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 (55) hide show
  1. package/dist/commands/auth.js +51 -68
  2. package/dist/commands/bridge.js +12 -19
  3. package/dist/commands/cancel.js +15 -22
  4. package/dist/commands/chat.d.ts +28 -0
  5. package/dist/commands/config.js +33 -73
  6. package/dist/commands/deploy.js +83 -123
  7. package/dist/commands/device.js +21 -61
  8. package/dist/commands/edit.js +32 -39
  9. package/dist/commands/explain.js +18 -25
  10. package/dist/commands/fork.d.ts +17 -0
  11. package/dist/commands/fork.js +164 -0
  12. package/dist/commands/generate.js +37 -44
  13. package/dist/commands/history.d.ts +17 -0
  14. package/dist/commands/history.js +113 -0
  15. package/dist/commands/hub.js +95 -102
  16. package/dist/commands/index.js +41 -46
  17. package/dist/commands/legion.js +146 -186
  18. package/dist/commands/preview.d.ts +55 -0
  19. package/dist/commands/preview.js +467 -0
  20. package/dist/commands/replay.d.ts +18 -0
  21. package/dist/commands/replay.js +156 -0
  22. package/dist/commands/repo.d.ts +97 -0
  23. package/dist/commands/repo.js +773 -0
  24. package/dist/commands/review.js +29 -36
  25. package/dist/commands/security.js +5 -12
  26. package/dist/commands/update.d.ts +9 -0
  27. package/dist/commands/update.js +201 -0
  28. package/dist/commands/wallet.js +28 -35
  29. package/dist/commands/workflow.js +13 -20
  30. package/dist/index.d.ts +21 -0
  31. package/dist/index.js +1826 -0
  32. package/dist/utils/api.d.ts +572 -0
  33. package/dist/utils/api.js +6629 -0
  34. package/dist/utils/brain-hub-client.js +1 -5
  35. package/dist/utils/bridge-client.js +11 -52
  36. package/dist/utils/cli-state.d.ts +54 -0
  37. package/dist/utils/cli-state.js +185 -0
  38. package/dist/utils/codebase-indexer.js +4 -41
  39. package/dist/utils/config.d.ts +85 -0
  40. package/dist/utils/config.js +267 -0
  41. package/dist/utils/context-ranker.js +15 -21
  42. package/dist/utils/files.js +5 -42
  43. package/dist/utils/logger.js +42 -50
  44. package/dist/utils/persona.js +3 -8
  45. package/dist/utils/post-write-validator.js +22 -29
  46. package/dist/utils/project-memory.js +16 -23
  47. package/dist/utils/session.d.ts +118 -0
  48. package/dist/utils/session.js +423 -0
  49. package/dist/utils/task-display.js +13 -20
  50. package/dist/utils/tools.d.ts +276 -0
  51. package/dist/utils/tools.js +3522 -0
  52. package/dist/utils/workspace-brain-service.js +8 -45
  53. package/dist/utils/workspace-cache.js +18 -26
  54. package/dist/utils/workspace-stream.js +21 -63
  55. package/package.json +1 -1
@@ -1,8 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizePersonaMode = normalizePersonaMode;
4
- exports.isToneDownPrompt = isToneDownPrompt;
5
- exports.buildPersonaOverlay = buildPersonaOverlay;
6
1
  const WIENER_GRANT_PROMPT = [
7
2
  'Optional persona overlay: Wiener Grantler mode.',
8
3
  'You remain Vigthoria: technically precise, helpful, safe, and comprehensive.',
@@ -18,7 +13,7 @@ const TONE_DOWN_PROMPT = [
18
13
  'Tone-down rule active: this request appears safety-sensitive, destructive, auth/billing-related, security-related, legal/medical/financial, or production-critical.',
19
14
  'Use only a very light flavor line if appropriate, then prioritize plain clarity, caution, and exact steps.',
20
15
  ].join('\n');
21
- function normalizePersonaMode(value) {
16
+ export function normalizePersonaMode(value) {
22
17
  const normalized = String(value || '').trim().toLowerCase().replace(/_/g, '-');
23
18
  if (!normalized || normalized === 'default' || normalized === 'off' || normalized === 'none')
24
19
  return 'default';
@@ -26,10 +21,10 @@ function normalizePersonaMode(value) {
26
21
  return 'wiener_grant';
27
22
  return null;
28
23
  }
29
- function isToneDownPrompt(prompt) {
24
+ export function isToneDownPrompt(prompt) {
30
25
  return /\b(delete|drop|destroy|wipe|rm\s+-rf|format|credential|secret|token|auth|login|billing|payment|wallet|security|vulnerability|exploit|incident|production|prod|deploy|legal|medical|financial|bank|tax)\b/i.test(prompt);
31
26
  }
32
- function buildPersonaOverlay(mode, prompt = '') {
27
+ export function buildPersonaOverlay(mode, prompt = '') {
33
28
  if (mode !== 'wiener_grant')
34
29
  return '';
35
30
  const parts = [WIENER_GRANT_PROMPT];
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Vigthoria CLI — Post-Write Validator / Self-Healing Layer
4
3
  *
@@ -9,18 +8,12 @@
9
8
  *
10
9
  * Used by runSelfHealingCycle in api.ts to send targeted correction prompts.
11
10
  */
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.runPostWriteValidation = runPostWriteValidation;
17
- exports.formatValidationErrors = formatValidationErrors;
18
- const node_child_process_1 = require("node:child_process");
19
- const node_fs_1 = __importDefault(require("node:fs"));
20
- const node_path_1 = __importDefault(require("node:path"));
11
+ import { execFileSync } from 'node:child_process';
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
21
14
  function commandExists(cmd, args = ['--version']) {
22
15
  try {
23
- (0, node_child_process_1.execFileSync)(cmd, args, { stdio: 'pipe', timeout: 4000, windowsHide: true });
16
+ execFileSync(cmd, args, { stdio: 'pipe', timeout: 4000, windowsHide: true });
24
17
  return true;
25
18
  }
26
19
  catch {
@@ -28,23 +21,23 @@ function commandExists(cmd, args = ['--version']) {
28
21
  }
29
22
  }
30
23
  function hasTsConfig(dir) {
31
- return node_fs_1.default.existsSync(node_path_1.default.join(dir, 'tsconfig.json'));
24
+ return fs.existsSync(path.join(dir, 'tsconfig.json'));
32
25
  }
33
26
  /**
34
27
  * Resolve the best available tsc binary for the project.
35
28
  * Prefers local node_modules/.bin/tsc over npx to avoid network fetches.
36
29
  */
37
30
  function resolveTscBin(workspacePath) {
38
- const localTsc = node_path_1.default.join(workspacePath, 'node_modules', '.bin', 'tsc');
39
- if (node_fs_1.default.existsSync(localTsc))
31
+ const localTsc = path.join(workspacePath, 'node_modules', '.bin', 'tsc');
32
+ if (fs.existsSync(localTsc))
40
33
  return { cmd: localTsc, args: ["--noEmit", "--skipLibCheck"] };
41
34
  try {
42
- (0, node_child_process_1.execFileSync)("npx", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
35
+ execFileSync("npx", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
43
36
  return { cmd: "npx", args: ["tsc", "--noEmit", "--skipLibCheck"] };
44
37
  }
45
38
  catch { /* npx not available */ }
46
39
  try {
47
- (0, node_child_process_1.execFileSync)("tsc", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
40
+ execFileSync("tsc", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
48
41
  return { cmd: "tsc", args: ["--noEmit", "--skipLibCheck"] };
49
42
  }
50
43
  catch { /* tsc not available */ }
@@ -52,10 +45,10 @@ function resolveTscBin(workspacePath) {
52
45
  }
53
46
  function hasRealTestScript(dir) {
54
47
  try {
55
- const pkgPath = node_path_1.default.join(dir, 'package.json');
56
- if (!node_fs_1.default.existsSync(pkgPath))
48
+ const pkgPath = path.join(dir, 'package.json');
49
+ if (!fs.existsSync(pkgPath))
57
50
  return false;
58
- const pkg = JSON.parse(node_fs_1.default.readFileSync(pkgPath, 'utf-8'));
51
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
59
52
  const script = pkg.scripts?.test ?? '';
60
53
  return typeof script === 'string' && script.length > 0
61
54
  && !script.startsWith('echo "Error: no test specified"');
@@ -68,8 +61,8 @@ function hasRealTestScript(dir) {
68
61
  * Run post-write validation in the project directory.
69
62
  * Each check is time-bounded to avoid blocking the CLI.
70
63
  */
71
- async function runPostWriteValidation(workspacePath) {
72
- if (!workspacePath || !node_fs_1.default.existsSync(workspacePath))
64
+ export async function runPostWriteValidation(workspacePath) {
65
+ if (!workspacePath || !fs.existsSync(workspacePath))
73
66
  return [];
74
67
  const results = [];
75
68
  const opts = { cwd: workspacePath, stdio: 'pipe', windowsHide: true };
@@ -77,7 +70,7 @@ async function runPostWriteValidation(workspacePath) {
77
70
  const tscBin = hasTsConfig(workspacePath) ? resolveTscBin(workspacePath) : null;
78
71
  if (tscBin) {
79
72
  try {
80
- (0, node_child_process_1.execFileSync)(tscBin.cmd, tscBin.args, { ...opts, timeout: 45_000 });
73
+ execFileSync(tscBin.cmd, tscBin.args, { ...opts, timeout: 45_000 });
81
74
  results.push({ ran: true, tool: 'tsc', passed: true, output: '' });
82
75
  }
83
76
  catch (err) {
@@ -88,7 +81,7 @@ async function runPostWriteValidation(workspacePath) {
88
81
  // 2. npm test
89
82
  if (hasRealTestScript(workspacePath) && commandExists('npm')) {
90
83
  try {
91
- (0, node_child_process_1.execFileSync)('npm', ['test', '--', '--passWithNoTests'], { ...opts, timeout: 90_000 });
84
+ execFileSync('npm', ['test', '--', '--passWithNoTests'], { ...opts, timeout: 90_000 });
92
85
  results.push({ ran: true, tool: 'npm test', passed: true, output: '' });
93
86
  }
94
87
  catch (err) {
@@ -97,13 +90,13 @@ async function runPostWriteValidation(workspacePath) {
97
90
  }
98
91
  }
99
92
  // 3. Python syntax check
100
- const hasPy = node_fs_1.default.existsSync(node_path_1.default.join(workspacePath, 'requirements.txt'))
101
- || node_fs_1.default.existsSync(node_path_1.default.join(workspacePath, 'setup.py'))
102
- || node_fs_1.default.existsSync(node_path_1.default.join(workspacePath, 'pyproject.toml'));
93
+ const hasPy = fs.existsSync(path.join(workspacePath, 'requirements.txt'))
94
+ || fs.existsSync(path.join(workspacePath, 'setup.py'))
95
+ || fs.existsSync(path.join(workspacePath, 'pyproject.toml'));
103
96
  if (hasPy && commandExists('python3', ['-c', 'import ast'])) {
104
97
  const pyFiles = [];
105
98
  try {
106
- const entries = node_fs_1.default.readdirSync(workspacePath, { withFileTypes: true });
99
+ const entries = fs.readdirSync(workspacePath, { withFileTypes: true });
107
100
  for (const e of entries) {
108
101
  if (e.isFile() && e.name.endsWith('.py'))
109
102
  pyFiles.push(e.name);
@@ -114,7 +107,7 @@ async function runPostWriteValidation(workspacePath) {
114
107
  let pyOut = '';
115
108
  for (const pyFile of pyFiles.slice(0, 20)) {
116
109
  try {
117
- (0, node_child_process_1.execFileSync)('python3', ['-m', 'py_compile', pyFile], { ...opts, timeout: 10_000 });
110
+ execFileSync('python3', ['-m', 'py_compile', pyFile], { ...opts, timeout: 10_000 });
118
111
  }
119
112
  catch (err) {
120
113
  pyPassed = false;
@@ -130,7 +123,7 @@ async function runPostWriteValidation(workspacePath) {
130
123
  /**
131
124
  * Build a clear error summary string from failed validation results.
132
125
  */
133
- function formatValidationErrors(results) {
126
+ export function formatValidationErrors(results) {
134
127
  return results
135
128
  .filter((r) => r.ran && !r.passed)
136
129
  .map((r) => `[${r.tool}]\n${r.output.trim()}`)
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Vigthoria Project Memory Service
4
3
  *
@@ -6,14 +5,9 @@
6
5
  * compact facts that can be retrieved for follow-up prompts without replaying
7
6
  * full chat history or noisy tool traces.
8
7
  */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.ProjectMemoryService = void 0;
14
- const node_fs_1 = __importDefault(require("node:fs"));
15
- const node_path_1 = __importDefault(require("node:path"));
16
- const node_crypto_1 = require("node:crypto");
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import { createHash } from 'node:crypto';
17
11
  const MAX_ITEMS = 240;
18
12
  const MAX_TEXT_LENGTH = 320;
19
13
  const MAX_CONTEXT_ITEMS = 14;
@@ -35,7 +29,7 @@ function clampText(value, max = MAX_TEXT_LENGTH) {
35
29
  return normalized.length > max ? `${normalized.slice(0, max - 3)}...` : normalized;
36
30
  }
37
31
  function slugHash(value) {
38
- return (0, node_crypto_1.createHash)('sha1').update(value).digest('hex').slice(0, 12);
32
+ return createHash('sha1').update(value).digest('hex').slice(0, 12);
39
33
  }
40
34
  function tokenize(value) {
41
35
  return Array.from(new Set(String(value || '')
@@ -65,22 +59,22 @@ function inferTags(text) {
65
59
  tags.add('validation');
66
60
  return Array.from(tags).slice(0, 8);
67
61
  }
68
- class ProjectMemoryService {
62
+ export class ProjectMemoryService {
69
63
  workspacePath;
70
64
  memoryDir;
71
65
  brainPath;
72
66
  constructor(workspacePath = process.cwd()) {
73
- this.workspacePath = node_path_1.default.resolve(workspacePath || process.cwd());
74
- this.memoryDir = node_path_1.default.join(this.workspacePath, '.vigthoria', 'memory');
75
- this.brainPath = node_path_1.default.join(this.memoryDir, 'brain.json');
67
+ this.workspacePath = path.resolve(workspacePath || process.cwd());
68
+ this.memoryDir = path.join(this.workspacePath, '.vigthoria', 'memory');
69
+ this.brainPath = path.join(this.memoryDir, 'brain.json');
76
70
  }
77
71
  getMemoryDir() {
78
72
  return this.memoryDir;
79
73
  }
80
74
  loadBrain() {
81
75
  try {
82
- if (node_fs_1.default.existsSync(this.brainPath)) {
83
- const parsed = JSON.parse(node_fs_1.default.readFileSync(this.brainPath, 'utf8'));
76
+ if (fs.existsSync(this.brainPath)) {
77
+ const parsed = JSON.parse(fs.readFileSync(this.brainPath, 'utf8'));
84
78
  if (parsed && parsed.version === 1 && Array.isArray(parsed.items)) {
85
79
  return parsed;
86
80
  }
@@ -91,21 +85,21 @@ class ProjectMemoryService {
91
85
  }
92
86
  return {
93
87
  version: 1,
94
- workspaceName: node_path_1.default.basename(this.workspacePath),
88
+ workspaceName: path.basename(this.workspacePath),
95
89
  updatedAt: nowIso(),
96
90
  items: [],
97
91
  };
98
92
  }
99
93
  saveBrain(brain) {
100
94
  try {
101
- node_fs_1.default.mkdirSync(this.memoryDir, { recursive: true });
95
+ fs.mkdirSync(this.memoryDir, { recursive: true });
102
96
  const normalized = {
103
97
  ...brain,
104
- workspaceName: brain.workspaceName || node_path_1.default.basename(this.workspacePath),
98
+ workspaceName: brain.workspaceName || path.basename(this.workspacePath),
105
99
  updatedAt: nowIso(),
106
100
  items: this.dedupeAndTrim(brain.items || []),
107
101
  };
108
- node_fs_1.default.writeFileSync(this.brainPath, JSON.stringify(normalized, null, 2) + '\n', 'utf8');
102
+ fs.writeFileSync(this.brainPath, JSON.stringify(normalized, null, 2) + '\n', 'utf8');
109
103
  this.writeMarkdownViews(normalized);
110
104
  }
111
105
  catch {
@@ -276,10 +270,10 @@ class ProjectMemoryService {
276
270
  continue;
277
271
  const heading = this.headingForType(type);
278
272
  const lines = [`# ${heading}`, '', ...items.map((item) => `- ${item.text}`)];
279
- node_fs_1.default.writeFileSync(node_path_1.default.join(this.memoryDir, `${type}s.md`), lines.join('\n') + '\n', 'utf8');
273
+ fs.writeFileSync(path.join(this.memoryDir, `${type}s.md`), lines.join('\n') + '\n', 'utf8');
280
274
  allLines.push(`## ${heading}`, '', ...items.slice(-40).map((item) => `- ${item.text}`), '');
281
275
  }
282
- node_fs_1.default.writeFileSync(node_path_1.default.join(this.memoryDir, 'README.md'), allLines.join('\n').trimEnd() + '\n', 'utf8');
276
+ fs.writeFileSync(path.join(this.memoryDir, 'README.md'), allLines.join('\n').trimEnd() + '\n', 'utf8');
283
277
  }
284
278
  headingForType(type) {
285
279
  switch (type) {
@@ -293,4 +287,3 @@ class ProjectMemoryService {
293
287
  }
294
288
  }
295
289
  }
296
- exports.ProjectMemoryService = ProjectMemoryService;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Session Manager - Persist and resume conversations
3
+ * Similar to Vigthoria's session persistence
4
+ */
5
+ import { ChatMessage } from './api.js';
6
+ export type AuthState = {
7
+ token: string | null;
8
+ expiresAt: number | null;
9
+ isValid: boolean;
10
+ };
11
+ export type CliError = {
12
+ code: string;
13
+ message: string;
14
+ details?: any;
15
+ isCritical: boolean;
16
+ };
17
+ /**
18
+ * Validate persisted authentication state without assuming any field is present.
19
+ *
20
+ * Production hygiene: by default this helper is SILENT. Earlier revisions
21
+ * wrote ``console.warn`` for every missing-token branch, which polluted
22
+ * stderr on PowerShell (NativeCommandError styling) and surfaced as noise
23
+ * on first-run installs. Pass ``{ silent: false }`` only when the caller
24
+ * actually wants the warning visible.
25
+ */
26
+ export declare function validateSession(session: AuthState, options?: {
27
+ silent?: boolean;
28
+ }): boolean;
29
+ /**
30
+ * Load persisted authentication state and normalize nullable token fields safely.
31
+ *
32
+ * Production behaviour: first-run users (no session file yet) get a clean
33
+ * ``{ token: null, expiresAt: null, isValid: false }`` instead of an
34
+ * exception. Callers that need a hard failure can inspect ``isValid``.
35
+ */
36
+ export declare function loadSession(): Promise<AuthState>;
37
+ export interface Session {
38
+ id: string;
39
+ name: string;
40
+ project: string;
41
+ model: string;
42
+ messages: ChatMessage[];
43
+ memorySummary?: string;
44
+ compactedAt?: string | null;
45
+ createdAt: string;
46
+ updatedAt: string;
47
+ agentMode: boolean;
48
+ operatorMode?: boolean;
49
+ }
50
+ export declare class SessionManager {
51
+ private sessionsDir;
52
+ private readonly compactThreshold;
53
+ private readonly retainRecentMessages;
54
+ /** Keep at most this many session files on disk per CLI install. */
55
+ private readonly maxRetainedSessions;
56
+ /** Sessions older than this are pruned (90 days). */
57
+ private readonly retentionWindowMs;
58
+ /** Avoid running prune on every save — at most once per CLI process. */
59
+ private prunedThisProcess;
60
+ constructor();
61
+ private ensureDir;
62
+ /**
63
+ * Prune sessions older than the retention window AND cap the number of
64
+ * retained files. This is best-effort: any unreadable file is treated
65
+ * as stale and removed. Pruning is bounded to once per process.
66
+ */
67
+ private pruneIfNeeded;
68
+ /**
69
+ * Generate unique session ID
70
+ */
71
+ private generateId;
72
+ /**
73
+ * Create a new session
74
+ */
75
+ create(project: string, model: string, agentMode?: boolean, operatorMode?: boolean): Session;
76
+ /**
77
+ * Save session to disk — atomic + 0600 mode so transcripts cannot be
78
+ * read by other local users on a shared POSIX host.
79
+ */
80
+ save(session: Session): void;
81
+ /**
82
+ * Load session by ID
83
+ */
84
+ load(id: string): Session | null;
85
+ /**
86
+ * Get the most recent session for a project
87
+ */
88
+ getLatest(project: string): Session | null;
89
+ /**
90
+ * List all sessions (metadata only) — corrupt or unreadable files are
91
+ * skipped silently unless VIGTHORIA_DEBUG=1.
92
+ */
93
+ list(): Omit<Session, 'messages'>[];
94
+ /**
95
+ * Delete a session
96
+ */
97
+ delete(id: string): boolean;
98
+ /**
99
+ * Clear all sessions
100
+ */
101
+ clearAll(): number;
102
+ /**
103
+ * Add message to session
104
+ */
105
+ addMessage(session: Session, message: ChatMessage): void;
106
+ compactInMemory(session: Session): Session;
107
+ buildMemoryContext(session: Session | null): string;
108
+ private compactSession;
109
+ private isPersistentSystemMessage;
110
+ private mergePersistentAndRecent;
111
+ private mergeSummaries;
112
+ private summarizeMessages;
113
+ private normalizeMessageContent;
114
+ /**
115
+ * Get session summary for display
116
+ */
117
+ getSummary(session: Session): string;
118
+ }