Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b968e50
Rust ORM module: database-agnostic storage with dbPath per-request
Feb 6, 2026
f3b7162
No fallbacks: require explicit dbPath in all storage adapters
Feb 6, 2026
b48a243
ORM unified data layer: migrate all DataDaemon calls, fix Rust FFI sa…
Feb 6, 2026
4048ca1
Improve data/open schema: add ADVANCED warning and valid adapter types
Feb 7, 2026
7dc7036
fixed some data issues
Feb 7, 2026
acced43
Phase 3: Migrate persona core files to ORM
Feb 7, 2026
09eda40
Phase 3: Migrate remaining system files to ORM
Feb 7, 2026
869a213
Phase 4 Rust ORM: Add ORMRustClient IPC bridge, clean architecture
Feb 7, 2026
770a0a3
more completel got queryWithJoin
Feb 7, 2026
f9cc748
Phase 4 Rust ORM: Type-safe collection names, zero warnings
Feb 7, 2026
0e53404
Fix log spam: remove debug prints, fix limit: -1 parse error
Feb 7, 2026
e7d33cb
Phase 5 ORM cleanup: remove dead DataDaemon fallback paths
Feb 7, 2026
9cc740e
well it runs now
Feb 7, 2026
661d094
rust based dl working
Feb 8, 2026
36b453e
ORM event emission: use DataDaemon.jtagContext for browser routing
Feb 8, 2026
19e941a
ORM Phase 5: route batch/listCollections/clear/clearAll/truncate thro…
Feb 8, 2026
5fe939c
ORM Phase 6: document performance bottleneck root cause
Feb 8, 2026
14d09ef
DataModule: add timing instrumentation for slow queries
Feb 8, 2026
d824f8b
diagnosing slowness fragility
Feb 8, 2026
9e3942a
Modular runtime: LoggerModule, SearchModule, TTS async fix
Feb 8, 2026
1a7dfeb
Phase 4c: EmbeddingModule + clippy/SCSS cleanup
Feb 8, 2026
7b727d5
RustEmbeddingClient: update protocol for continuum-core
Feb 8, 2026
4035809
Identity refactoring: context.userId as single source of truth
Feb 9, 2026
bd0d10a
DataModule: migrate vector search from data-daemon to continuum-core
Feb 9, 2026
c234481
Disable data-daemon worker: ORM uses continuum-core directly
Feb 9, 2026
708e278
Remove RustWorkerStorageAdapter dead code (1,685 lines)
Feb 9, 2026
1e69e90
Remove data-daemon Rust worker (3,689 lines) - migrated to DataModule…
Feb 9, 2026
ed312c1
Remove deprecated workers: logger, search, training, embedding, infer…
Feb 9, 2026
84419d9
Remove leftover worker binaries
Feb 9, 2026
654d437
Remove dead code: comms-test, RustAdapter, data/chat-drain workers (7…
Feb 9, 2026
5d24ec2
Update logger socket paths to continuum-core unified runtime
Feb 9, 2026
ebcecda
Update stale references and add deprecation notes
Feb 9, 2026
20596e6
Remove orphaned IPC type files from removed workers
Feb 9, 2026
01c322b
Update remaining socket path references to continuum-core
Feb 9, 2026
0301ee7
Fix continuum-core startup: make logger socket optional (Phase 4a)
Feb 9, 2026
fa521a2
Route vector search to Rust + delete dead code
Feb 9, 2026
8e01590
ORM Phase 7: route all data commands through ORM, move ORM to server/
Feb 9, 2026
e580f19
ORM Phase 8: route generateEmbedding to Rust EmbeddingModule
Feb 9, 2026
db7550e
ORM Phase 9: Route indexVector, getVectorIndexStats to Rust DataModule
Feb 9, 2026
cc5a74f
ORM Phase 10: Route paginated queries to Rust DataModule
Feb 9, 2026
9d068f9
ORM Phase 11: Route backfillVectors to Rust DataModule
Feb 9, 2026
74d1f3b
ORM Phase 12: Add storage-agnostic include terminology
Feb 9, 2026
5c71986
ORM Phase 13: DatabaseHandleRegistry → lightweight path registry
Feb 9, 2026
8eaaf5a
Fix Rust warnings: remove unused import and dead struct field
Feb 9, 2026
d5bfc5c
Add command shorthand design from AI team collaboration
Feb 9, 2026
f08fe86
Fix ORM.update to emit full entity instead of partial data
Feb 9, 2026
8b1f5a4
ORM cleanup: delete dead TypeScript SQLite code (~5.7K lines)
Feb 9, 2026
c835306
ORM cleanup phase 2: delete more dead code
Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,14 @@ npm start # DEPLOYS code changes, takes 130s or so

**IF YOU FORGET `npm start`, THE BROWSER SHOWS OLD CODE!**

**NEVER CALL `cargo build` DIRECTLY!**
- ALL Rust binaries MUST be built via `npm start`
- If you run `cargo build --release` manually, that binary only exists on YOUR machine
- When someone else clones the repo and runs `npm start`, that step doesn't happen
- The repo is BROKEN for everyone except you
- Manual build steps = broken repo for all other users
- If a Rust binary needs to be built, it MUST be wired into the `npm start` build scripts

Don't panic and stash changes first before anything drastic. Use the stash to your advantage and you will be safe from catastrophe. Remember we have git for a reason!

### Chat Commands
Expand Down
184 changes: 184 additions & 0 deletions docs/COMMAND-SHORTHAND-DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Command Shorthand Design

**Status**: Approved via team decision (2026-02-09)
**Proposal ID**: 13208c93-714a-436d-b681-a2e1b8a71a3a
**Contributors**: Grok, DeepSeek Assistant, Together Assistant, Groq Lightning, Joel

## Overview

Unix-like 2-5 letter command prefixes to reduce context length while maintaining discoverability. This design emerged from collaborative discussion in general chat where multiple AI assistants independently converged on the same modular, hierarchical approach.

## Core Prefixes

| Prefix | Domain | Examples | Replaces |
|--------|--------|----------|----------|
| `cd/` | Code operations | cd/edit, cd/read, cd/diff, cd/verify | code/* |
| `gt/` | Git operations | gt/init, gt/status, gt/commit, gt/push | workspace/git/* |
| `sh/` | Shell operations | sh/exec, sh/stat, sh/watch, sh/kill | code/shell/* |
| `dt/` | Data operations | dt/list, dt/read, dt/query, dt/create | data/* |
| `cl/` | Collaboration | cl/chat, cl/vote, cl/prop, cl/wall | collaboration/* |
| `ai/` | AI operations | ai/gen, ai/rag, ai/status | (already short) |
| `lg/` | Logs | lg/read, lg/search, lg/stats | logs/* |
| `ui/` | Interface | ui/click, ui/screenshot, ui/scroll | interface/* |
| `ws/` | Workspace | ws/list, ws/tree, ws/task | workspace/* |

## Context Savings

Real examples from current command set:

| Shorthand | Full Command | Chars Saved |
|-----------|--------------|-------------|
| `gt/init` | `workspace/git/workspace/init` | **28** |
| `cl/vote` | `collaboration/decision/vote` | **22** |
| `sh/exec` | `code/shell/execute` | **10** |
| `cd/edit` | `code/edit` | 2 |
| `dt/list` | `data/list` | 1 |

**Cumulative impact**: Hundreds of characters saved per session, significant for AI context windows.

## Subcommand Patterns

Keep subcommands short and consistent:

```
cd/rd → code/read
cd/ed → code/edit
cd/wr → code/write
cd/df → code/diff
cd/tr → code/tree

gt/st → workspace/git/status
gt/cm → workspace/git/commit
gt/ps → workspace/git/push
gt/br → workspace/git/branch

sh/x → code/shell/execute
sh/k → code/shell/kill
sh/w → code/shell/watch

dt/ls → data/list
dt/rd → data/read
dt/cr → data/create
dt/rm → data/delete
dt/q → data/query-open

cl/msg → collaboration/chat/send
cl/exp → collaboration/chat/export
cl/vt → collaboration/decision/vote
cl/pr → collaboration/decision/propose
```

## Discovery System

### Help Command
```bash
help cd # List all code commands
help gt # List all git commands
help # List all prefixes with descriptions
```

### Search Command
```bash
search edit # Find all edit-related commands
search vector # Find all vector-related commands
```

### Tab Completion
- Type `cd/` + TAB → shows all code subcommands
- Type `gt/s` + TAB → completes to `gt/status` or shows options

## Implementation Phases

### Phase 1: Core Prefixes (Immediate)
- `cd/` for code operations (most frequently used)
- `gt/` for git operations (highest char savings)
- `sh/` for shell operations

### Phase 2: Data & Collaboration
- `dt/` for data operations
- `cl/` for collaboration
- `lg/` for logs

### Phase 3: Discovery System
- Help command with prefix listings
- Search command for keyword lookup
- Tab completion (CLI enhancement)

### Phase 4: Migration
- Backward compatibility aliases (old commands still work)
- Deprecation warnings for verbose forms
- Documentation updates
- Gradual transition timeline

## Architecture

### Alias Resolution Layer
```
User Input → Alias Resolver → Full Command → Command Router → Handler
↓ ↓ ↓ ↓ ↓
"cd/ed" → "code/edit" → lookup → dispatch → execute
```

### Registration Pattern
```typescript
// In command registration
CommandRegistry.registerAlias('cd/ed', 'code/edit');
CommandRegistry.registerAlias('gt/st', 'workspace/git/status');

// Or via prefix mapping
const PREFIX_MAP = {
'cd/': 'code/',
'gt/': 'workspace/git/',
'sh/': 'code/shell/',
'dt/': 'data/',
'cl/': 'collaboration/',
} as const;
```

### Constants (Single Source of Truth)
```typescript
// system/shared/CommandPrefixes.ts
export const CMD_PREFIX = {
CODE: 'cd',
GIT: 'gt',
SHELL: 'sh',
DATA: 'dt',
COLLAB: 'cl',
AI: 'ai',
LOGS: 'lg',
UI: 'ui',
WORKSPACE: 'ws',
} as const;

export type CmdPrefix = typeof CMD_PREFIX[keyof typeof CMD_PREFIX];
```

## Design Principles

1. **Unix Philosophy**: Short, memorable, composable commands
2. **Discoverability**: Help/search prevents "where's that tool?" frustration
3. **Backward Compatible**: Old verbose commands continue to work
4. **Hierarchical**: Prefixes map to logical domains
5. **Consistent**: Same patterns across all prefixes
6. **Context-Optimized**: Reduces token usage for AI assistants

## Team Discussion Highlights

> "Keeps things snappy like Unix (ls over list-files, right?)" — Grok

> "gt/init alone shaves off a chunk that adds up fast in long sessions" — DeepSeek

> "I prefer shorthands like we do with unix commands, which will cut down on context length for you" — Joel

> "Multiple AI assistants independently converged on the same modular, hierarchical strategy" — DeepSeek (noting emergent consensus)

## Decision Outcome

- **Winner**: Option 1 (cd/ for code operations as foundation)
- **Confidence**: 5/5 pairwise victories
- **Consensus**: 100% agreement among voters
- **Next Step**: Prototype in test branch, validate, then expand

---

*Document generated from team discussion in general chat, 2026-02-09*
4 changes: 2 additions & 2 deletions src/debug/jtag/commands/ai/cost/server/AICostServerCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ export class AICostServerCommand extends AICostCommand {
}

// Query AIGenerationEntity from database using data/list - let SQL do the filtering
// Note: omitting 'limit' means no limit (Rust ORM Option<usize> defaults to None)
const listParams = createDataListParams(
params.context,
params.sessionId,
{
collection: 'ai_generations',
filter,
orderBy: [{ field: 'timestamp', direction: 'desc' }],
limit: -1 // Get ALL records (no pagination for aggregate queries)
orderBy: [{ field: 'timestamp', direction: 'desc' }]
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { paramsToRequest, responseToResult, createErrorResult, createAIGenerateR
import { AIProviderDaemon } from '../../../../daemons/ai-provider-daemon/shared/AIProviderDaemon';
import { RAGBuilderFactory } from '../../../../system/rag/shared/RAGBuilder';
import { ChatRAGBuilder } from '../../../../system/rag/builders/ChatRAGBuilder';
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
import { UserEntity } from '../../../../system/data/entities/UserEntity';
import type { TextGenerationRequest } from '../../../../daemons/ai-provider-daemon/shared/AIProviderTypesV2';
import { SystemPaths } from '../../../../system/core/config/SystemPaths';
Expand Down Expand Up @@ -43,7 +43,7 @@ export class AIGenerateServerCommand extends AIGenerateCommand {
let targetPersonaId = params.personaId;
let personaDisplayName = 'ai-generate-command'; // Fallback name for tracking
if (!targetPersonaId) {
const usersResult = await DataDaemon.query<UserEntity>({
const usersResult = await ORM.query<UserEntity>({
collection: UserEntity.collection,
filter: { type: 'persona' },
limit: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { JTAGContext } from '../../../../../system/core/types/JTAGTypes';
import type { ICommandDaemon } from '../../../../../daemons/command-daemon/shared/CommandBase';
import type { RAGInspectParams, RAGInspectResult } from '../shared/RAGInspectTypes';
import { ChatRAGBuilder } from '../../../../../system/rag/builders/ChatRAGBuilder';
import { DataDaemon } from '../../../../../daemons/data-daemon/shared/DataDaemon';
import { ORM } from '../../../../../daemons/data-daemon/server/ORM';
import { ChatMessageEntity } from '../../../../../system/data/entities/ChatMessageEntity';
import { getThoughtStreamCoordinator } from '../../../../../system/conversation/server/ThoughtStreamCoordinator';
import type { Thought } from '../../../../../system/conversation/shared/ConversationCoordinationTypes';
Expand Down Expand Up @@ -101,7 +101,7 @@ export class RAGInspectServerCommand extends RAGInspectCommand {
if (params.triggerMessageId) {
try {
// Load the trigger message
const msg = await DataDaemon.read<ChatMessageEntity>(ChatMessageEntity.collection, params.triggerMessageId);
const msg = await ORM.read<ChatMessageEntity>(ChatMessageEntity.collection, params.triggerMessageId);
if (msg) {

// Get actual decision from ThoughtStream
Expand Down
16 changes: 12 additions & 4 deletions src/debug/jtag/commands/ai/sleep/server/AiSleepServerCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,20 @@ export class AiSleepServerCommand extends CommandBase<AiSleepParams, AiSleepResu
});
}

// Get persona ID - use explicit personaId, or callerId (injected by PersonaToolExecutor),
// or fall back to UserIdentityResolver for external callers (CLI, etc.)
let targetPersonaId = personaId || (params as any).callerId;
// Get persona ID - priority: context.userId, then explicit personaId, then callerId, then UserIdentityResolver
// Priority:
// 1. params.context?.userId - When a PersonaUser executes, their ID is in context
// 2. Explicit personaId param - For backwards compatibility
// 3. callerId (injected) - Legacy PersonaToolExecutor injection
// 4. UserIdentityResolver - Fallback for CLI calls
let targetPersonaId = params.context?.userId || personaId || (params as any).callerId;

if (params.context?.userId) {
console.log(`😴 ai/sleep: Using context.userId: ${params.context.userId}`);
}

if (!targetPersonaId) {
// Fall back to UserIdentityResolver for external callers (CLI, Claude Code, etc.)
// FALLBACK: Use UserIdentityResolver for external callers (CLI, Claude Code, etc.)
const identity = await UserIdentityResolver.resolve();
if (identity.exists && identity.userId) {
targetPersonaId = identity.userId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { JTAGContext } from '../../../../system/core/types/JTAGTypes';
import type { ICommandDaemon } from '../../../../daemons/command-daemon/shared/CommandBase';
import type { AIStatusParams, AIStatusResult, PersonaHealth } from '../shared/AIStatusTypes';
import { UserDaemonServer } from '../../../../daemons/user-daemon/server/UserDaemonServer';
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
import { COLLECTIONS } from '../../../../system/data/config/DatabaseConfig';
import type { UserEntity } from '../../../../system/data/entities/UserEntity';
import type { PersonaUser } from '../../../../system/user/server/PersonaUser';
Expand Down Expand Up @@ -61,7 +61,7 @@ export class AIStatusServerCommand extends AIStatusCommand {
private async executeWithDaemon(userDaemon: UserDaemonServer, params: AIStatusParams): Promise<AIStatusResult> {

// Query all PersonaUser entities from database
const result = await DataDaemon.query<UserEntity>({
const result = await ORM.query<UserEntity>({
collection: COLLECTIONS.USERS,
filter: { type: 'persona' }
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { JTAGContext } from '../../../../system/core/types/JTAGTypes';
import type { ICommandDaemon } from '../../../../daemons/command-daemon/shared/CommandBase';
import { getThoughtStreamCoordinator } from '../../../../system/conversation/server/ThoughtStreamCoordinator';
import { RAGBuilderFactory } from '../../../../system/rag/shared/RAGBuilder';
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
import { COLLECTIONS } from '../../../../system/data/config/DatabaseConfig';
import type { ChatMessageEntity } from '../../../../system/data/entities/ChatMessageEntity';
import type { UserEntity } from '../../../../system/data/entities/UserEntity';
Expand Down Expand Up @@ -74,7 +74,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {

try {
// Query data daemon for the message
const msg = await DataDaemon.read<ChatMessageEntity>(
const msg = await ORM.read<ChatMessageEntity>(
COLLECTIONS.CHAT_MESSAGES,
stream.messageId
);
Expand Down Expand Up @@ -497,7 +497,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {

try {
// Query user collection by displayName field
const result = await DataDaemon.query<UserEntity>({
const result = await ORM.query<UserEntity>({
collection: COLLECTIONS.USERS,
filter: { displayName: name },
limit: 1
Expand Down Expand Up @@ -583,7 +583,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {

private async getPersonaName(personaId: string, params: ThoughtStreamParams): Promise<string> {
try {
const user = await DataDaemon.read<UserEntity>(
const user = await ORM.read<UserEntity>(
COLLECTIONS.USERS,
personaId
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { createCanvasStrokeAddResult, CANVAS_STROKE_EVENTS } from '../shared/Can
import { CanvasStrokeEntity } from '@system/data/entities/CanvasStrokeEntity';
import { ChatMessageEntity } from '@system/data/entities/ChatMessageEntity';
import { RoomEntity } from '@system/data/entities/RoomEntity';
import { DataDaemon } from '@daemons/data-daemon/shared/DataDaemon';
import { UserEntity } from '@system/data/entities/UserEntity';
import { ORM } from '@daemons/data-daemon/server/ORM';
import { Events } from '@system/core/shared/Events';
import { Commands } from '@system/core/shared/Commands';
import { COLLECTIONS } from '@system/shared/Constants';
Expand Down Expand Up @@ -49,10 +50,30 @@ export class CanvasStrokeAddServerCommand extends CommandBase<CanvasStrokeAddPar
}

try {
// Get creator info - use UserIdentityResolver to detect caller (Claude Code, Joel, etc.)
const identity = await UserIdentityResolver.resolve();
const creatorId = identity.userId || strokeParams.sessionId;
const creatorName = identity.displayName;
// Get creator info - priority: context.userId (PersonaUsers), then UserIdentityResolver (CLI)
let creatorId: string;
let creatorName: string;

if (strokeParams.context?.userId) {
// FIRST: Check context.userId (PersonaUsers set this)
creatorId = strokeParams.context.userId;
// We need to look up the name from the database
const userResult = await DataList.execute<UserEntity>({
collection: UserEntity.collection,
filter: { id: creatorId },
limit: 1,
context: strokeParams.context,
sessionId: strokeParams.sessionId
});
creatorName = userResult.success && userResult.items && userResult.items.length > 0
? userResult.items[0].displayName
: 'Unknown';
} else {
// FALLBACK: Use UserIdentityResolver (CLI, Claude Code, Joel, etc.)
const identity = await UserIdentityResolver.resolve();
creatorId = identity.userId || strokeParams.sessionId;
creatorName = identity.displayName;
}

// Create stroke entity
const stroke = new CanvasStrokeEntity();
Expand Down Expand Up @@ -81,7 +102,7 @@ export class CanvasStrokeAddServerCommand extends CommandBase<CanvasStrokeAddPar
}

// Save to database (throws on failure, returns entity on success)
await DataDaemon.store(COLLECTIONS.CANVAS_STROKES, stroke);
await ORM.store(COLLECTIONS.CANVAS_STROKES, stroke);

// Emit real-time event for all clients to sync
Events.emit(CANVAS_STROKE_EVENTS.STROKE_ADDED, {
Expand Down
Loading
Loading