@aperdomoll90/ledger-ai 1.0.2 → 1.1.0

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/cli.js CHANGED
@@ -4,16 +4,23 @@ import { loadConfig } from './lib/config.js';
4
4
  import { pull } from './commands/pull.js';
5
5
  import { push } from './commands/push.js';
6
6
  import { check } from './commands/check.js';
7
+ import { sync } from './commands/sync.js';
7
8
  import { show } from './commands/show.js';
8
9
  import { exportNote } from './commands/export.js';
9
10
  import { ingest } from './commands/ingest.js';
10
11
  import { init } from './commands/init.js';
12
+ import { wizard } from './commands/wizard.js';
11
13
  import { setupClaudeCode, setupOpenclaw, setupChatgpt } from './commands/setup.js';
12
14
  import { backup, enableBackupCron, disableBackupCron } from './commands/backup.js';
13
15
  import { restore } from './commands/restore.js';
14
16
  import { onboard } from './commands/onboard.js';
15
17
  import { configGet, configSet, configList } from './commands/config.js';
16
18
  import { migrate } from './commands/migrate.js';
19
+ import { add } from './commands/add.js';
20
+ import { update } from './commands/update.js';
21
+ import { deleteNote } from './commands/delete.js';
22
+ import { list } from './commands/list.js';
23
+ import { tag } from './commands/tag.js';
17
24
  process.on('unhandledRejection', (err) => {
18
25
  console.error(err instanceof Error ? err.message : String(err));
19
26
  process.exit(1);
@@ -41,11 +48,25 @@ program
41
48
  });
42
49
  program
43
50
  .command('check')
44
- .description('Compare local files vs Ledger, report sync status')
51
+ .description('Compare local files vs Ledger, report sync status (alias for sync --dry-run)')
45
52
  .action(async () => {
46
53
  const config = loadConfig();
47
54
  await check(config);
48
55
  });
56
+ program
57
+ .command('sync')
58
+ .description('Bidirectional sync of persona files between Ledger and local cache')
59
+ .option('-q, --quiet', 'suppress output unless conflicts (for hooks)')
60
+ .option('-f, --force', 'overwrite local with Ledger version')
61
+ .option('-n, --dry-run', 'show what would happen without doing it')
62
+ .action(async (options) => {
63
+ const config = loadConfig();
64
+ await sync(config, {
65
+ quiet: options.quiet ?? false,
66
+ force: options.force ?? false,
67
+ dryRun: options.dryRun ?? false,
68
+ });
69
+ });
49
70
  program
50
71
  .command('show <query...>')
51
72
  .description('Search Ledger by meaning, open matching note')
@@ -126,9 +147,15 @@ program
126
147
  });
127
148
  program
128
149
  .command('init')
129
- .description('Set up Ledger on this machine (credentials, database schema)')
130
- .action(async () => {
131
- await init();
150
+ .description('Guided setup wizard (credentials, database, persona, platforms, sync)')
151
+ .option('--legacy', 'run legacy init (credentials + database only)')
152
+ .action(async (options) => {
153
+ if (options.legacy) {
154
+ await init();
155
+ }
156
+ else {
157
+ await wizard();
158
+ }
132
159
  });
133
160
  const setupCmd = program
134
161
  .command('setup')
@@ -158,4 +185,66 @@ program
158
185
  const config = loadConfig();
159
186
  await migrate(config);
160
187
  });
