@learnrudi/cli 1.10.2 → 1.10.4
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/dist/index.cjs +919 -310
- package/dist/packages-manifest.json +1 -1
- package/package.json +21 -22
package/dist/index.cjs
CHANGED
|
@@ -34261,7 +34261,7 @@ var import_fs13 = __toESM(require("fs"), 1);
|
|
|
34261
34261
|
init_src2();
|
|
34262
34262
|
|
|
34263
34263
|
// packages/db/src/schema.js
|
|
34264
|
-
var SCHEMA_VERSION =
|
|
34264
|
+
var SCHEMA_VERSION = 6;
|
|
34265
34265
|
var SCHEMA_SQL = `
|
|
34266
34266
|
-- Schema version tracking
|
|
34267
34267
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -34282,6 +34282,7 @@ CREATE TABLE IF NOT EXISTS projects (
|
|
|
34282
34282
|
cross_project_id TEXT,
|
|
34283
34283
|
session_count INTEGER DEFAULT 0,
|
|
34284
34284
|
total_cost REAL DEFAULT 0,
|
|
34285
|
+
settings TEXT,
|
|
34285
34286
|
created_at TEXT NOT NULL,
|
|
34286
34287
|
|
|
34287
34288
|
UNIQUE(provider, name)
|
|
@@ -34303,15 +34304,17 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|
|
34303
34304
|
|
|
34304
34305
|
-- Display
|
|
34305
34306
|
title TEXT,
|
|
34307
|
+
title_override TEXT,
|
|
34306
34308
|
snippet TEXT,
|
|
34307
34309
|
|
|
34308
34310
|
-- State
|
|
34309
34311
|
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deleted')),
|
|
34310
34312
|
model TEXT,
|
|
34313
|
+
system_prompt TEXT,
|
|
34311
34314
|
|
|
34312
34315
|
-- Context
|
|
34313
34316
|
cwd TEXT,
|
|
34314
|
-
dir_scope TEXT DEFAULT 'project',
|
|
34317
|
+
dir_scope TEXT DEFAULT 'project' CHECK (dir_scope IN ('project', 'home')),
|
|
34315
34318
|
git_branch TEXT,
|
|
34316
34319
|
native_storage_path TEXT,
|
|
34317
34320
|
|
|
@@ -34321,7 +34324,8 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|
|
34321
34324
|
parent_session_id TEXT,
|
|
34322
34325
|
agent_id TEXT,
|
|
34323
34326
|
is_sidechain INTEGER DEFAULT 0,
|
|
34324
|
-
session_type TEXT DEFAULT '
|
|
34327
|
+
session_type TEXT DEFAULT 'main',
|
|
34328
|
+
slug TEXT,
|
|
34325
34329
|
version TEXT,
|
|
34326
34330
|
user_type TEXT DEFAULT 'external',
|
|
34327
34331
|
|
|
@@ -34344,8 +34348,13 @@ CREATE INDEX IF NOT EXISTS idx_sessions_provider ON sessions(provider);
|
|
|
34344
34348
|
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id);
|
|
34345
34349
|
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
34346
34350
|
CREATE INDEX IF NOT EXISTS idx_sessions_last_active ON sessions(last_active_at DESC);
|
|
34347
|
-
CREATE INDEX IF NOT EXISTS
|
|
34351
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_provider_session_unique
|
|
34352
|
+
ON sessions(provider, provider_session_id)
|
|
34353
|
+
WHERE provider_session_id IS NOT NULL AND status != 'deleted';
|
|
34348
34354
|
CREATE INDEX IF NOT EXISTS idx_sessions_cwd ON sessions(cwd);
|
|
34355
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
|
|
34356
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_id);
|
|
34357
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_type ON sessions(session_type);
|
|
34349
34358
|
|
|
34350
34359
|
-- Turns (individual user->assistant exchanges)
|
|
34351
34360
|
CREATE TABLE IF NOT EXISTS turns (
|
|
@@ -34353,6 +34362,7 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
34353
34362
|
session_id TEXT NOT NULL,
|
|
34354
34363
|
provider TEXT NOT NULL,
|
|
34355
34364
|
provider_session_id TEXT,
|
|
34365
|
+
provider_turn_id TEXT,
|
|
34356
34366
|
|
|
34357
34367
|
-- Sequence
|
|
34358
34368
|
turn_number INTEGER NOT NULL,
|
|
@@ -34365,6 +34375,7 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
34365
34375
|
-- Config at time of turn
|
|
34366
34376
|
model TEXT,
|
|
34367
34377
|
permission_mode TEXT,
|
|
34378
|
+
system_prompt TEXT,
|
|
34368
34379
|
|
|
34369
34380
|
-- Metrics
|
|
34370
34381
|
cost REAL,
|
|
@@ -34381,9 +34392,33 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
34381
34392
|
|
|
34382
34393
|
-- Rich metadata (JSON)
|
|
34383
34394
|
tools_used TEXT,
|
|
34395
|
+
tool_results TEXT,
|
|
34396
|
+
todos TEXT,
|
|
34397
|
+
thinking_config TEXT,
|
|
34398
|
+
image_ids TEXT,
|
|
34399
|
+
compact_metadata TEXT,
|
|
34400
|
+
|
|
34401
|
+
-- Turn linking
|
|
34402
|
+
parent_turn_id TEXT,
|
|
34403
|
+
uuid TEXT,
|
|
34404
|
+
logical_parent_id TEXT,
|
|
34405
|
+
leaf_uuid TEXT,
|
|
34406
|
+
|
|
34407
|
+
-- Message metadata
|
|
34408
|
+
user_type TEXT,
|
|
34409
|
+
is_meta INTEGER DEFAULT 0,
|
|
34410
|
+
display_only INTEGER DEFAULT 0,
|
|
34411
|
+
|
|
34412
|
+
-- API metadata
|
|
34413
|
+
service_tier TEXT,
|
|
34414
|
+
api_request_id TEXT,
|
|
34415
|
+
|
|
34416
|
+
-- Event classification
|
|
34417
|
+
kind TEXT DEFAULT 'message' CHECK (kind IN ('message', 'display', 'summary', 'tool', 'error')),
|
|
34384
34418
|
|
|
34385
34419
|
-- Timestamps
|
|
34386
34420
|
ts TEXT NOT NULL,
|
|
34421
|
+
ts_ms INTEGER,
|
|
34387
34422
|
|
|
34388
34423
|
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
34389
34424
|
);
|
|
@@ -34392,6 +34427,9 @@ CREATE INDEX IF NOT EXISTS idx_turns_session ON turns(session_id);
|
|
|
34392
34427
|
CREATE INDEX IF NOT EXISTS idx_turns_ts ON turns(ts DESC);
|
|
34393
34428
|
CREATE INDEX IF NOT EXISTS idx_turns_model ON turns(model);
|
|
34394
34429
|
CREATE INDEX IF NOT EXISTS idx_turns_session_number ON turns(session_id, turn_number);
|
|
34430
|
+
CREATE INDEX IF NOT EXISTS idx_turns_session_ts_ms ON turns(session_id, ts_ms);
|
|
34431
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_turns_provider_dedup
|
|
34432
|
+
ON turns(session_id, provider_turn_id) WHERE provider_turn_id IS NOT NULL;
|
|
34395
34433
|
|
|
34396
34434
|
-- Full-text search on turns
|
|
34397
34435
|
CREATE VIRTUAL TABLE IF NOT EXISTS turns_fts USING fts5(
|
|
@@ -34453,6 +34491,160 @@ CREATE TABLE IF NOT EXISTS model_pricing (
|
|
|
34453
34491
|
CREATE INDEX IF NOT EXISTS idx_model_pricing_provider ON model_pricing(provider);
|
|
34454
34492
|
CREATE INDEX IF NOT EXISTS idx_model_pricing_pattern ON model_pricing(model_pattern);
|
|
34455
34493
|
|
|
34494
|
+
-- =============================================================================
|
|
34495
|
+
-- FILE POSITIONS (session file tailing)
|
|
34496
|
+
-- =============================================================================
|
|
34497
|
+
|
|
34498
|
+
CREATE TABLE IF NOT EXISTS file_positions (
|
|
34499
|
+
file_path TEXT PRIMARY KEY,
|
|
34500
|
+
byte_offset INTEGER NOT NULL DEFAULT 0,
|
|
34501
|
+
file_size INTEGER NOT NULL DEFAULT 0,
|
|
34502
|
+
mtime_ms INTEGER NOT NULL DEFAULT 0,
|
|
34503
|
+
inode TEXT,
|
|
34504
|
+
provider TEXT NOT NULL CHECK (provider IN ('claude', 'codex', 'gemini', 'ollama')),
|
|
34505
|
+
last_synced_at TEXT NOT NULL,
|
|
34506
|
+
created_at TEXT NOT NULL
|
|
34507
|
+
);
|
|
34508
|
+
|
|
34509
|
+
CREATE INDEX IF NOT EXISTS idx_file_positions_provider ON file_positions(provider);
|
|
34510
|
+
|
|
34511
|
+
-- =============================================================================
|
|
34512
|
+
-- FILE HISTORY (tracked files / revisions)
|
|
34513
|
+
-- =============================================================================
|
|
34514
|
+
|
|
34515
|
+
CREATE TABLE IF NOT EXISTS tracked_files (
|
|
34516
|
+
id TEXT PRIMARY KEY,
|
|
34517
|
+
current_path TEXT NOT NULL,
|
|
34518
|
+
risk_level TEXT DEFAULT 'low' CHECK (risk_level IN ('low', 'medium', 'high')),
|
|
34519
|
+
created_at TEXT NOT NULL,
|
|
34520
|
+
deleted_at TEXT
|
|
34521
|
+
);
|
|
34522
|
+
|
|
34523
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tracked_files_path_active
|
|
34524
|
+
ON tracked_files(current_path) WHERE deleted_at IS NULL;
|
|
34525
|
+
CREATE INDEX IF NOT EXISTS idx_tracked_files_path ON tracked_files(current_path);
|
|
34526
|
+
CREATE INDEX IF NOT EXISTS idx_tracked_files_active ON tracked_files(deleted_at) WHERE deleted_at IS NULL;
|
|
34527
|
+
|
|
34528
|
+
CREATE TABLE IF NOT EXISTS file_revisions (
|
|
34529
|
+
id TEXT PRIMARY KEY,
|
|
34530
|
+
file_id TEXT NOT NULL,
|
|
34531
|
+
revision_number INTEGER NOT NULL,
|
|
34532
|
+
parent_revision_id TEXT,
|
|
34533
|
+
content_hash TEXT NOT NULL,
|
|
34534
|
+
size_bytes INTEGER NOT NULL,
|
|
34535
|
+
kind TEXT NOT NULL CHECK (kind IN ('edit', 'revert', 'import', 'external', 'delete')),
|
|
34536
|
+
author TEXT NOT NULL CHECK (author IN ('agent', 'user', 'external', 'system')),
|
|
34537
|
+
summary TEXT,
|
|
34538
|
+
is_binary INTEGER DEFAULT 0 CHECK (is_binary IN (0, 1)),
|
|
34539
|
+
reverted_to_revision_id TEXT,
|
|
34540
|
+
created_at TEXT NOT NULL,
|
|
34541
|
+
path_at_revision TEXT NOT NULL,
|
|
34542
|
+
FOREIGN KEY (file_id) REFERENCES tracked_files(id) ON DELETE CASCADE,
|
|
34543
|
+
FOREIGN KEY (parent_revision_id) REFERENCES file_revisions(id),
|
|
34544
|
+
FOREIGN KEY (reverted_to_revision_id) REFERENCES file_revisions(id)
|
|
34545
|
+
);
|
|
34546
|
+
|
|
34547
|
+
CREATE INDEX IF NOT EXISTS idx_file_revisions_file ON file_revisions(file_id, created_at DESC);
|
|
34548
|
+
CREATE INDEX IF NOT EXISTS idx_file_revisions_file_rev ON file_revisions(file_id, revision_number DESC);
|
|
34549
|
+
CREATE INDEX IF NOT EXISTS idx_file_revisions_hash ON file_revisions(content_hash);
|
|
34550
|
+
CREATE INDEX IF NOT EXISTS idx_file_revisions_path ON file_revisions(path_at_revision);
|
|
34551
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_file_revisions_number ON file_revisions(file_id, revision_number);
|
|
34552
|
+
|
|
34553
|
+
-- =============================================================================
|
|
34554
|
+
-- FILE CHANGES / SYSTEM EVENTS
|
|
34555
|
+
-- =============================================================================
|
|
34556
|
+
|
|
34557
|
+
CREATE TABLE IF NOT EXISTS file_changes (
|
|
34558
|
+
id TEXT PRIMARY KEY,
|
|
34559
|
+
session_id TEXT NOT NULL,
|
|
34560
|
+
turn_id TEXT,
|
|
34561
|
+
file_path TEXT NOT NULL,
|
|
34562
|
+
operation TEXT NOT NULL,
|
|
34563
|
+
content_before_hash TEXT,
|
|
34564
|
+
content_after_hash TEXT,
|
|
34565
|
+
diff_summary TEXT,
|
|
34566
|
+
ts TEXT NOT NULL,
|
|
34567
|
+
ts_ms INTEGER,
|
|
34568
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
34569
|
+
);
|
|
34570
|
+
|
|
34571
|
+
CREATE INDEX IF NOT EXISTS idx_file_changes_session ON file_changes(session_id);
|
|
34572
|
+
CREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(file_path);
|
|
34573
|
+
CREATE INDEX IF NOT EXISTS idx_file_changes_ts ON file_changes(ts_ms);
|
|
34574
|
+
|
|
34575
|
+
CREATE TABLE IF NOT EXISTS system_events (
|
|
34576
|
+
id TEXT PRIMARY KEY,
|
|
34577
|
+
session_id TEXT NOT NULL,
|
|
34578
|
+
event_type TEXT NOT NULL,
|
|
34579
|
+
payload TEXT,
|
|
34580
|
+
ts TEXT NOT NULL,
|
|
34581
|
+
ts_ms INTEGER,
|
|
34582
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
34583
|
+
);
|
|
34584
|
+
|
|
34585
|
+
CREATE INDEX IF NOT EXISTS idx_system_events_session ON system_events(session_id);
|
|
34586
|
+
CREATE INDEX IF NOT EXISTS idx_system_events_type ON system_events(event_type);
|
|
34587
|
+
|
|
34588
|
+
-- =============================================================================
|
|
34589
|
+
-- SESSION RUNTIME
|
|
34590
|
+
-- =============================================================================
|
|
34591
|
+
|
|
34592
|
+
CREATE TABLE IF NOT EXISTS session_runtime_state (
|
|
34593
|
+
session_id TEXT PRIMARY KEY,
|
|
34594
|
+
status TEXT NOT NULL CHECK(status IN ('running','completed','error','stopped')),
|
|
34595
|
+
provider TEXT,
|
|
34596
|
+
provider_session_id TEXT,
|
|
34597
|
+
started_at TEXT NOT NULL,
|
|
34598
|
+
updated_at TEXT NOT NULL,
|
|
34599
|
+
completed_at TEXT,
|
|
34600
|
+
last_seq INTEGER NOT NULL DEFAULT 0,
|
|
34601
|
+
cost_total REAL NOT NULL DEFAULT 0,
|
|
34602
|
+
tokens_total INTEGER NOT NULL DEFAULT 0,
|
|
34603
|
+
unseen_completion INTEGER NOT NULL DEFAULT 0,
|
|
34604
|
+
last_error TEXT
|
|
34605
|
+
);
|
|
34606
|
+
|
|
34607
|
+
CREATE TABLE IF NOT EXISTS session_runtime_events (
|
|
34608
|
+
session_id TEXT NOT NULL,
|
|
34609
|
+
seq INTEGER NOT NULL,
|
|
34610
|
+
type TEXT NOT NULL,
|
|
34611
|
+
payload_json TEXT NOT NULL,
|
|
34612
|
+
ts TEXT NOT NULL,
|
|
34613
|
+
PRIMARY KEY (session_id, seq)
|
|
34614
|
+
);
|
|
34615
|
+
|
|
34616
|
+
CREATE INDEX IF NOT EXISTS idx_session_runtime_events_session_ts
|
|
34617
|
+
ON session_runtime_events(session_id, ts);
|
|
34618
|
+
|
|
34619
|
+
-- =============================================================================
|
|
34620
|
+
-- OBSERVABILITY LOGS
|
|
34621
|
+
-- =============================================================================
|
|
34622
|
+
|
|
34623
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
34624
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34625
|
+
timestamp INTEGER NOT NULL,
|
|
34626
|
+
source TEXT NOT NULL,
|
|
34627
|
+
level TEXT NOT NULL CHECK (level IN ('debug', 'info', 'warn', 'error')),
|
|
34628
|
+
type TEXT NOT NULL,
|
|
34629
|
+
provider TEXT,
|
|
34630
|
+
cid TEXT,
|
|
34631
|
+
session_id TEXT,
|
|
34632
|
+
terminal_id INTEGER,
|
|
34633
|
+
feature TEXT,
|
|
34634
|
+
step TEXT,
|
|
34635
|
+
duration_ms INTEGER,
|
|
34636
|
+
data_json TEXT NOT NULL,
|
|
34637
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
34638
|
+
);
|
|
34639
|
+
|
|
34640
|
+
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp DESC);
|
|
34641
|
+
CREATE INDEX IF NOT EXISTS idx_logs_source ON logs(source);
|
|
34642
|
+
CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);
|
|
34643
|
+
CREATE INDEX IF NOT EXISTS idx_logs_type ON logs(type);
|
|
34644
|
+
CREATE INDEX IF NOT EXISTS idx_logs_provider ON logs(provider);
|
|
34645
|
+
CREATE INDEX IF NOT EXISTS idx_logs_session ON logs(session_id);
|
|
34646
|
+
CREATE INDEX IF NOT EXISTS idx_logs_duration ON logs(duration_ms) WHERE duration_ms IS NOT NULL;
|
|
34647
|
+
|
|
34456
34648
|
-- =============================================================================
|
|
34457
34649
|
-- PACKAGES (stacks, prompts, runtimes, binaries, agents)
|
|
34458
34650
|
-- =============================================================================
|
|
@@ -34568,16 +34760,15 @@ CREATE TABLE IF NOT EXISTS secrets_meta (
|
|
|
34568
34760
|
last_used_at TEXT
|
|
34569
34761
|
);
|
|
34570
34762
|
`;
|
|
34571
|
-
function
|
|
34572
|
-
const db3 = getDb();
|
|
34763
|
+
function initSchemaWithDb(db3) {
|
|
34573
34764
|
const hasVersionTable = db3.prepare(`
|
|
34574
34765
|
SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'
|
|
34575
34766
|
`).get();
|
|
34576
34767
|
if (!hasVersionTable) {
|
|
34577
34768
|
console.log("Initializing database schema...");
|
|
34578
34769
|
db3.exec(SCHEMA_SQL);
|
|
34770
|
+
applySchemaUpdates(db3);
|
|
34579
34771
|
db3.prepare("INSERT INTO schema_version (version, applied_at) VALUES (?, ?)").run(SCHEMA_VERSION, (/* @__PURE__ */ new Date()).toISOString());
|
|
34580
|
-
seedModelPricing(db3);
|
|
34581
34772
|
console.log(`Database initialized at schema version ${SCHEMA_VERSION}`);
|
|
34582
34773
|
return { version: SCHEMA_VERSION, migrated: false };
|
|
34583
34774
|
}
|
|
@@ -34586,8 +34777,380 @@ function initSchema() {
|
|
|
34586
34777
|
runMigrations(db3, currentVersion, SCHEMA_VERSION);
|
|
34587
34778
|
return { version: SCHEMA_VERSION, migrated: true, from: currentVersion };
|
|
34588
34779
|
}
|
|
34780
|
+
db3.exec(SCHEMA_SQL);
|
|
34781
|
+
applySchemaUpdates(db3);
|
|
34589
34782
|
return { version: currentVersion, migrated: false };
|
|
34590
34783
|
}
|
|
34784
|
+
function initSchema() {
|
|
34785
|
+
return initSchemaWithDb(getDb());
|
|
34786
|
+
}
|
|
34787
|
+
function applySchemaUpdates(db3) {
|
|
34788
|
+
if (tableExists(db3, "projects")) {
|
|
34789
|
+
ensureColumn(db3, "projects", "settings", "ALTER TABLE projects ADD COLUMN settings TEXT");
|
|
34790
|
+
}
|
|
34791
|
+
if (tableExists(db3, "sessions")) {
|
|
34792
|
+
ensureColumn(db3, "sessions", "title_override", "ALTER TABLE sessions ADD COLUMN title_override TEXT");
|
|
34793
|
+
ensureColumn(db3, "sessions", "system_prompt", "ALTER TABLE sessions ADD COLUMN system_prompt TEXT");
|
|
34794
|
+
ensureColumn(
|
|
34795
|
+
db3,
|
|
34796
|
+
"sessions",
|
|
34797
|
+
"dir_scope",
|
|
34798
|
+
"ALTER TABLE sessions ADD COLUMN dir_scope TEXT DEFAULT 'project' CHECK (dir_scope IN ('project', 'home'))"
|
|
34799
|
+
);
|
|
34800
|
+
ensureColumn(
|
|
34801
|
+
db3,
|
|
34802
|
+
"sessions",
|
|
34803
|
+
"inherit_project_prompt",
|
|
34804
|
+
"ALTER TABLE sessions ADD COLUMN inherit_project_prompt INTEGER DEFAULT 1"
|
|
34805
|
+
);
|
|
34806
|
+
ensureColumn(db3, "sessions", "is_warmup", "ALTER TABLE sessions ADD COLUMN is_warmup INTEGER DEFAULT 0");
|
|
34807
|
+
ensureColumn(db3, "sessions", "parent_session_id", "ALTER TABLE sessions ADD COLUMN parent_session_id TEXT");
|
|
34808
|
+
ensureColumn(db3, "sessions", "agent_id", "ALTER TABLE sessions ADD COLUMN agent_id TEXT");
|
|
34809
|
+
ensureColumn(db3, "sessions", "is_sidechain", "ALTER TABLE sessions ADD COLUMN is_sidechain INTEGER DEFAULT 0");
|
|
34810
|
+
ensureColumn(
|
|
34811
|
+
db3,
|
|
34812
|
+
"sessions",
|
|
34813
|
+
"session_type",
|
|
34814
|
+
"ALTER TABLE sessions ADD COLUMN session_type TEXT DEFAULT 'main'"
|
|
34815
|
+
);
|
|
34816
|
+
ensureColumn(db3, "sessions", "slug", "ALTER TABLE sessions ADD COLUMN slug TEXT");
|
|
34817
|
+
ensureColumn(db3, "sessions", "version", "ALTER TABLE sessions ADD COLUMN version TEXT");
|
|
34818
|
+
ensureColumn(
|
|
34819
|
+
db3,
|
|
34820
|
+
"sessions",
|
|
34821
|
+
"user_type",
|
|
34822
|
+
"ALTER TABLE sessions ADD COLUMN user_type TEXT DEFAULT 'external'"
|
|
34823
|
+
);
|
|
34824
|
+
if (columnExists(db3, "sessions", "session_type")) {
|
|
34825
|
+
db3.exec("UPDATE sessions SET session_type = 'main' WHERE session_type = 'task'");
|
|
34826
|
+
}
|
|
34827
|
+
ensureIndex(db3, "idx_sessions_parent", "CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id)");
|
|
34828
|
+
ensureIndex(db3, "idx_sessions_agent", "CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent_id)");
|
|
34829
|
+
ensureIndex(db3, "idx_sessions_type", "CREATE INDEX IF NOT EXISTS idx_sessions_type ON sessions(session_type)");
|
|
34830
|
+
if (!indexExists(db3, "idx_sessions_provider_session_unique")) {
|
|
34831
|
+
dedupeProviderSessions(db3);
|
|
34832
|
+
db3.exec("DROP INDEX IF EXISTS idx_sessions_provider_session");
|
|
34833
|
+
db3.exec(`
|
|
34834
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_provider_session_unique
|
|
34835
|
+
ON sessions(provider, provider_session_id)
|
|
34836
|
+
WHERE provider_session_id IS NOT NULL AND status != 'deleted'
|
|
34837
|
+
`);
|
|
34838
|
+
}
|
|
34839
|
+
}
|
|
34840
|
+
if (tableExists(db3, "turns")) {
|
|
34841
|
+
ensureColumn(db3, "turns", "provider_turn_id", "ALTER TABLE turns ADD COLUMN provider_turn_id TEXT");
|
|
34842
|
+
ensureColumn(db3, "turns", "system_prompt", "ALTER TABLE turns ADD COLUMN system_prompt TEXT");
|
|
34843
|
+
ensureColumn(db3, "turns", "parent_turn_id", "ALTER TABLE turns ADD COLUMN parent_turn_id TEXT");
|
|
34844
|
+
ensureColumn(db3, "turns", "uuid", "ALTER TABLE turns ADD COLUMN uuid TEXT");
|
|
34845
|
+
ensureColumn(db3, "turns", "service_tier", "ALTER TABLE turns ADD COLUMN service_tier TEXT");
|
|
34846
|
+
ensureColumn(db3, "turns", "api_request_id", "ALTER TABLE turns ADD COLUMN api_request_id TEXT");
|
|
34847
|
+
ensureColumn(db3, "turns", "tool_results", "ALTER TABLE turns ADD COLUMN tool_results TEXT");
|
|
34848
|
+
ensureColumn(db3, "turns", "user_type", "ALTER TABLE turns ADD COLUMN user_type TEXT");
|
|
34849
|
+
ensureColumn(db3, "turns", "is_meta", "ALTER TABLE turns ADD COLUMN is_meta INTEGER DEFAULT 0");
|
|
34850
|
+
ensureColumn(db3, "turns", "display_only", "ALTER TABLE turns ADD COLUMN display_only INTEGER DEFAULT 0");
|
|
34851
|
+
ensureColumn(db3, "turns", "todos", "ALTER TABLE turns ADD COLUMN todos TEXT");
|
|
34852
|
+
ensureColumn(db3, "turns", "thinking_config", "ALTER TABLE turns ADD COLUMN thinking_config TEXT");
|
|
34853
|
+
ensureColumn(db3, "turns", "image_ids", "ALTER TABLE turns ADD COLUMN image_ids TEXT");
|
|
34854
|
+
ensureColumn(db3, "turns", "compact_metadata", "ALTER TABLE turns ADD COLUMN compact_metadata TEXT");
|
|
34855
|
+
ensureColumn(db3, "turns", "logical_parent_id", "ALTER TABLE turns ADD COLUMN logical_parent_id TEXT");
|
|
34856
|
+
ensureColumn(db3, "turns", "leaf_uuid", "ALTER TABLE turns ADD COLUMN leaf_uuid TEXT");
|
|
34857
|
+
if (!columnExists(db3, "turns", "ts_ms")) {
|
|
34858
|
+
db3.exec("ALTER TABLE turns ADD COLUMN ts_ms INTEGER");
|
|
34859
|
+
db3.exec(`
|
|
34860
|
+
UPDATE turns
|
|
34861
|
+
SET ts_ms = CASE
|
|
34862
|
+
WHEN ts GLOB '[0-9]*' AND LENGTH(ts) >= 13 THEN CAST(ts AS INTEGER)
|
|
34863
|
+
WHEN ts LIKE '____-__-__T__:__:__*' THEN
|
|
34864
|
+
CAST((julianday(SUBSTR(ts, 1, 19)) - julianday('1970-01-01')) * 86400000 AS INTEGER)
|
|
34865
|
+
ELSE CAST((julianday(ts) - julianday('1970-01-01')) * 86400000 AS INTEGER)
|
|
34866
|
+
END
|
|
34867
|
+
WHERE ts_ms IS NULL AND ts IS NOT NULL
|
|
34868
|
+
`);
|
|
34869
|
+
}
|
|
34870
|
+
if (columnExists(db3, "turns", "ts_ms")) {
|
|
34871
|
+
ensureIndex(
|
|
34872
|
+
db3,
|
|
34873
|
+
"idx_turns_session_ts_ms",
|
|
34874
|
+
"CREATE INDEX IF NOT EXISTS idx_turns_session_ts_ms ON turns(session_id, ts_ms)"
|
|
34875
|
+
);
|
|
34876
|
+
}
|
|
34877
|
+
if (!columnExists(db3, "turns", "kind")) {
|
|
34878
|
+
db3.exec("ALTER TABLE turns ADD COLUMN kind TEXT DEFAULT 'message' CHECK (kind IN ('message', 'display', 'summary', 'tool', 'error'))");
|
|
34879
|
+
db3.exec(`
|
|
34880
|
+
UPDATE turns SET kind = 'display'
|
|
34881
|
+
WHERE user_message LIKE '[display: %]' AND assistant_response IS NULL
|
|
34882
|
+
`);
|
|
34883
|
+
}
|
|
34884
|
+
if (columnExists(db3, "turns", "provider_turn_id")) {
|
|
34885
|
+
ensureIndex(
|
|
34886
|
+
db3,
|
|
34887
|
+
"idx_turns_provider_dedup",
|
|
34888
|
+
"CREATE UNIQUE INDEX IF NOT EXISTS idx_turns_provider_dedup ON turns(session_id, provider_turn_id) WHERE provider_turn_id IS NOT NULL"
|
|
34889
|
+
);
|
|
34890
|
+
}
|
|
34891
|
+
}
|
|
34892
|
+
ensureTable(db3, "file_positions", `
|
|
34893
|
+
CREATE TABLE IF NOT EXISTS file_positions (
|
|
34894
|
+
file_path TEXT PRIMARY KEY,
|
|
34895
|
+
byte_offset INTEGER NOT NULL DEFAULT 0,
|
|
34896
|
+
file_size INTEGER NOT NULL DEFAULT 0,
|
|
34897
|
+
mtime_ms INTEGER NOT NULL DEFAULT 0,
|
|
34898
|
+
inode TEXT,
|
|
34899
|
+
provider TEXT NOT NULL CHECK (provider IN ('claude', 'codex', 'gemini', 'ollama')),
|
|
34900
|
+
last_synced_at TEXT NOT NULL,
|
|
34901
|
+
created_at TEXT NOT NULL
|
|
34902
|
+
);
|
|
34903
|
+
`);
|
|
34904
|
+
ensureIndex(
|
|
34905
|
+
db3,
|
|
34906
|
+
"idx_file_positions_provider",
|
|
34907
|
+
"CREATE INDEX IF NOT EXISTS idx_file_positions_provider ON file_positions(provider)"
|
|
34908
|
+
);
|
|
34909
|
+
ensureTable(db3, "tracked_files", `
|
|
34910
|
+
CREATE TABLE IF NOT EXISTS tracked_files (
|
|
34911
|
+
id TEXT PRIMARY KEY,
|
|
34912
|
+
current_path TEXT NOT NULL,
|
|
34913
|
+
risk_level TEXT DEFAULT 'low' CHECK (risk_level IN ('low', 'medium', 'high')),
|
|
34914
|
+
created_at TEXT NOT NULL,
|
|
34915
|
+
deleted_at TEXT
|
|
34916
|
+
);
|
|
34917
|
+
`);
|
|
34918
|
+
ensureIndex(
|
|
34919
|
+
db3,
|
|
34920
|
+
"idx_tracked_files_path_active",
|
|
34921
|
+
"CREATE UNIQUE INDEX IF NOT EXISTS idx_tracked_files_path_active ON tracked_files(current_path) WHERE deleted_at IS NULL"
|
|
34922
|
+
);
|
|
34923
|
+
ensureIndex(
|
|
34924
|
+
db3,
|
|
34925
|
+
"idx_tracked_files_path",
|
|
34926
|
+
"CREATE INDEX IF NOT EXISTS idx_tracked_files_path ON tracked_files(current_path)"
|
|
34927
|
+
);
|
|
34928
|
+
ensureIndex(
|
|
34929
|
+
db3,
|
|
34930
|
+
"idx_tracked_files_active",
|
|
34931
|
+
"CREATE INDEX IF NOT EXISTS idx_tracked_files_active ON tracked_files(deleted_at) WHERE deleted_at IS NULL"
|
|
34932
|
+
);
|
|
34933
|
+
ensureTable(db3, "file_revisions", `
|
|
34934
|
+
CREATE TABLE IF NOT EXISTS file_revisions (
|
|
34935
|
+
id TEXT PRIMARY KEY,
|
|
34936
|
+
file_id TEXT NOT NULL,
|
|
34937
|
+
revision_number INTEGER NOT NULL,
|
|
34938
|
+
parent_revision_id TEXT,
|
|
34939
|
+
content_hash TEXT NOT NULL,
|
|
34940
|
+
size_bytes INTEGER NOT NULL,
|
|
34941
|
+
kind TEXT NOT NULL CHECK (kind IN ('edit', 'revert', 'import', 'external', 'delete')),
|
|
34942
|
+
author TEXT NOT NULL CHECK (author IN ('agent', 'user', 'external', 'system')),
|
|
34943
|
+
summary TEXT,
|
|
34944
|
+
is_binary INTEGER DEFAULT 0 CHECK (is_binary IN (0, 1)),
|
|
34945
|
+
reverted_to_revision_id TEXT,
|
|
34946
|
+
created_at TEXT NOT NULL,
|
|
34947
|
+
path_at_revision TEXT NOT NULL,
|
|
34948
|
+
FOREIGN KEY (file_id) REFERENCES tracked_files(id) ON DELETE CASCADE,
|
|
34949
|
+
FOREIGN KEY (parent_revision_id) REFERENCES file_revisions(id),
|
|
34950
|
+
FOREIGN KEY (reverted_to_revision_id) REFERENCES file_revisions(id)
|
|
34951
|
+
);
|
|
34952
|
+
`);
|
|
34953
|
+
ensureIndex(
|
|
34954
|
+
db3,
|
|
34955
|
+
"idx_file_revisions_file",
|
|
34956
|
+
"CREATE INDEX IF NOT EXISTS idx_file_revisions_file ON file_revisions(file_id, created_at DESC)"
|
|
34957
|
+
);
|
|
34958
|
+
ensureIndex(
|
|
34959
|
+
db3,
|
|
34960
|
+
"idx_file_revisions_file_rev",
|
|
34961
|
+
"CREATE INDEX IF NOT EXISTS idx_file_revisions_file_rev ON file_revisions(file_id, revision_number DESC)"
|
|
34962
|
+
);
|
|
34963
|
+
ensureIndex(
|
|
34964
|
+
db3,
|
|
34965
|
+
"idx_file_revisions_hash",
|
|
34966
|
+
"CREATE INDEX IF NOT EXISTS idx_file_revisions_hash ON file_revisions(content_hash)"
|
|
34967
|
+
);
|
|
34968
|
+
ensureIndex(
|
|
34969
|
+
db3,
|
|
34970
|
+
"idx_file_revisions_path",
|
|
34971
|
+
"CREATE INDEX IF NOT EXISTS idx_file_revisions_path ON file_revisions(path_at_revision)"
|
|
34972
|
+
);
|
|
34973
|
+
ensureIndex(
|
|
34974
|
+
db3,
|
|
34975
|
+
"idx_file_revisions_number",
|
|
34976
|
+
"CREATE UNIQUE INDEX IF NOT EXISTS idx_file_revisions_number ON file_revisions(file_id, revision_number)"
|
|
34977
|
+
);
|
|
34978
|
+
if (tableExists(db3, "file_revisions")) {
|
|
34979
|
+
ensureColumn(
|
|
34980
|
+
db3,
|
|
34981
|
+
"file_revisions",
|
|
34982
|
+
"is_binary",
|
|
34983
|
+
"ALTER TABLE file_revisions ADD COLUMN is_binary INTEGER DEFAULT 0 CHECK (is_binary IN (0, 1))"
|
|
34984
|
+
);
|
|
34985
|
+
}
|
|
34986
|
+
ensureTable(db3, "file_changes", `
|
|
34987
|
+
CREATE TABLE IF NOT EXISTS file_changes (
|
|
34988
|
+
id TEXT PRIMARY KEY,
|
|
34989
|
+
session_id TEXT NOT NULL,
|
|
34990
|
+
turn_id TEXT,
|
|
34991
|
+
file_path TEXT NOT NULL,
|
|
34992
|
+
operation TEXT NOT NULL,
|
|
34993
|
+
content_before_hash TEXT,
|
|
34994
|
+
content_after_hash TEXT,
|
|
34995
|
+
diff_summary TEXT,
|
|
34996
|
+
ts TEXT NOT NULL,
|
|
34997
|
+
ts_ms INTEGER,
|
|
34998
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
34999
|
+
);
|
|
35000
|
+
`);
|
|
35001
|
+
ensureIndex(
|
|
35002
|
+
db3,
|
|
35003
|
+
"idx_file_changes_session",
|
|
35004
|
+
"CREATE INDEX IF NOT EXISTS idx_file_changes_session ON file_changes(session_id)"
|
|
35005
|
+
);
|
|
35006
|
+
ensureIndex(
|
|
35007
|
+
db3,
|
|
35008
|
+
"idx_file_changes_path",
|
|
35009
|
+
"CREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(file_path)"
|
|
35010
|
+
);
|
|
35011
|
+
ensureIndex(
|
|
35012
|
+
db3,
|
|
35013
|
+
"idx_file_changes_ts",
|
|
35014
|
+
"CREATE INDEX IF NOT EXISTS idx_file_changes_ts ON file_changes(ts_ms)"
|
|
35015
|
+
);
|
|
35016
|
+
ensureTable(db3, "system_events", `
|
|
35017
|
+
CREATE TABLE IF NOT EXISTS system_events (
|
|
35018
|
+
id TEXT PRIMARY KEY,
|
|
35019
|
+
session_id TEXT NOT NULL,
|
|
35020
|
+
event_type TEXT NOT NULL,
|
|
35021
|
+
payload TEXT,
|
|
35022
|
+
ts TEXT NOT NULL,
|
|
35023
|
+
ts_ms INTEGER,
|
|
35024
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
35025
|
+
);
|
|
35026
|
+
`);
|
|
35027
|
+
ensureIndex(
|
|
35028
|
+
db3,
|
|
35029
|
+
"idx_system_events_session",
|
|
35030
|
+
"CREATE INDEX IF NOT EXISTS idx_system_events_session ON system_events(session_id)"
|
|
35031
|
+
);
|
|
35032
|
+
ensureIndex(
|
|
35033
|
+
db3,
|
|
35034
|
+
"idx_system_events_type",
|
|
35035
|
+
"CREATE INDEX IF NOT EXISTS idx_system_events_type ON system_events(event_type)"
|
|
35036
|
+
);
|
|
35037
|
+
ensureTable(db3, "session_runtime_state", `
|
|
35038
|
+
CREATE TABLE IF NOT EXISTS session_runtime_state (
|
|
35039
|
+
session_id TEXT PRIMARY KEY,
|
|
35040
|
+
status TEXT NOT NULL CHECK(status IN ('running','completed','error','stopped')),
|
|
35041
|
+
provider TEXT,
|
|
35042
|
+
provider_session_id TEXT,
|
|
35043
|
+
started_at TEXT NOT NULL,
|
|
35044
|
+
updated_at TEXT NOT NULL,
|
|
35045
|
+
completed_at TEXT,
|
|
35046
|
+
last_seq INTEGER NOT NULL DEFAULT 0,
|
|
35047
|
+
cost_total REAL NOT NULL DEFAULT 0,
|
|
35048
|
+
tokens_total INTEGER NOT NULL DEFAULT 0,
|
|
35049
|
+
unseen_completion INTEGER NOT NULL DEFAULT 0,
|
|
35050
|
+
last_error TEXT
|
|
35051
|
+
)
|
|
35052
|
+
`);
|
|
35053
|
+
ensureTable(db3, "session_runtime_events", `
|
|
35054
|
+
CREATE TABLE IF NOT EXISTS session_runtime_events (
|
|
35055
|
+
session_id TEXT NOT NULL,
|
|
35056
|
+
seq INTEGER NOT NULL,
|
|
35057
|
+
type TEXT NOT NULL,
|
|
35058
|
+
payload_json TEXT NOT NULL,
|
|
35059
|
+
ts TEXT NOT NULL,
|
|
35060
|
+
PRIMARY KEY (session_id, seq)
|
|
35061
|
+
);
|
|
35062
|
+
`);
|
|
35063
|
+
ensureIndex(
|
|
35064
|
+
db3,
|
|
35065
|
+
"idx_session_runtime_events_session_ts",
|
|
35066
|
+
"CREATE INDEX IF NOT EXISTS idx_session_runtime_events_session_ts ON session_runtime_events(session_id, ts)"
|
|
35067
|
+
);
|
|
35068
|
+
ensureTable(db3, "logs", `
|
|
35069
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
35070
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35071
|
+
timestamp INTEGER NOT NULL,
|
|
35072
|
+
source TEXT NOT NULL,
|
|
35073
|
+
level TEXT NOT NULL CHECK (level IN ('debug', 'info', 'warn', 'error')),
|
|
35074
|
+
type TEXT NOT NULL,
|
|
35075
|
+
provider TEXT,
|
|
35076
|
+
cid TEXT,
|
|
35077
|
+
session_id TEXT,
|
|
35078
|
+
terminal_id INTEGER,
|
|
35079
|
+
feature TEXT,
|
|
35080
|
+
step TEXT,
|
|
35081
|
+
duration_ms INTEGER,
|
|
35082
|
+
data_json TEXT NOT NULL,
|
|
35083
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
35084
|
+
);
|
|
35085
|
+
`);
|
|
35086
|
+
ensureIndex(
|
|
35087
|
+
db3,
|
|
35088
|
+
"idx_logs_timestamp",
|
|
35089
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp DESC)"
|
|
35090
|
+
);
|
|
35091
|
+
ensureIndex(
|
|
35092
|
+
db3,
|
|
35093
|
+
"idx_logs_source",
|
|
35094
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_source ON logs(source)"
|
|
35095
|
+
);
|
|
35096
|
+
ensureIndex(
|
|
35097
|
+
db3,
|
|
35098
|
+
"idx_logs_level",
|
|
35099
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level)"
|
|
35100
|
+
);
|
|
35101
|
+
ensureIndex(
|
|
35102
|
+
db3,
|
|
35103
|
+
"idx_logs_type",
|
|
35104
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_type ON logs(type)"
|
|
35105
|
+
);
|
|
35106
|
+
ensureIndex(
|
|
35107
|
+
db3,
|
|
35108
|
+
"idx_logs_provider",
|
|
35109
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_provider ON logs(provider)"
|
|
35110
|
+
);
|
|
35111
|
+
ensureIndex(
|
|
35112
|
+
db3,
|
|
35113
|
+
"idx_logs_session",
|
|
35114
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_session ON logs(session_id)"
|
|
35115
|
+
);
|
|
35116
|
+
ensureIndex(
|
|
35117
|
+
db3,
|
|
35118
|
+
"idx_logs_duration",
|
|
35119
|
+
"CREATE INDEX IF NOT EXISTS idx_logs_duration ON logs(duration_ms) WHERE duration_ms IS NOT NULL"
|
|
35120
|
+
);
|
|
35121
|
+
ensureTable(db3, "model_pricing", `
|
|
35122
|
+
CREATE TABLE IF NOT EXISTS model_pricing (
|
|
35123
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35124
|
+
provider TEXT NOT NULL CHECK (provider IN ('claude', 'codex', 'gemini', 'openai', 'ollama')),
|
|
35125
|
+
model_pattern TEXT NOT NULL,
|
|
35126
|
+
display_name TEXT,
|
|
35127
|
+
input_cost_per_mtok REAL NOT NULL,
|
|
35128
|
+
output_cost_per_mtok REAL NOT NULL,
|
|
35129
|
+
cache_read_cost_per_mtok REAL DEFAULT 0,
|
|
35130
|
+
cache_write_cost_per_mtok REAL DEFAULT 0,
|
|
35131
|
+
effective_from TEXT NOT NULL,
|
|
35132
|
+
effective_until TEXT,
|
|
35133
|
+
notes TEXT,
|
|
35134
|
+
UNIQUE(provider, model_pattern, effective_from)
|
|
35135
|
+
);
|
|
35136
|
+
`);
|
|
35137
|
+
ensureIndex(
|
|
35138
|
+
db3,
|
|
35139
|
+
"idx_model_pricing_provider",
|
|
35140
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_provider ON model_pricing(provider)"
|
|
35141
|
+
);
|
|
35142
|
+
ensureIndex(
|
|
35143
|
+
db3,
|
|
35144
|
+
"idx_model_pricing_pattern",
|
|
35145
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_pattern ON model_pricing(model_pattern)"
|
|
35146
|
+
);
|
|
35147
|
+
if (tableExists(db3, "model_pricing")) {
|
|
35148
|
+
const count = db3.prepare("SELECT COUNT(*) as count FROM model_pricing").get();
|
|
35149
|
+
if (count && count.count === 0) {
|
|
35150
|
+
seedModelPricing(db3);
|
|
35151
|
+
}
|
|
35152
|
+
}
|
|
35153
|
+
}
|
|
34591
35154
|
function runMigrations(db3, from, to) {
|
|
34592
35155
|
console.log(`Migrating database from v${from} to v${to}...`);
|
|
34593
35156
|
const migrations = {
|
|
@@ -34751,17 +35314,64 @@ function runMigrations(db3, from, to) {
|
|
|
34751
35314
|
},
|
|
34752
35315
|
// Version 5: Add session metadata columns for Claude import
|
|
34753
35316
|
5: (db4) => {
|
|
34754
|
-
|
|
34755
|
-
|
|
34756
|
-
|
|
34757
|
-
|
|
34758
|
-
ALTER TABLE sessions ADD COLUMN
|
|
34759
|
-
|
|
34760
|
-
|
|
34761
|
-
|
|
34762
|
-
|
|
34763
|
-
|
|
34764
|
-
|
|
35317
|
+
ensureColumn(
|
|
35318
|
+
db4,
|
|
35319
|
+
"sessions",
|
|
35320
|
+
"dir_scope",
|
|
35321
|
+
"ALTER TABLE sessions ADD COLUMN dir_scope TEXT DEFAULT 'project' CHECK (dir_scope IN ('project', 'home'))"
|
|
35322
|
+
);
|
|
35323
|
+
ensureColumn(
|
|
35324
|
+
db4,
|
|
35325
|
+
"sessions",
|
|
35326
|
+
"inherit_project_prompt",
|
|
35327
|
+
"ALTER TABLE sessions ADD COLUMN inherit_project_prompt INTEGER DEFAULT 1"
|
|
35328
|
+
);
|
|
35329
|
+
ensureColumn(
|
|
35330
|
+
db4,
|
|
35331
|
+
"sessions",
|
|
35332
|
+
"is_warmup",
|
|
35333
|
+
"ALTER TABLE sessions ADD COLUMN is_warmup INTEGER DEFAULT 0"
|
|
35334
|
+
);
|
|
35335
|
+
ensureColumn(
|
|
35336
|
+
db4,
|
|
35337
|
+
"sessions",
|
|
35338
|
+
"parent_session_id",
|
|
35339
|
+
"ALTER TABLE sessions ADD COLUMN parent_session_id TEXT"
|
|
35340
|
+
);
|
|
35341
|
+
ensureColumn(
|
|
35342
|
+
db4,
|
|
35343
|
+
"sessions",
|
|
35344
|
+
"agent_id",
|
|
35345
|
+
"ALTER TABLE sessions ADD COLUMN agent_id TEXT"
|
|
35346
|
+
);
|
|
35347
|
+
ensureColumn(
|
|
35348
|
+
db4,
|
|
35349
|
+
"sessions",
|
|
35350
|
+
"is_sidechain",
|
|
35351
|
+
"ALTER TABLE sessions ADD COLUMN is_sidechain INTEGER DEFAULT 0"
|
|
35352
|
+
);
|
|
35353
|
+
ensureColumn(
|
|
35354
|
+
db4,
|
|
35355
|
+
"sessions",
|
|
35356
|
+
"session_type",
|
|
35357
|
+
"ALTER TABLE sessions ADD COLUMN session_type TEXT DEFAULT 'main'"
|
|
35358
|
+
);
|
|
35359
|
+
ensureColumn(
|
|
35360
|
+
db4,
|
|
35361
|
+
"sessions",
|
|
35362
|
+
"version",
|
|
35363
|
+
"ALTER TABLE sessions ADD COLUMN version TEXT"
|
|
35364
|
+
);
|
|
35365
|
+
ensureColumn(
|
|
35366
|
+
db4,
|
|
35367
|
+
"sessions",
|
|
35368
|
+
"user_type",
|
|
35369
|
+
"ALTER TABLE sessions ADD COLUMN user_type TEXT DEFAULT 'external'"
|
|
35370
|
+
);
|
|
35371
|
+
},
|
|
35372
|
+
// Version 6: Bring schema to Studio parity
|
|
35373
|
+
6: (db4) => {
|
|
35374
|
+
applySchemaUpdates(db4);
|
|
34765
35375
|
}
|
|
34766
35376
|
};
|
|
34767
35377
|
for (let v2 = from + 1; v2 <= to; v2++) {
|
|
@@ -34780,6 +35390,72 @@ function runMigrations(db3, from, to) {
|
|
|
34780
35390
|
}
|
|
34781
35391
|
console.log("Migrations complete.");
|
|
34782
35392
|
}
|
|
35393
|
+
function tableExists(db3, table) {
|
|
35394
|
+
const result = db3.prepare(`
|
|
35395
|
+
SELECT name FROM sqlite_master WHERE type='table' AND name=?
|
|
35396
|
+
`).get(table);
|
|
35397
|
+
return !!result;
|
|
35398
|
+
}
|
|
35399
|
+
function columnExists(db3, table, column) {
|
|
35400
|
+
try {
|
|
35401
|
+
const columns = db3.pragma(`table_info(${table})`);
|
|
35402
|
+
return columns.some((col) => col.name === column);
|
|
35403
|
+
} catch {
|
|
35404
|
+
return false;
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35407
|
+
function indexExists(db3, indexName) {
|
|
35408
|
+
const result = db3.prepare(`
|
|
35409
|
+
SELECT name FROM sqlite_master WHERE type='index' AND name=?
|
|
35410
|
+
`).get(indexName);
|
|
35411
|
+
return !!result;
|
|
35412
|
+
}
|
|
35413
|
+
function ensureColumn(db3, table, column, statement) {
|
|
35414
|
+
if (!columnExists(db3, table, column)) {
|
|
35415
|
+
db3.exec(statement);
|
|
35416
|
+
}
|
|
35417
|
+
}
|
|
35418
|
+
function ensureIndex(db3, indexName, statement) {
|
|
35419
|
+
if (!indexExists(db3, indexName)) {
|
|
35420
|
+
db3.exec(statement);
|
|
35421
|
+
}
|
|
35422
|
+
}
|
|
35423
|
+
function ensureTable(db3, table, statement) {
|
|
35424
|
+
if (!tableExists(db3, table)) {
|
|
35425
|
+
db3.exec(statement);
|
|
35426
|
+
}
|
|
35427
|
+
}
|
|
35428
|
+
function dedupeProviderSessions(db3) {
|
|
35429
|
+
const duplicates = db3.prepare(`
|
|
35430
|
+
SELECT provider, provider_session_id, COUNT(*) as cnt
|
|
35431
|
+
FROM sessions
|
|
35432
|
+
WHERE provider_session_id IS NOT NULL
|
|
35433
|
+
GROUP BY provider, provider_session_id
|
|
35434
|
+
HAVING COUNT(*) > 1
|
|
35435
|
+
`).all();
|
|
35436
|
+
if (!duplicates.length) {
|
|
35437
|
+
return;
|
|
35438
|
+
}
|
|
35439
|
+
for (const dup of duplicates) {
|
|
35440
|
+
const sessions = db3.prepare(`
|
|
35441
|
+
SELECT id, turn_count, created_at
|
|
35442
|
+
FROM sessions
|
|
35443
|
+
WHERE provider = ? AND provider_session_id = ?
|
|
35444
|
+
ORDER BY turn_count DESC, created_at ASC
|
|
35445
|
+
`).all(dup.provider, dup.provider_session_id);
|
|
35446
|
+
const keepId = sessions[0].id;
|
|
35447
|
+
const deleteIds = sessions.slice(1).map((s2) => s2.id);
|
|
35448
|
+
for (const id of deleteIds) {
|
|
35449
|
+
db3.prepare(`
|
|
35450
|
+
UPDATE sessions
|
|
35451
|
+
SET status = 'deleted',
|
|
35452
|
+
deleted_at = datetime('now'),
|
|
35453
|
+
provider_session_id = provider_session_id || '-dup-' || id
|
|
35454
|
+
WHERE id = ?
|
|
35455
|
+
`).run(id);
|
|
35456
|
+
}
|
|
35457
|
+
}
|
|
35458
|
+
}
|
|
34783
35459
|
function seedModelPricing(db3) {
|
|
34784
35460
|
const insert = db3.prepare(`
|
|
34785
35461
|
INSERT OR REPLACE INTO model_pricing
|
|
@@ -35657,8 +36333,8 @@ async function ensureEmbeddingProvider(preferredProvider = "auto", options = {})
|
|
|
35657
36333
|
});
|
|
35658
36334
|
console.log("\r \u2713 Ollama installed ");
|
|
35659
36335
|
console.log(" Starting ollama serve...");
|
|
35660
|
-
const { spawn:
|
|
35661
|
-
const server =
|
|
36336
|
+
const { spawn: spawn5 } = await import("child_process");
|
|
36337
|
+
const server = spawn5("ollama", ["serve"], {
|
|
35662
36338
|
detached: true,
|
|
35663
36339
|
stdio: "ignore",
|
|
35664
36340
|
env: { ...process.env, HOME: process.env.HOME }
|
|
@@ -36406,8 +37082,7 @@ Analyzing ${sessions.length} sessions...
|
|
|
36406
37082
|
const projectSuggestions = [];
|
|
36407
37083
|
const moveSuggestions = [];
|
|
36408
37084
|
const knownProjects = {
|
|
36409
|
-
"studio": "
|
|
36410
|
-
"prompt-stack": "Prompt Stack Studio",
|
|
37085
|
+
"studio": "RUDI Studio",
|
|
36411
37086
|
"RUDI": "RUDI",
|
|
36412
37087
|
"rudi": "RUDI",
|
|
36413
37088
|
"cli": "RUDI",
|
|
@@ -36495,8 +37170,8 @@ Proposed actions:`);
|
|
|
36495
37170
|
console.log(` ... and ${titleSuggestions.length - 5} more`);
|
|
36496
37171
|
}
|
|
36497
37172
|
}
|
|
36498
|
-
const { writeFileSync:
|
|
36499
|
-
|
|
37173
|
+
const { writeFileSync: writeFileSync6 } = await import("fs");
|
|
37174
|
+
writeFileSync6(outputFile, JSON.stringify(plan, null, 2));
|
|
36500
37175
|
console.log(`
|
|
36501
37176
|
\u2713 Plan saved to: ${outputFile}`);
|
|
36502
37177
|
console.log("\nTo apply this plan:");
|
|
@@ -38446,281 +39121,6 @@ Wiring up RUDI router...`);
|
|
|
38446
39121
|
console.log(" rudi index # Rebuild tool cache");
|
|
38447
39122
|
}
|
|
38448
39123
|
|
|
38449
|
-
// src/commands/migrate.js
|
|
38450
|
-
var fs30 = __toESM(require("fs"), 1);
|
|
38451
|
-
var path30 = __toESM(require("path"), 1);
|
|
38452
|
-
var import_os7 = __toESM(require("os"), 1);
|
|
38453
|
-
init_src();
|
|
38454
|
-
var HOME3 = import_os7.default.homedir();
|
|
38455
|
-
var OLD_PROMPT_STACK = path30.join(HOME3, ".prompt-stack");
|
|
38456
|
-
var SHIM_PATH = path30.join(PATHS.home, "shims", "rudi-mcp");
|
|
38457
|
-
function getOldStacks() {
|
|
38458
|
-
const stacksDir = path30.join(OLD_PROMPT_STACK, "stacks");
|
|
38459
|
-
if (!fs30.existsSync(stacksDir)) return [];
|
|
38460
|
-
return fs30.readdirSync(stacksDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && !d2.name.startsWith(".")).filter((d2) => {
|
|
38461
|
-
const hasManifest = fs30.existsSync(path30.join(stacksDir, d2.name, "manifest.json"));
|
|
38462
|
-
const hasPackage = fs30.existsSync(path30.join(stacksDir, d2.name, "package.json"));
|
|
38463
|
-
return hasManifest || hasPackage;
|
|
38464
|
-
}).map((d2) => d2.name);
|
|
38465
|
-
}
|
|
38466
|
-
function copyStack(stackName) {
|
|
38467
|
-
const oldPath = path30.join(OLD_PROMPT_STACK, "stacks", stackName);
|
|
38468
|
-
const newPath = path30.join(PATHS.stacks, stackName);
|
|
38469
|
-
if (!fs30.existsSync(oldPath)) {
|
|
38470
|
-
return { success: false, error: "Source not found" };
|
|
38471
|
-
}
|
|
38472
|
-
if (fs30.existsSync(newPath)) {
|
|
38473
|
-
return { success: true, skipped: true, reason: "Already exists" };
|
|
38474
|
-
}
|
|
38475
|
-
if (!fs30.existsSync(PATHS.stacks)) {
|
|
38476
|
-
fs30.mkdirSync(PATHS.stacks, { recursive: true });
|
|
38477
|
-
}
|
|
38478
|
-
copyRecursive(oldPath, newPath);
|
|
38479
|
-
return { success: true, copied: true };
|
|
38480
|
-
}
|
|
38481
|
-
function copyRecursive(src, dest) {
|
|
38482
|
-
const stat = fs30.statSync(src);
|
|
38483
|
-
if (stat.isDirectory()) {
|
|
38484
|
-
fs30.mkdirSync(dest, { recursive: true });
|
|
38485
|
-
for (const child of fs30.readdirSync(src)) {
|
|
38486
|
-
copyRecursive(path30.join(src, child), path30.join(dest, child));
|
|
38487
|
-
}
|
|
38488
|
-
} else {
|
|
38489
|
-
fs30.copyFileSync(src, dest);
|
|
38490
|
-
}
|
|
38491
|
-
}
|
|
38492
|
-
function ensureShim() {
|
|
38493
|
-
const shimsDir = path30.dirname(SHIM_PATH);
|
|
38494
|
-
if (!fs30.existsSync(shimsDir)) {
|
|
38495
|
-
fs30.mkdirSync(shimsDir, { recursive: true });
|
|
38496
|
-
}
|
|
38497
|
-
const shimContent = `#!/usr/bin/env bash
|
|
38498
|
-
set -euo pipefail
|
|
38499
|
-
if command -v rudi &> /dev/null; then
|
|
38500
|
-
exec rudi mcp "$1"
|
|
38501
|
-
else
|
|
38502
|
-
exec npx --yes @learnrudi/cli mcp "$1"
|
|
38503
|
-
fi
|
|
38504
|
-
`;
|
|
38505
|
-
fs30.writeFileSync(SHIM_PATH, shimContent, { mode: 493 });
|
|
38506
|
-
}
|
|
38507
|
-
function buildNewEntry(stackName, agentId) {
|
|
38508
|
-
const base = {
|
|
38509
|
-
command: SHIM_PATH,
|
|
38510
|
-
args: [stackName]
|
|
38511
|
-
};
|
|
38512
|
-
if (agentId === "claude-desktop" || agentId === "claude-code") {
|
|
38513
|
-
return { type: "stdio", ...base };
|
|
38514
|
-
}
|
|
38515
|
-
return base;
|
|
38516
|
-
}
|
|
38517
|
-
function isOldEntry(entry) {
|
|
38518
|
-
if (!entry) return false;
|
|
38519
|
-
const command = entry.command || "";
|
|
38520
|
-
const args = entry.args || [];
|
|
38521
|
-
const cwd = entry.cwd || "";
|
|
38522
|
-
return command.includes(".prompt-stack") || args.some((a2) => typeof a2 === "string" && a2.includes(".prompt-stack")) || cwd.includes(".prompt-stack");
|
|
38523
|
-
}
|
|
38524
|
-
function migrateAgentConfig(agentConfig, installedStacks, flags) {
|
|
38525
|
-
const configPath = findAgentConfig(agentConfig);
|
|
38526
|
-
if (!configPath) return { skipped: true, reason: "Config not found" };
|
|
38527
|
-
let config;
|
|
38528
|
-
try {
|
|
38529
|
-
config = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
38530
|
-
} catch {
|
|
38531
|
-
return { skipped: true, reason: "Could not parse config" };
|
|
38532
|
-
}
|
|
38533
|
-
const key = agentConfig.key;
|
|
38534
|
-
const mcpServers = config[key] || {};
|
|
38535
|
-
let updated = 0;
|
|
38536
|
-
let removed = 0;
|
|
38537
|
-
const changes = [];
|
|
38538
|
-
for (const [name, entry] of Object.entries(mcpServers)) {
|
|
38539
|
-
if (isOldEntry(entry)) {
|
|
38540
|
-
if (installedStacks.includes(name)) {
|
|
38541
|
-
const newEntry = buildNewEntry(name, agentConfig.id);
|
|
38542
|
-
mcpServers[name] = newEntry;
|
|
38543
|
-
updated++;
|
|
38544
|
-
changes.push({ name, action: "updated" });
|
|
38545
|
-
} else if (flags.removeOrphans) {
|
|
38546
|
-
delete mcpServers[name];
|
|
38547
|
-
removed++;
|
|
38548
|
-
changes.push({ name, action: "removed (not installed)" });
|
|
38549
|
-
} else {
|
|
38550
|
-
changes.push({ name, action: "skipped (not installed in .rudi)" });
|
|
38551
|
-
}
|
|
38552
|
-
}
|
|
38553
|
-
}
|
|
38554
|
-
if (updated > 0 || removed > 0) {
|
|
38555
|
-
const backupPath = configPath + ".backup." + Date.now();
|
|
38556
|
-
fs30.copyFileSync(configPath, backupPath);
|
|
38557
|
-
config[key] = mcpServers;
|
|
38558
|
-
fs30.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
38559
|
-
}
|
|
38560
|
-
return { updated, removed, changes };
|
|
38561
|
-
}
|
|
38562
|
-
async function cmdMigrate(args, flags) {
|
|
38563
|
-
const subcommand = args[0];
|
|
38564
|
-
if (!subcommand || subcommand === "help") {
|
|
38565
|
-
console.log(`
|
|
38566
|
-
rudi migrate - Migrate from .prompt-stack to .rudi
|
|
38567
|
-
|
|
38568
|
-
USAGE
|
|
38569
|
-
rudi migrate status Show what needs to be migrated
|
|
38570
|
-
rudi migrate stacks Copy stacks from .prompt-stack to .rudi
|
|
38571
|
-
rudi migrate configs Update agent configs to use new shim
|
|
38572
|
-
rudi migrate all Do everything
|
|
38573
|
-
|
|
38574
|
-
OPTIONS
|
|
38575
|
-
--remove-orphans Remove entries for stacks not installed in .rudi
|
|
38576
|
-
--dry-run Show what would be done without making changes
|
|
38577
|
-
`);
|
|
38578
|
-
return;
|
|
38579
|
-
}
|
|
38580
|
-
if (subcommand === "status") {
|
|
38581
|
-
await migrateStatus();
|
|
38582
|
-
return;
|
|
38583
|
-
}
|
|
38584
|
-
if (subcommand === "stacks") {
|
|
38585
|
-
await migrateStacks(flags);
|
|
38586
|
-
return;
|
|
38587
|
-
}
|
|
38588
|
-
if (subcommand === "configs") {
|
|
38589
|
-
await migrateConfigs(flags);
|
|
38590
|
-
return;
|
|
38591
|
-
}
|
|
38592
|
-
if (subcommand === "all") {
|
|
38593
|
-
await migrateStacks(flags);
|
|
38594
|
-
console.log("");
|
|
38595
|
-
await migrateConfigs(flags);
|
|
38596
|
-
return;
|
|
38597
|
-
}
|
|
38598
|
-
console.error(`Unknown subcommand: ${subcommand}`);
|
|
38599
|
-
console.error("Run: rudi migrate help");
|
|
38600
|
-
}
|
|
38601
|
-
async function migrateStatus() {
|
|
38602
|
-
console.log("\n=== Migration Status ===\n");
|
|
38603
|
-
const oldStacks = getOldStacks();
|
|
38604
|
-
console.log(`Old .prompt-stack stacks: ${oldStacks.length}`);
|
|
38605
|
-
if (oldStacks.length > 0) {
|
|
38606
|
-
for (const name of oldStacks) {
|
|
38607
|
-
const existsInRudi = fs30.existsSync(path30.join(PATHS.stacks, name));
|
|
38608
|
-
const status = existsInRudi ? "\u2713 (already in .rudi)" : "\u25CB (needs migration)";
|
|
38609
|
-
console.log(` ${status} ${name}`);
|
|
38610
|
-
}
|
|
38611
|
-
}
|
|
38612
|
-
const newStacksDir = PATHS.stacks;
|
|
38613
|
-
let newStacks = [];
|
|
38614
|
-
if (fs30.existsSync(newStacksDir)) {
|
|
38615
|
-
newStacks = fs30.readdirSync(newStacksDir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && !d2.name.startsWith(".")).map((d2) => d2.name);
|
|
38616
|
-
}
|
|
38617
|
-
console.log(`
|
|
38618
|
-
New .rudi stacks: ${newStacks.length}`);
|
|
38619
|
-
if (newStacks.length > 0) {
|
|
38620
|
-
for (const name of newStacks) {
|
|
38621
|
-
console.log(` \u2713 ${name}`);
|
|
38622
|
-
}
|
|
38623
|
-
}
|
|
38624
|
-
console.log("\n=== Agent Configs ===\n");
|
|
38625
|
-
for (const agentConfig of AGENT_CONFIGS) {
|
|
38626
|
-
const configPath = findAgentConfig(agentConfig);
|
|
38627
|
-
if (!configPath) continue;
|
|
38628
|
-
let config;
|
|
38629
|
-
try {
|
|
38630
|
-
config = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
38631
|
-
} catch {
|
|
38632
|
-
continue;
|
|
38633
|
-
}
|
|
38634
|
-
const mcpServers = config[agentConfig.key] || {};
|
|
38635
|
-
const entries = Object.entries(mcpServers);
|
|
38636
|
-
const oldEntries = entries.filter(([_2, e2]) => isOldEntry(e2));
|
|
38637
|
-
const newEntries = entries.filter(([_2, e2]) => !isOldEntry(e2));
|
|
38638
|
-
if (entries.length === 0) continue;
|
|
38639
|
-
console.log(`${agentConfig.name}:`);
|
|
38640
|
-
console.log(` Config: ${configPath}`);
|
|
38641
|
-
console.log(` Old entries: ${oldEntries.length}`);
|
|
38642
|
-
console.log(` New entries: ${newEntries.length}`);
|
|
38643
|
-
if (oldEntries.length > 0) {
|
|
38644
|
-
console.log(" Needs update:");
|
|
38645
|
-
for (const [name] of oldEntries) {
|
|
38646
|
-
const installed = newStacks.includes(name);
|
|
38647
|
-
const status = installed ? "(ready)" : "(not in .rudi)";
|
|
38648
|
-
console.log(` - ${name} ${status}`);
|
|
38649
|
-
}
|
|
38650
|
-
}
|
|
38651
|
-
console.log("");
|
|
38652
|
-
}
|
|
38653
|
-
console.log("Run: rudi migrate all");
|
|
38654
|
-
}
|
|
38655
|
-
async function migrateStacks(flags) {
|
|
38656
|
-
console.log("=== Migrating Stacks ===\n");
|
|
38657
|
-
const oldStacks = getOldStacks();
|
|
38658
|
-
if (oldStacks.length === 0) {
|
|
38659
|
-
console.log("No stacks found in .prompt-stack");
|
|
38660
|
-
return;
|
|
38661
|
-
}
|
|
38662
|
-
console.log(`Found ${oldStacks.length} stack(s) in .prompt-stack
|
|
38663
|
-
`);
|
|
38664
|
-
for (const name of oldStacks) {
|
|
38665
|
-
if (flags.dryRun) {
|
|
38666
|
-
const exists = fs30.existsSync(path30.join(PATHS.stacks, name));
|
|
38667
|
-
console.log(` [dry-run] ${name}: ${exists ? "would skip (exists)" : "would copy"}`);
|
|
38668
|
-
} else {
|
|
38669
|
-
const result = copyStack(name);
|
|
38670
|
-
if (result.skipped) {
|
|
38671
|
-
console.log(` \u25CB ${name}: skipped (${result.reason})`);
|
|
38672
|
-
} else if (result.copied) {
|
|
38673
|
-
console.log(` \u2713 ${name}: copied to .rudi/stacks/`);
|
|
38674
|
-
} else {
|
|
38675
|
-
console.log(` \u2717 ${name}: ${result.error}`);
|
|
38676
|
-
}
|
|
38677
|
-
}
|
|
38678
|
-
}
|
|
38679
|
-
if (!flags.dryRun) {
|
|
38680
|
-
console.log(`
|
|
38681
|
-
Stacks migrated to: ${PATHS.stacks}`);
|
|
38682
|
-
}
|
|
38683
|
-
}
|
|
38684
|
-
async function migrateConfigs(flags) {
|
|
38685
|
-
console.log("=== Updating Agent Configs ===\n");
|
|
38686
|
-
if (!flags.dryRun) {
|
|
38687
|
-
ensureShim();
|
|
38688
|
-
console.log(`Shim ready: ${SHIM_PATH}
|
|
38689
|
-
`);
|
|
38690
|
-
}
|
|
38691
|
-
let installedStacks = [];
|
|
38692
|
-
if (fs30.existsSync(PATHS.stacks)) {
|
|
38693
|
-
installedStacks = fs30.readdirSync(PATHS.stacks, { withFileTypes: true }).filter((d2) => d2.isDirectory() && !d2.name.startsWith(".")).map((d2) => d2.name);
|
|
38694
|
-
}
|
|
38695
|
-
for (const agentConfig of AGENT_CONFIGS) {
|
|
38696
|
-
const configPath = findAgentConfig(agentConfig);
|
|
38697
|
-
if (!configPath) continue;
|
|
38698
|
-
if (flags.dryRun) {
|
|
38699
|
-
console.log(`${agentConfig.name}:`);
|
|
38700
|
-
console.log(` [dry-run] Would update entries using .prompt-stack paths`);
|
|
38701
|
-
continue;
|
|
38702
|
-
}
|
|
38703
|
-
const result = migrateAgentConfig(agentConfig, installedStacks, flags);
|
|
38704
|
-
if (result.skipped) {
|
|
38705
|
-
continue;
|
|
38706
|
-
}
|
|
38707
|
-
console.log(`${agentConfig.name}:`);
|
|
38708
|
-
console.log(` Config: ${configPath}`);
|
|
38709
|
-
if (result.changes && result.changes.length > 0) {
|
|
38710
|
-
for (const change of result.changes) {
|
|
38711
|
-
console.log(` ${change.action}: ${change.name}`);
|
|
38712
|
-
}
|
|
38713
|
-
}
|
|
38714
|
-
if (result.updated > 0 || result.removed > 0) {
|
|
38715
|
-
console.log(` Updated: ${result.updated}, Removed: ${result.removed}`);
|
|
38716
|
-
} else {
|
|
38717
|
-
console.log(` No changes needed`);
|
|
38718
|
-
}
|
|
38719
|
-
console.log("");
|
|
38720
|
-
}
|
|
38721
|
-
console.log("Restart your agents to use the updated configs.");
|
|
38722
|
-
}
|
|
38723
|
-
|
|
38724
39124
|
// src/commands/index-tools.js
|
|
38725
39125
|
init_src4();
|
|
38726
39126
|
init_src4();
|
|
@@ -38860,7 +39260,7 @@ init_src4();
|
|
|
38860
39260
|
var import_child_process11 = require("child_process");
|
|
38861
39261
|
var import_fs24 = __toESM(require("fs"), 1);
|
|
38862
39262
|
var import_path22 = __toESM(require("path"), 1);
|
|
38863
|
-
var
|
|
39263
|
+
var import_os7 = __toESM(require("os"), 1);
|
|
38864
39264
|
var AGENTS = [
|
|
38865
39265
|
{
|
|
38866
39266
|
id: "claude",
|
|
@@ -38905,7 +39305,7 @@ var BINARIES = [
|
|
|
38905
39305
|
{ id: "jq", name: "jq", command: "jq", versionFlag: "--version" }
|
|
38906
39306
|
];
|
|
38907
39307
|
function fileExists(filePath) {
|
|
38908
|
-
const resolved = filePath.replace("~",
|
|
39308
|
+
const resolved = filePath.replace("~", import_os7.default.homedir());
|
|
38909
39309
|
return import_fs24.default.existsSync(resolved);
|
|
38910
39310
|
}
|
|
38911
39311
|
function checkKeychain(service) {
|
|
@@ -39156,7 +39556,7 @@ init_src4();
|
|
|
39156
39556
|
var import_child_process12 = require("child_process");
|
|
39157
39557
|
var import_fs25 = __toESM(require("fs"), 1);
|
|
39158
39558
|
var import_path23 = __toESM(require("path"), 1);
|
|
39159
|
-
var
|
|
39559
|
+
var import_os8 = __toESM(require("os"), 1);
|
|
39160
39560
|
var AGENT_CREDENTIALS = {
|
|
39161
39561
|
claude: { type: "keychain", service: "Claude Code-credentials" },
|
|
39162
39562
|
codex: { type: "file", path: "~/.codex/auth.json" },
|
|
@@ -39164,7 +39564,7 @@ var AGENT_CREDENTIALS = {
|
|
|
39164
39564
|
copilot: { type: "file", path: "~/.config/github-copilot/hosts.json" }
|
|
39165
39565
|
};
|
|
39166
39566
|
function fileExists2(filePath) {
|
|
39167
|
-
const resolved = filePath.replace("~",
|
|
39567
|
+
const resolved = filePath.replace("~", import_os8.default.homedir());
|
|
39168
39568
|
return import_fs25.default.existsSync(resolved);
|
|
39169
39569
|
}
|
|
39170
39570
|
function checkKeychain2(service) {
|
|
@@ -39673,7 +40073,7 @@ Lockfile: ${lockPath}`);
|
|
|
39673
40073
|
// src/commands/apply.js
|
|
39674
40074
|
var import_fs28 = require("fs");
|
|
39675
40075
|
var import_path26 = require("path");
|
|
39676
|
-
var
|
|
40076
|
+
var import_os9 = require("os");
|
|
39677
40077
|
var import_crypto3 = require("crypto");
|
|
39678
40078
|
async function cmdApply(args, flags) {
|
|
39679
40079
|
const planFile = args[0];
|
|
@@ -39856,10 +40256,10 @@ Applying plan ${planId}...
|
|
|
39856
40256
|
}
|
|
39857
40257
|
console.log(` \u2713 Updated ${updated} titles`);
|
|
39858
40258
|
}
|
|
39859
|
-
const undoDir = (0, import_path26.join)((0,
|
|
39860
|
-
const { mkdirSync:
|
|
40259
|
+
const undoDir = (0, import_path26.join)((0, import_os9.homedir)(), ".rudi", "plans");
|
|
40260
|
+
const { mkdirSync: mkdirSync4 } = await import("fs");
|
|
39861
40261
|
try {
|
|
39862
|
-
|
|
40262
|
+
mkdirSync4(undoDir, { recursive: true });
|
|
39863
40263
|
} catch (e2) {
|
|
39864
40264
|
}
|
|
39865
40265
|
const undoFile = (0, import_path26.join)(undoDir, `${planId}.undo.json`);
|
|
@@ -40035,6 +40435,215 @@ Project deleted: ${project.name}`);
|
|
|
40035
40435
|
}
|
|
40036
40436
|
}
|
|
40037
40437
|
|
|
40438
|
+
// src/commands/studio.js
|
|
40439
|
+
var import_fs29 = __toESM(require("fs"), 1);
|
|
40440
|
+
var import_path27 = __toESM(require("path"), 1);
|
|
40441
|
+
var import_os10 = __toESM(require("os"), 1);
|
|
40442
|
+
var import_child_process13 = require("child_process");
|
|
40443
|
+
var STUDIO_WEBSITE = "https://learnrudi.com";
|
|
40444
|
+
var STUDIO_PATHS = {
|
|
40445
|
+
darwin: [
|
|
40446
|
+
"/Applications/RUDI Studio.app",
|
|
40447
|
+
import_path27.default.join(import_os10.default.homedir(), "Applications/RUDI Studio.app")
|
|
40448
|
+
],
|
|
40449
|
+
win32: [
|
|
40450
|
+
import_path27.default.join(import_os10.default.homedir(), "AppData/Local/Programs/RUDI Studio"),
|
|
40451
|
+
"C:/Program Files/RUDI Studio"
|
|
40452
|
+
],
|
|
40453
|
+
linux: [
|
|
40454
|
+
"/opt/RUDI Studio",
|
|
40455
|
+
import_path27.default.join(import_os10.default.homedir(), ".local/share/applications/rudi-studio")
|
|
40456
|
+
]
|
|
40457
|
+
};
|
|
40458
|
+
var APP_DATA_PATHS = {
|
|
40459
|
+
darwin: [
|
|
40460
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Application Support/RUDI Studio"),
|
|
40461
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Application Support/rudi-studio"),
|
|
40462
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Caches/RUDI Studio"),
|
|
40463
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Caches/rudi-studio"),
|
|
40464
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Preferences/com.rudi.studio.plist"),
|
|
40465
|
+
import_path27.default.join(import_os10.default.homedir(), "Library/Saved Application State/com.rudi.studio.savedState")
|
|
40466
|
+
],
|
|
40467
|
+
win32: [
|
|
40468
|
+
import_path27.default.join(import_os10.default.homedir(), "AppData/Roaming/RUDI Studio"),
|
|
40469
|
+
import_path27.default.join(import_os10.default.homedir(), "AppData/Local/RUDI Studio")
|
|
40470
|
+
],
|
|
40471
|
+
linux: [
|
|
40472
|
+
import_path27.default.join(import_os10.default.homedir(), ".config/RUDI Studio"),
|
|
40473
|
+
import_path27.default.join(import_os10.default.homedir(), ".config/rudi-studio")
|
|
40474
|
+
]
|
|
40475
|
+
};
|
|
40476
|
+
function findStudioPath() {
|
|
40477
|
+
const platform = process.platform;
|
|
40478
|
+
const paths = STUDIO_PATHS[platform] || [];
|
|
40479
|
+
for (const p2 of paths) {
|
|
40480
|
+
if (import_fs29.default.existsSync(p2)) {
|
|
40481
|
+
return p2;
|
|
40482
|
+
}
|
|
40483
|
+
}
|
|
40484
|
+
return null;
|
|
40485
|
+
}
|
|
40486
|
+
function getStudioVersion(studioPath) {
|
|
40487
|
+
if (process.platform === "darwin") {
|
|
40488
|
+
const plistPath = import_path27.default.join(studioPath, "Contents/Info.plist");
|
|
40489
|
+
if (import_fs29.default.existsSync(plistPath)) {
|
|
40490
|
+
const content = import_fs29.default.readFileSync(plistPath, "utf-8");
|
|
40491
|
+
const match = content.match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);
|
|
40492
|
+
if (match) {
|
|
40493
|
+
return match[1];
|
|
40494
|
+
}
|
|
40495
|
+
}
|
|
40496
|
+
} else {
|
|
40497
|
+
const pkgPath = import_path27.default.join(studioPath, "resources/app/package.json");
|
|
40498
|
+
if (import_fs29.default.existsSync(pkgPath)) {
|
|
40499
|
+
try {
|
|
40500
|
+
const pkg = JSON.parse(import_fs29.default.readFileSync(pkgPath, "utf-8"));
|
|
40501
|
+
return pkg.version;
|
|
40502
|
+
} catch {
|
|
40503
|
+
}
|
|
40504
|
+
}
|
|
40505
|
+
}
|
|
40506
|
+
return null;
|
|
40507
|
+
}
|
|
40508
|
+
function openUrl(url) {
|
|
40509
|
+
const platform = process.platform;
|
|
40510
|
+
let cmd, args;
|
|
40511
|
+
if (platform === "darwin") {
|
|
40512
|
+
cmd = "open";
|
|
40513
|
+
args = [url];
|
|
40514
|
+
} else if (platform === "win32") {
|
|
40515
|
+
cmd = "cmd";
|
|
40516
|
+
args = ["/c", "start", "", url];
|
|
40517
|
+
} else {
|
|
40518
|
+
cmd = "xdg-open";
|
|
40519
|
+
args = [url];
|
|
40520
|
+
}
|
|
40521
|
+
(0, import_child_process13.spawn)(cmd, args, { detached: true, stdio: "ignore" }).unref();
|
|
40522
|
+
}
|
|
40523
|
+
async function studioOpen() {
|
|
40524
|
+
console.log(`Opening ${STUDIO_WEBSITE}...`);
|
|
40525
|
+
openUrl(STUDIO_WEBSITE);
|
|
40526
|
+
}
|
|
40527
|
+
async function studioVersion(flags) {
|
|
40528
|
+
const studioPath = findStudioPath();
|
|
40529
|
+
if (!studioPath) {
|
|
40530
|
+
console.log("RUDI Studio is not installed");
|
|
40531
|
+
console.log(`
|
|
40532
|
+
Get it at: ${STUDIO_WEBSITE}`);
|
|
40533
|
+
process.exit(1);
|
|
40534
|
+
}
|
|
40535
|
+
const version = getStudioVersion(studioPath);
|
|
40536
|
+
if (version) {
|
|
40537
|
+
console.log(`RUDI Studio v${version}`);
|
|
40538
|
+
} else {
|
|
40539
|
+
console.log("RUDI Studio installed");
|
|
40540
|
+
console.log(` Location: ${studioPath}`);
|
|
40541
|
+
console.log(" Version: unknown");
|
|
40542
|
+
}
|
|
40543
|
+
if (flags.verbose) {
|
|
40544
|
+
console.log(`
|
|
40545
|
+
Path: ${studioPath}`);
|
|
40546
|
+
}
|
|
40547
|
+
}
|
|
40548
|
+
async function studioUninstall(flags) {
|
|
40549
|
+
const studioPath = findStudioPath();
|
|
40550
|
+
const platform = process.platform;
|
|
40551
|
+
const dataPaths = APP_DATA_PATHS[platform] || [];
|
|
40552
|
+
const existingDataPaths = dataPaths.filter((p2) => import_fs29.default.existsSync(p2));
|
|
40553
|
+
if (!studioPath && existingDataPaths.length === 0) {
|
|
40554
|
+
console.log("RUDI Studio is not installed");
|
|
40555
|
+
process.exit(0);
|
|
40556
|
+
}
|
|
40557
|
+
console.log("The following will be removed:");
|
|
40558
|
+
if (studioPath) {
|
|
40559
|
+
console.log(` App: ${studioPath}`);
|
|
40560
|
+
}
|
|
40561
|
+
for (const p2 of existingDataPaths) {
|
|
40562
|
+
console.log(` Data: ${p2}`);
|
|
40563
|
+
}
|
|
40564
|
+
console.log("");
|
|
40565
|
+
console.log("Note: ~/.rudi/ will NOT be removed (managed by RUDI CLI)");
|
|
40566
|
+
console.log("");
|
|
40567
|
+
if (!flags.force && !flags.y) {
|
|
40568
|
+
console.log("Run with --force or -y to confirm uninstall");
|
|
40569
|
+
process.exit(0);
|
|
40570
|
+
}
|
|
40571
|
+
let errors = [];
|
|
40572
|
+
if (studioPath) {
|
|
40573
|
+
try {
|
|
40574
|
+
import_fs29.default.rmSync(studioPath, { recursive: true, force: true });
|
|
40575
|
+
console.log(`Removed: ${studioPath}`);
|
|
40576
|
+
} catch (err) {
|
|
40577
|
+
errors.push(`Failed to remove ${studioPath}: ${err.message}`);
|
|
40578
|
+
}
|
|
40579
|
+
}
|
|
40580
|
+
for (const p2 of existingDataPaths) {
|
|
40581
|
+
try {
|
|
40582
|
+
import_fs29.default.rmSync(p2, { recursive: true, force: true });
|
|
40583
|
+
console.log(`Removed: ${p2}`);
|
|
40584
|
+
} catch (err) {
|
|
40585
|
+
errors.push(`Failed to remove ${p2}: ${err.message}`);
|
|
40586
|
+
}
|
|
40587
|
+
}
|
|
40588
|
+
if (errors.length > 0) {
|
|
40589
|
+
console.log("");
|
|
40590
|
+
console.log("Some items could not be removed:");
|
|
40591
|
+
for (const err of errors) {
|
|
40592
|
+
console.log(` ${err}`);
|
|
40593
|
+
}
|
|
40594
|
+
console.log("");
|
|
40595
|
+
console.log("You may need to remove them manually or use sudo.");
|
|
40596
|
+
process.exit(1);
|
|
40597
|
+
}
|
|
40598
|
+
console.log("");
|
|
40599
|
+
console.log("RUDI Studio uninstalled successfully");
|
|
40600
|
+
}
|
|
40601
|
+
function showHelp() {
|
|
40602
|
+
console.log(`rudi studio - Manage RUDI Studio
|
|
40603
|
+
|
|
40604
|
+
Usage:
|
|
40605
|
+
rudi studio Open RUDI website
|
|
40606
|
+
rudi studio version Show installed Studio version
|
|
40607
|
+
rudi studio uninstall Uninstall RUDI Studio
|
|
40608
|
+
|
|
40609
|
+
Options:
|
|
40610
|
+
--force, -y Skip confirmation for uninstall
|
|
40611
|
+
--verbose Show additional details
|
|
40612
|
+
|
|
40613
|
+
Examples:
|
|
40614
|
+
rudi studio # Open learnrudi.com in browser
|
|
40615
|
+
rudi studio version # Check installed version
|
|
40616
|
+
rudi studio uninstall -y # Remove Studio and app data
|
|
40617
|
+
`);
|
|
40618
|
+
}
|
|
40619
|
+
async function cmdStudio(args, flags) {
|
|
40620
|
+
const subcommand = args[0];
|
|
40621
|
+
switch (subcommand) {
|
|
40622
|
+
case "version":
|
|
40623
|
+
case "v":
|
|
40624
|
+
await studioVersion(flags);
|
|
40625
|
+
break;
|
|
40626
|
+
case "uninstall":
|
|
40627
|
+
case "remove":
|
|
40628
|
+
case "rm":
|
|
40629
|
+
await studioUninstall(flags);
|
|
40630
|
+
break;
|
|
40631
|
+
case "help":
|
|
40632
|
+
case "-h":
|
|
40633
|
+
case "--help":
|
|
40634
|
+
showHelp();
|
|
40635
|
+
break;
|
|
40636
|
+
case "open":
|
|
40637
|
+
case void 0:
|
|
40638
|
+
await studioOpen();
|
|
40639
|
+
break;
|
|
40640
|
+
default:
|
|
40641
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
40642
|
+
console.error(`Run 'rudi studio help' for usage`);
|
|
40643
|
+
process.exit(1);
|
|
40644
|
+
}
|
|
40645
|
+
}
|
|
40646
|
+
|
|
40038
40647
|
// src/index.js
|
|
40039
40648
|
var VERSION2 = "2.0.0";
|
|
40040
40649
|
async function main() {
|
|
@@ -40123,9 +40732,6 @@ async function main() {
|
|
|
40123
40732
|
case "integrate":
|
|
40124
40733
|
await cmdIntegrate(args, flags);
|
|
40125
40734
|
break;
|
|
40126
|
-
case "migrate":
|
|
40127
|
-
await cmdMigrate(args, flags);
|
|
40128
|
-
break;
|
|
40129
40735
|
case "index":
|
|
40130
40736
|
await cmdIndex(args, flags);
|
|
40131
40737
|
break;
|
|
@@ -40145,6 +40751,9 @@ async function main() {
|
|
|
40145
40751
|
case "package":
|
|
40146
40752
|
await cmdInfo(args, flags);
|
|
40147
40753
|
break;
|
|
40754
|
+
case "studio":
|
|
40755
|
+
await cmdStudio(args, flags);
|
|
40756
|
+
break;
|
|
40148
40757
|
// Shortcuts for listing specific package types
|
|
40149
40758
|
case "stacks":
|
|
40150
40759
|
await cmdList(["stacks"], flags);
|