vectorforce 0.3.8 → 0.3.9

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/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.3.9] - 2026-11-05
11
+
12
+ ### Fixed
13
+ - Fixed Studio displaying VectorForce fallback mock decisions, mock checkpoints, and mock invariants when connected to projects with 0 checkpoints (such as `HackathonDemo`).
14
+ - Enhanced `CodeIndexProvider` to automatically filter Python virtual environments (`.venv`, `venv`, `env`), caches (`__pycache__`, `.pytest_cache`), local vector stores (`chroma_db`), and hidden dot-directories so real workspace code is not crowded out.
15
+ - Added cross-module Python import dependency resolution (e.g., `swarm.model_client`, `agents.tools`) to dynamically link architecture topology graphs for multi-agent swarm applications.
16
+ - Added clean empty states across Dashboard, Memory & Ontology, Timeline, and Graph views for new and active workspaces.
17
+
10
18
  ## [0.3.8] - 2026-11-04
11
19
 
12
20
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vectorforce",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Portable engineering context platform - Git for AI context",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -10,7 +10,10 @@ export const SNIPPET_MAX_CHARS = 4000;
10
10
 
11
11
  const SKIP_DIRS = new Set([
12
12
  '.git', 'node_modules', 'dist', 'build', 'coverage', '.vectorforce',
13
- '.commandcode', '.agents', 'tmp', '.next', '.cache', 'out', 'vendor', 'target'
13
+ '.commandcode', '.agents', 'tmp', '.next', '.cache', 'out', 'vendor', 'target',
14
+ '.venv', 'venv', 'env', '.env', 'virtualenv', '__pycache__', '.pytest_cache',
15
+ '.mypy_cache', '.ruff_cache', '.tox', 'chroma_db', '.chroma', '.idea', '.vscode',
16
+ '.cursor', '.kiro', '.muse', '.windsurf', '.github', '.settings'
14
17
  ]);
15
18
 