188
+ program
189
+ .command('add')
190
+ .description('Add a new note to Ledger (with duplicate detection)')
191
+ .requiredOption('-c, --content <content>', 'note content (or use stdin)')
192
+ .requiredOption('-t, --type <type>', 'note type (feedback, reference, event, etc.)')
193
+ .option('-a, --agent <agent>', 'agent name', 'cli')
194
+ .option('-p, --project <project>', 'project name')
195
+ .option('-k, --upsert-key <key>', 'upsert key for dedup')
196
+ .option('-f, --force', 'skip duplicate check')
197
+ .action(async (options) => {
198
+ const config = loadConfig();
199
+ await add(config, options.content, {
200
+ type: options.type,
201
+ agent: options.agent,
202
+ project: options.project,
203
+ upsertKey: options.upsertKey,
204
+ force: options.force ?? false,
205
+ });
206
+ });
207
+ program
208
+ .command('update <id>')
209
+ .description('Update an existing note by ID (with confirmation)')
210
+ .requiredOption('-c, --content <content>', 'new content')
211
+ .action(async (id, options) => {
212
+ const config = loadConfig();
213
+ await update(config, parseInt(id, 10), options.content, {});
214
+ });
215
+ program
216
+ .command('delete <id>')
217
+ .description('Delete a note by ID (with confirmation)')
218
+ .action(async (id) => {
219
+ const config = loadConfig();
220
+ await deleteNote(config, parseInt(id, 10));
221
+ });
222
+ program
223
+ .command('list')
224
+ .description('List recent notes from Ledger')
225
+ .option('-n, --limit <number>', 'number of notes', '20')
226
+ .option('-t, --type <type>', 'filter by note type')
227
+ .option('-p, --project <project>', 'filter by project name')
228
+ .action(async (options) => {
229
+ const config = loadConfig();
230
+ await list(config, {
231
+ limit: parseInt(options.limit, 10),
232
+ type: options.type,
233
+ project: options.project,
234
+ });
235
+ });
236
+ program
237
+ .command('tag <id>')
238
+ .description('Update metadata on a note (description, project, scope)')
239
+ .option('-d, --description <text>', 'note description/purpose')
240
+ .option('-p, --project <name>', 'project name')
241
+ .option('-s, --scope <scope>', 'scope (user, system, general)')
242
+ .action(async (id, options) => {
243
+ const config = loadConfig();
244
+ await tag(config, parseInt(id, 10), {
245
+ description: options.description,
246
+ project: options.project,
247
+ scope: options.scope,
248
+ });
249
+ });
161
250
  program.parse();
@@ -0,0 +1,25 @@
1
+ import { opAddNote } from '../lib/notes.js';
2
+ import { confirm } from '../lib/prompt.js';
3
+ export async function add(config, content, options) {
4
+ const metadata = {};
5
+ if (options.project)
6
+ metadata.project = options.project;
7
+ if (options.upsertKey)
8
+ metadata.upsert_key = options.upsertKey;
9
+ const result = await opAddNote({ supabase: config.supabase, openai: config.openai }, content, options.type, options.agent || 'cli', metadata, options.force ?? false);
10
+ if (result.status === 'confirm') {
11
+ console.error(result.message);
12
+ const proceed = await confirm('\nCreate new note anyway?');
13
+ if (proceed) {
14
+ const forced = await opAddNote({ supabase: config.supabase, openai: config.openai }, content, options.type, options.agent || 'cli', metadata, true);
15
+ console.error(forced.message);
16
+ }
17
+ else {
18
+ console.error('Cancelled.');
19
+ }
20
+ return;
21
+ }
22
+ console.error(result.message);
23
+ if (result.status === 'error')
24
+ process.exit(1);
25
+ }
@@ -0,0 +1,22 @@
1
+ import { opDeleteNote } from '../lib/notes.js';
2
+ import { confirm } from '../lib/prompt.js';
3
+ export async function deleteNote(config, id) {
4
+ const clients = { supabase: config.supabase, openai: config.openai };
5
+ // First call: show confirmation
6
+ const preview = await opDeleteNote(clients, id, false);
7
+ if (preview.status === 'error') {
8
+ console.error(preview.message);
9
+ process.exit(1);
10
+ }
11
+ console.error(preview.message);
12
+ const proceed = await confirm('\nProceed with deletion?');
13
+ if (!proceed) {
14
+ console.error('Cancelled.');
15
+ return;
16
+ }
17
+ // Second call: execute
18
+ const result = await opDeleteNote(clients, id, true);
19
+ console.error(result.message);
20
+ if (result.status === 'error')
21
+ process.exit(1);
22
+ }
@@ -1,10 +1,10 @@
1
1
  import { readFileSync, unlinkSync, readdirSync, existsSync } from 'fs';
2
2
  import { resolve, basename } from 'path';
3
- import { fetchCachedNotes, searchNotes } from '../lib/notes.js';
3
+ import { fetchPersonaNotes, findNoteByFile, searchNotes, inferDelivery } from '../lib/notes.js';
4
4
  import { contentHash } from '../lib/hash.js';
5
5
  import { confirm, choose } from '../lib/prompt.js';
6
6
  export async function ingest(config, options) {
7
- const existingNotes = await fetchCachedNotes(config.supabase);
7
+ const existingNotes = await fetchPersonaNotes(config.supabase);
8
8
  if (options.file) {
9
9
  if (options.auto) {
10
10
  await autoIngestFile(config, resolve(options.file), existingNotes);
@@ -105,6 +105,12 @@ async function ingestFile(config, filePath, existingNotes) {
105
105
  'error',
106
106
  'general',
107
107
  ]);
108
+ const defaultDelivery = inferDelivery(noteType);
109
+ const deliveryChoice = await choose(`Delivery tier (default: ${defaultDelivery}):`, [
110
+ `${defaultDelivery} (default)`,
111
+ ...['persona', 'project', 'knowledge'].filter(d => d !== defaultDelivery),
112
+ ]);
113
+ const delivery = deliveryChoice.replace(' (default)', '');
108
114
  const { openai } = config;
109
115
  const embeddingResponse = await openai.embeddings.create({
110
116
  model: 'text-embedding-3-small',
@@ -122,7 +128,7 @@ async function ingestFile(config, filePath, existingNotes) {
122
128
  upsert_key: upsertKey,
123
129
  local_file: filename,
124
130
  content_hash: hash,
125
- local_cache: true,
131
+ delivery,
126
132
  },
127
133
  embedding,
128
134
  })
@@ -132,7 +138,7 @@ async function ingestFile(config, filePath, existingNotes) {
132
138
  console.error(`Error adding note: ${error.message}`);
133
139
  return;
134
140
  }
135
- console.error(`Added "${filename}" → Ledger (note ${data.id})`);
141
+ console.error(`Added "${filename}" → Ledger (note ${data.id}, delivery: ${delivery})`);
136
142
  await askDeleteLocal(filePath, filename);
137
143
  }
138
144
  async function updateAndHash(config, noteId, content) {
@@ -176,10 +182,18 @@ async function autoIngestFile(config, filePath, existingNotes) {
176
182
  // Check for exact duplicate — skip silently if identical
177
183
  const exactMatch = existingNotes.find(n => n.metadata.content_hash === hash);
178
184
  if (exactMatch) {
179
- unlinkSync(filePath);
180
- console.error(`AUTO: ${filename} — identical to existing note, deleted local.`);
185
+ console.error(`AUTO: ${filename} — identical to existing note, skipped.`);
186
+ return;
187
+ }
188
+ // Check if a note already exists for this file (by local_file or upsert_key)
189
+ const existingNote = await findNoteByFile(config.supabase, filename);
190
+ if (existingNote) {
191
+ // Update existing note instead of creating a duplicate
192
+ await updateAndHash(config, existingNote.id, content);
193
+ console.error(`AUTO: ${filename} — updated existing note ${existingNote.id}.`);
181
194
  return;
182
195
  }
196
+ // No existing note — create new
183
197
  // Infer type from filename
184
198
  let noteType = 'general';
185
199
  if (filename.startsWith('feedback_'))
@@ -206,7 +220,7 @@ async function autoIngestFile(config, filePath, existingNotes) {
206
220
  upsert_key: upsertKey,
207
221
  local_file: filename,
208
222
  content_hash: hash,
209
- local_cache: true,
223
+ delivery: inferDelivery(noteType),
210
224
  },
211
225
  embedding,
212
226
  })
@@ -216,6 +230,5 @@ async function autoIngestFile(config, filePath, existingNotes) {
216
230
  console.error(`AUTO: Error ingesting ${filename}: ${error.message}`);
217
231
  return;
218
232
  }
219
- unlinkSync(filePath);
220
- console.error(`AUTO: ${filename} → Ledger (note ${data.id}), deleted local.`);
233
+ console.error(`AUTO: ${filename} → Ledger (note ${data.id}, delivery: ${inferDelivery(noteType)}).`);
221
234
  }
@@ -6,16 +6,17 @@ import { ask, askMasked, confirm } from '../lib/prompt.js';
6
6
  import { getLedgerDir, loadConfigFile, getDefaultConfig } from '../lib/config.js';
7
7
  import { getMigrationFiles, readMigration } from '../lib/migrate.js';
8
8
  import { enableBackupCron } from './backup.js';