16
19
  const SKIP_FILE_PATTERNS = [
@@ -103,7 +106,7 @@ export class CodeIndexProvider {
103
106
  if (entry.isSymbolicLink()) continue;
104
107
 
105
108
  if (entry.isDirectory()) {
106
- if (SKIP_DIRS.has(name)) continue;
109
+ if (SKIP_DIRS.has(name) || (name.startsWith('.') && name !== '.')) continue;
107
110
  walk(abs, rel);
108
111
  continue;
109
112
  }
@@ -269,9 +272,24 @@ export class CodeIndexProvider {
269
272
  for (const file of files) {
270
273
  const dir = posix.dirname(file.path);
271
274
  for (const spec of file.symbols.imports) {
272
- if (!spec.startsWith('.')) continue;
273
- const base = posix.normalize(posix.join(dir, spec));
274
- const hit = RESOLUTION_SUFFIXES.map(suffix => base + suffix).find(candidate => known.has(candidate));
275
+ let hit = null;
276
+ if (spec.startsWith('.')) {
277
+ const base = posix.normalize(posix.join(dir, spec));
278
+ hit = RESOLUTION_SUFFIXES.map(suffix => base + suffix).find(candidate => known.has(candidate));
279
+ } else {
280
+ // Check Python workspace imports (e.g. 'swarm.model_client' -> 'swarm/model_client.py')
281
+ const pyPath = spec.replace(/\./g, '/') + '.py';
282
+ const pyInit = spec.replace(/\./g, '/') + '/__init__.py';
283
+ if (known.has(pyPath)) {
284
+ hit = pyPath;
285
+ } else if (known.has(pyInit)) {
286
+ hit = pyInit;
287
+ } else if (known.has(spec)) {
288
+ hit = spec;
289
+ } else {
290
+ hit = RESOLUTION_SUFFIXES.map(suffix => spec + suffix).find(candidate => known.has(candidate));
291
+ }
292
+ }
275
293
  if (!hit || hit === file.path) continue;
276
294
  const key = `${file.path}->${hit}`;
277
295
  if (seen.has(key)) continue;
@@ -237,25 +237,23 @@ export function createStudioServer({
237
237
  : checkpoints;
238
238
 
239
239
  // Dynamically cluster code files by architectural module
240
+ const resolveModId = (filePath) => {
241
+ if (filePath === 'src/index.js') return 'core';
242
+ if (filePath.startsWith('src/')) {
243
+ const parts = filePath.split('/');
244
+ return parts[1] || 'core';
245
+ }
246
+ if (filePath.startsWith('test/') || filePath.startsWith('tests/')) return 'tests';
247
+ if (filePath.startsWith('wiki/')) return 'wiki';
248
+ if (filePath.startsWith('studio/')) return 'studio-ui';
249
+ if (filePath.startsWith('bin/')) return 'cli';
250
+ const parts = filePath.split('/');
251
+ return parts.length > 1 ? parts[0] : 'root';
252
+ };
253
+
240
254
  const moduleMap = new Map();
241
255
  for (const file of codeIndex.files) {
242
- const parts = file.path.split('/');
243
- let modId = parts[0];
244
- if (file.path === 'src/index.js') {
245
- modId = 'core';
246
- } else if (file.path.startsWith('src/')) {
247
- modId = parts[1] || 'core';
248
- } else if (file.path.startsWith('test/')) {
249
- modId = 'tests';
250
- } else if (file.path.startsWith('wiki/')) {
251
- modId = 'wiki';
252
- } else if (file.path.startsWith('studio/')) {
253
- modId = 'studio-ui';
254
- } else if (file.path.startsWith('bin/')) {
255
- modId = 'cli';
256
- } else {
257
- modId = parts.length > 1 ? parts[0] : 'root';
258
- }
256
+ const modId = resolveModId(file.path);
259
257
 
260
258
  if (!moduleMap.has(modId)) {
261
259
  const prettyNames = {
@@ -265,7 +263,7 @@ export function createStudioServer({
265
263
  'providers': 'AI LLM & AST Code Index',
266
264
  'mcp': 'Harness MCP Bridge',
267
265
  'studio': 'Studio API Server',
268
- 'core': 'Core Hashes & Errors',
266
+ 'core': 'Core Engine & Foundation',
269
267
  'cli': 'Unified CLI Interface',
270
268
  'studio-ui': 'React Studio Frontend',
271
269
  'tests': 'Automated Test Suite',
@@ -278,14 +276,20 @@ export function createStudioServer({
278
276
  'controllers': 'Controllers',
279
277
  'routes': 'Router Layer',
280
278
  'services': 'Domain Services',
281
- 'utils': 'Utility Helpers'
279
+ 'utils': 'Utility Helpers',
280
+ 'agents': 'AI Autonomous Agents',
281
+ 'rag': 'RAG & Vector Retrieval',
282
+ 'swarm': 'Multi-Agent Swarm Orchestrator',
283
+ 'tools': 'Agent Tools & Functions',
284
+ 'chains': 'Cognitive Chains',
285
+ 'prompts': 'System Prompts'
282
286
  };
283
287
  const capitalizedMod = modId.charAt(0).toUpperCase() + modId.slice(1);
284
288
  moduleMap.set(modId, {
285
289
  id: modId,
286
290
  name: prettyNames[modId] || capitalizedMod,
287
291
  category: (modId === 'policy' || modId === 'storage' || modId === 'auth' || modId === 'security') ? 'Security' :
288
- (modId === 'providers' || modId === 'ai' || modId === 'llm') ? 'AI / Copilot' :
292
+ (modId === 'providers' || modId === 'ai' || modId === 'llm' || modId === 'agents' || modId === 'rag' || modId === 'swarm') ? 'AI / Copilot' :
289
293
  (modId === 'mcp' || modId === 'api' || modId === 'routes') ? 'Integration' : 'Core',
290
294
  owner: project.name || 'Workspace',
291
295
  files: [],
@@ -306,10 +310,8 @@ export function createStudioServer({
306
310
 
307
311
  // Map codeIndex.edges to module dependencies
308
312
  for (const edge of codeIndex.edges) {
309
- const fromParts = edge.from.split('/');
310
- const toParts = edge.to.split('/');
311
- const fromMod = fromParts[0] === 'src' ? (fromParts[1] || 'core') : fromParts[0];
312
- const toMod = toParts[0] === 'src' ? (toParts[1] || 'core') : toParts[0];
313
+ const fromMod = resolveModId(edge.from);
314
+ const toMod = resolveModId(edge.to);
313
315
  if (fromMod !== toMod && moduleMap.has(fromMod) && moduleMap.has(toMod)) {
314
316
  moduleMap.get(fromMod).dependencies.add(toMod);
315
317
  }