9
- export async function init() {
9
+ // --- Extracted helpers ---
10
+ /** Gather or load credentials. Returns raw values for use before loadConfig() is safe. */
11
+ export async function gatherCredentials() {
10
12
  const ledgerDir = getLedgerDir();
11
13
  const envPath = resolve(ledgerDir, '.env');
12
14
  const configPath = resolve(ledgerDir, 'config.json');
13
- console.error('Welcome to Ledger.\n');
14
15
  mkdirSync(ledgerDir, { recursive: true });
15
16
  let supabaseUrl = '';
16
17
  let supabaseKey = '';
17
18
  let openaiKey = '';
18
- // Step 1: Check existing credentials
19
+ // Check existing credentials
19
20
  if (existsSync(envPath)) {
20
21
  const overwrite = await confirm('Existing credentials found. Overwrite?');
21
22
  if (!overwrite) {
@@ -36,7 +37,7 @@ export async function init() {
36
37
  }
37
38
  }
38
39
  }
39
- // Step 2: Get credentials
40
+ // Prompt for credentials if not loaded
40
41
  if (!supabaseUrl) {
41
42
  const hasProject = await confirm('Do you have a Supabase project?');
42
43
  if (!hasProject) {
@@ -64,7 +65,7 @@ To create a Supabase project:
64
65
  writeFileSync(envPath, envContent, { mode: 0o600 });
65
66
  console.error('Credentials saved to ~/.ledger/.env\n');
66
67
  }
67
- // Step 3: Write/merge config.json
68
+ // Write/merge config.json
68
69
  const existing = loadConfigFile();
69
70
  const defaults = getDefaultConfig();
70
71
  const merged = {
@@ -74,20 +75,56 @@ To create a Supabase project:
74
75
  };
75
76
  writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\n');
76
77
  console.error('Config saved to ~/.ledger/config.json\n');
77
- // Step 4: Verify Supabase connection by checking for notes table
78
+ return { supabaseUrl, supabaseKey, openaiKey };
79
+ }
80
+ /** Check if credentials file exists and has all required keys. */
81
+ export function hasCredentials() {
82
+ const envPath = resolve(getLedgerDir(), '.env');
83
+ if (!existsSync(envPath))
84
+ return false;
85
+ const content = readFileSync(envPath, 'utf-8');
86
+ return content.includes('SUPABASE_URL=') &&
87
+ content.includes('SUPABASE_SERVICE_ROLE_KEY=') &&
88
+ content.includes('OPENAI_API_KEY=');
89
+ }
90
+ /** Read raw credentials from the .env file without prompting. */
91
+ export function readCredentials() {
92
+ const envPath = resolve(getLedgerDir(), '.env');
93
+ if (!existsSync(envPath))
94
+ return null;
95
+ let supabaseUrl = '';
96
+ let supabaseKey = '';
97
+ let openaiKey = '';
98
+ const content = readFileSync(envPath, 'utf-8');
99
+ for (const line of content.split('\n')) {
100
+ const eqIndex = line.indexOf('=');
101
+ if (eqIndex === -1)
102
+ continue;
103
+ const key = line.slice(0, eqIndex);
104
+ const value = line.slice(eqIndex + 1);
105
+ if (key === 'SUPABASE_URL')
106
+ supabaseUrl = value;
107
+ if (key === 'SUPABASE_SERVICE_ROLE_KEY')
108
+ supabaseKey = value;
109
+ if (key === 'OPENAI_API_KEY')
110
+ openaiKey = value;
111
+ }
112
+ if (!supabaseUrl || !supabaseKey || !openaiKey)
113
+ return null;
114
+ return { supabaseUrl, supabaseKey, openaiKey };
115
+ }
116
+ /** Connect to Supabase + OpenAI, run migrations if needed. Returns clients + note count. */
117
+ export async function connectAndMigrate(creds) {
118
+ // Verify Supabase connection
78
119
  console.error('Connecting to Supabase...');
79
- const supabase = createClient(supabaseUrl, supabaseKey);
120
+ const supabase = createClient(creds.supabaseUrl, creds.supabaseKey);
80
121
  const { error: connError } = await supabase
81
122
  .from('notes')
82
123
  .select('id')
83
124
  .limit(1);
84
- // If notes table doesn't exist, it's a new database
85
125
  const isNew = connError !== null;
86
126
  if (isNew && !connError.message.includes('notes')) {
87
- // Connection-level error (bad URL, bad key), not just missing table
88
- console.error(`Connection error: ${connError.message}`);
89
- console.error('Check your Supabase URL and service role key.');
90
- process.exit(1);
127
+ throw new Error(`Connection error: ${connError.message}`);
91
128
  }
92
129
  if (isNew) {
93
130
  console.error('Connected (new database).\n');
@@ -95,19 +132,18 @@ To create a Supabase project:
95
132
  else {
96
133
  console.error('Connected.\n');
97
134
  }
98
- // Step 5: Validate OpenAI key
135
+ // Validate OpenAI key
99
136
  console.error('Validating OpenAI key...');
137
+ const openai = new OpenAI({ apiKey: creds.openaiKey });
100
138
  try {
101
- const openai = new OpenAI({ apiKey: openaiKey });
102
139
  await openai.embeddings.create({ model: 'text-embedding-3-small', input: 'test' });
103
140
  console.error('OpenAI key valid.\n');
104
141
  }
105
142
  catch (e) {
106
- console.error(`OpenAI key invalid: ${e.message}`);
107
- console.error('Check your OpenAI API key.');
108
- process.exit(1);
143
+ throw new Error(`OpenAI key invalid: ${e.message}`);
109
144
  }
110
- // Step 6: Run migrations or confirm existing
145
+ // Run migrations if new database
146
+ let noteCount = 0;
111
147
  if (isNew) {
112
148
  console.error('New database detected. Setting up schema...\n');
113
149
  const files = getMigrationFiles();
@@ -121,14 +157,12 @@ To create a Supabase project:
121
157
  console.error('='.repeat(60));
122
158
  console.error('');
123
159
  await ask('Press Enter after running the SQL...');
124
- // Verify
125
160
  const { error: verifyError } = await supabase
126
161
  .from('notes')
127
162
  .select('id')
128
163
  .limit(1);
129
164
  if (verifyError) {
130
- console.error('Notes table not found. Make sure you ran all the SQL above.');
131
- process.exit(1);
165
+ throw new Error('Notes table not found. Make sure you ran all the SQL above.');
132
166
  }
133
167
  console.error('Schema verified.\n');
134
168
  }
@@ -136,9 +170,22 @@ To create a Supabase project:
136
170
  const { count } = await supabase
137
171
  .from('notes')
138
172
  .select('*', { count: 'exact', head: true });
139
- console.error(`Found existing Ledger with ${count ?? 0} notes.\n`);
173
+ noteCount = count ?? 0;
174
+ console.error(`Found existing Ledger with ${noteCount} notes.\n`);
175
+ }
176
+ return { supabase, openai, noteCount };
177
+ }
178
+ // --- Standalone init command (delegates to helpers) ---
179
+ export async function init() {
180
+ console.error('Welcome to Ledger.\n');
181
+ const creds = await gatherCredentials();
182
+ try {
183
+ await connectAndMigrate(creds);
184
+ }
185
+ catch (e) {
186
+ console.error(e.message);
187
+ process.exit(1);
140
188
  }
141
- // Step 7: Offer daily backup
142
189
  const wantBackup = await confirm('Enable daily local backup? (Saves all notes to ~/.ledger/backups/ at 1am)');
143
190
  if (wantBackup) {
144
191
  enableBackupCron();
@@ -0,0 +1,10 @@
1
+ import { opListNotes } from '../lib/notes.js';
2
+ export async function list(config, options) {
3
+ const result = await opListNotes({ supabase: config.supabase, openai: config.openai }, options.limit, options.type, options.project);
4
+ if (result.status === 'error') {
5
+ console.error(result.message);
6
+ process.exit(1);
7
+ }
8
+ // List output goes to stdout (machine-readable)
9
+ console.log(result.message);
10
+ }
@@ -1,7 +1,7 @@
1
1
  import { readFileSync, existsSync, mkdirSync, cpSync, readdirSync } from 'fs';
2
2
  import { resolve, basename, join } from 'path';
3
3
  import { homedir } from 'os';
4
- import { searchNotes } from '../lib/notes.js';
4
+ import { searchNotes, inferDelivery } from '../lib/notes.js';
5
5
  import { contentHash } from '../lib/hash.js';
6
6
  import { confirm, choose } from '../lib/prompt.js';
7
7
  export async function migrate(config) {
@@ -76,7 +76,7 @@ async function backupExisting(config) {
76
76
  }
77
77
  return backupDir;
78
78
  }
79
- function parseReferences(config) {
79
+ export function parseReferences(config) {
80
80
  const referenced = new Set();
81
81
  // Parse MEMORY.md for linked files: [name](filename.md)
82
82
  const memoryMdPath = resolve(config.memoryDir, 'MEMORY.md');
@@ -103,7 +103,7 @@ function parseReferences(config) {
103
103
  }
104
104
  return referenced;
105
105
  }
106
- function getMemoryFiles(config) {
106
+ export function getMemoryFiles(config) {
107
107
  if (!existsSync(config.memoryDir))
108
108
  return [];
109
109
  return readdirSync(config.memoryDir)
@@ -369,6 +369,7 @@ async function uploadNewNote(config, filename, content, hash) {
369
369
  .eq('metadata->>upsert_key', upsertKey)
370
370
  .limit(1)
371
371
  .single();
372
+ const delivery = inferDelivery(noteType);
372
373
  if (existing) {
373
374
  await updateNote(config, existing.id, content, {
374
375
  type: noteType,
@@ -376,9 +377,9 @@ async function uploadNewNote(config, filename, content, hash) {
376
377
  upsert_key: upsertKey,
377
378
  local_file: filename,
378
379
  content_hash: hash,
379
- local_cache: true,
380
+ delivery,
380
381
  });
381
- console.error(` Updated existing note ${existing.id} (type: ${noteType}, cached)`);
382
+ console.error(` Updated existing note ${existing.id} (type: ${noteType}, delivery: ${delivery})`);
382
383
  return;
383
384
  }
384
385
  const { data, error } = await config.supabase
@@ -391,7 +392,7 @@ async function uploadNewNote(config, filename, content, hash) {
391
392
  upsert_key: upsertKey,
392
393
  local_file: filename,
393
394
  content_hash: hash,
394
- local_cache: true,
395
+ delivery,
395
396
  },
396
397
  embedding,
397
398
  })
@@ -401,7 +402,7 @@ async function uploadNewNote(config, filename, content, hash) {
401
402
  console.error(` Error uploading: ${error.message}`);
402
403
  return;
403
404
  }
404
- console.error(` Uploaded (note ${data.id}, type: ${noteType}, cached)`);
405
+ console.error(` Uploaded (note ${data.id}, type: ${noteType}, delivery: ${delivery})`);
405
406
  }
406
407
  async function uploadFeedbackNote(config, upsertKey, content) {
407
408
  const embeddingResponse = await config.openai.embeddings.create({
@@ -421,7 +422,7 @@ async function uploadFeedbackNote(config, upsertKey, content) {
421
422
  upsert_key: upsertKey,
422
423
  local_file: localFile,
423
424
  content_hash: hash,
424
- local_cache: true,
425
+ delivery: inferDelivery('feedback'),
425
426
  },
426
427
  embedding,
427
428
  })
@@ -1,5 +1,5 @@
1
1
  import { getLedgerDir } from '../lib/config.js';
2
- import { fetchCachedNotes } from '../lib/notes.js';
2
+ import { fetchPersonaNotes } from '../lib/notes.js';
3
3
  import { contentHash } from '../lib/hash.js';
4
4
  import { ask, confirm, choose } from '../lib/prompt.js';
5
5
  import { existsSync } from 'fs';
@@ -42,7 +42,7 @@ export async function onboard(config) {
42
42
  process.exit(1);
43
43
  }
44
44
  // Check if persona already exists
45
- const existing = await fetchCachedNotes(config.supabase);
45
+ const existing = await fetchPersonaNotes(config.supabase);
46
46
  const hasProfile = existing.some(n => n.metadata.type === 'user-preference');
47
47
  const hasFeedback = existing.some(n => n.metadata.type === 'feedback');
48
48
  if (hasProfile || hasFeedback) {
@@ -177,7 +177,7 @@ async function createNote(config, input) {
177
177
  agent: 'ledger-onboard',
178
178
  upsert_key: upsertKey,
179
179
  local_file: localFile,
180
- local_cache: true,
180
+ delivery: 'persona',
181
181
  content_hash: contentHash(content),
182
182
  },
183
183
  embedding,
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { fetchCachedNotes, updateNoteHash } from '../lib/notes.js';
3
+ import { fetchPersonaNotes, updateNoteHash } from '../lib/notes.js';
4
4
  import { contentHash } from '../lib/hash.js';
5
5
  import { generateClaudeMd, generateMemoryMd } from '../lib/generators.js';
6
6
  export async function pull(config, options) {
7
7
  const { quiet, force } = options;
8
- const notes = await fetchCachedNotes(config.supabase);
8
+ const notes = await fetchPersonaNotes(config.supabase);
9
9
  if (notes.length === 0) {
10
10
  if (!quiet)
11
11
  console.error('No cached notes found in Ledger.');