@cleocode/core 2026.4.51 → 2026.4.52

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.
@@ -316,7 +316,7 @@ export async function migrateClaudeMem(
316
316
 
317
317
  try {
318
318
  for (const row of batch) {
319
- if (!row.learned || !row.learned.trim()) {
319
+ if (!row.learned?.trim()) {
320
320
  continue;
321
321
  }
322
322
 
@@ -292,7 +292,7 @@ async function computeLastSession(
292
292
  const allSessions = await accessor.loadSessions();
293
293
 
294
294
  const session = allSessions.find((s) => s.id === sessionId);
295
- if (!session || !session.endedAt) return null;
295
+ if (!session?.endedAt) return null;
296
296
 
297
297
  // Calculate duration if startedAt is available
298
298
  let duration = 0;
@@ -382,7 +382,7 @@ export function prepareSpawnMulti(
382
382
  const isPrimary = i === 0;
383
383
 
384
384
  const skill = findSkill(skillName, cwd);
385
- if (!skill || !skill.content) {
385
+ if (!skill?.content) {
386
386
  continue;
387
387
  }
388
388
 
@@ -205,7 +205,7 @@ export async function orchestratorSpawnSkill(
205
205
  ): Promise<string> {
206
206
  // Find the skill
207
207
  const skill = findSkill(skillName, cwd);
208
- if (!skill || !skill.content) {
208
+ if (!skill?.content) {
209
209
  throw new CleoError(ExitCode.NOT_FOUND, `Skill not found: ${skillName}`, {
210
210
  fix: `Check skills directory for ${skillName}/SKILL.md`,
211
211
  });
@@ -45,7 +45,7 @@ export async function buildPrompt(
45
45
 
46
46
  // Find skill template
47
47
  const skill = findSkill(templateName, cwd);
48
- if (!skill || !skill.content) {
48
+ if (!skill?.content) {
49
49
  const { canonical } = mapSkillName(templateName);
50
50
  throw new CleoError(ExitCode.NOT_FOUND, `Skill template ${templateName} not found`, {
51
51
  fix: `Expected at skills/${canonical}/SKILL.md`,
@@ -707,11 +707,15 @@ export const brainRetrievalLog = sqliteTable(
707
707
  /** Estimated tokens consumed by this retrieval. */
708
708
  tokensUsed: integer('tokens_used'),
709
709
 
710
+ /** Session ID (soft FK to tasks.db sessions). Enables grouping retrievals by session for STDP analysis. */
711
+ sessionId: text('session_id'),
712
+
710
713
  createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
711
714
  },
712
715
  (table) => [
713
716
  index('idx_retrieval_log_created').on(table.createdAt),
714
717
  index('idx_retrieval_log_source').on(table.source),
718
+ index('idx_retrieval_log_session').on(table.sessionId),
715
719
  ],
716
720
  );
717
721
 
package/src/store/json.ts CHANGED
@@ -206,7 +206,7 @@ export async function readLogEntries(filePath: string): Promise<Record<string, u
206
206
  if (remainder) {
207
207
  for (const line of remainder.split('\n')) {
208
208
  const l = line.trim();
209
- if (!l || !l.startsWith('{')) continue;
209
+ if (!l?.startsWith('{')) continue;
210
210
  try {
211
211
  entries.push(JSON.parse(l) as Record<string, unknown>);
212
212
  } catch {
@@ -219,7 +219,7 @@ export async function readLogEntries(filePath: string): Promise<Record<string, u
219
219
  // Pure JSONL (no initial JSON object)
220
220
  for (const line of trimmed.split('\n')) {
221
221
  const l = line.trim();
222
- if (!l || !l.startsWith('{')) continue;
222
+ if (!l?.startsWith('{')) continue;
223
223
  try {
224
224
  entries.push(JSON.parse(l) as Record<string, unknown>);
225
225
  } catch {
@@ -420,7 +420,7 @@ export async function analyzeArchive(
420
420
 
421
421
  const reportType = opts.report ?? 'summary';
422
422
 
423
- if (!data || !data.archivedTasks?.length) {
423
+ if (!data?.archivedTasks?.length) {
424
424
  return {
425
425
  report: reportType,
426
426
  filters: null,
@@ -144,7 +144,7 @@ function measureDependencyDepth(
144
144
  visited.add(taskId);
145
145
 
146
146
  const task = taskMap.get(taskId);
147
- if (!task || !task.depends || task.depends.length === 0) return 0;
147
+ if (!task?.depends || task.depends.length === 0) return 0;
148
148
 
149
149
  let maxDepth = 0;
150
150
  for (const depId of task.depends) {
@@ -921,7 +921,7 @@ export async function coreTaskUnarchive(
921
921
  }
922
922
 
923
923
  const archive = await accessor.loadArchive();
924
- if (!archive || !archive.archivedTasks) {
924
+ if (!archive?.archivedTasks) {
925
925
  throw new Error('No archive file found');
926
926
  }
927
927
 
@@ -438,9 +438,7 @@ export function allEpicChildrenVerified(epicId: string, tasks: TaskForVerificati
438
438
  const incomplete = children.filter((t) => t.status !== 'done');
439
439
  if (incomplete.length > 0) return false;
440
440
 
441
- const unverified = children.filter(
442
- (t) => t.status === 'done' && (!t.verification || !t.verification.passed),
443
- );
441
+ const unverified = children.filter((t) => t.status === 'done' && !t.verification?.passed);
444
442
  return unverified.length === 0;
445
443
  }
446
444
 
@@ -451,9 +449,7 @@ export function allEpicChildrenVerified(epicId: string, tasks: TaskForVerificati
451
449
  export function allSiblingsVerified(parentId: string, tasks: TaskForVerification[]): boolean {
452
450
  const siblings = tasks.filter((t) => t.parentId === parentId);
453
451
 
454
- const unverifiedDone = siblings.filter(
455
- (t) => t.status === 'done' && (!t.verification || !t.verification.passed),
456
- );
452
+ const unverifiedDone = siblings.filter((t) => t.status === 'done' && !t.verification?.passed);
457
453
 
458
454
  const incomplete = siblings.filter(
459
455
  (t) => t.status === 'pending' || t.status === 'active' || t.status === 'blocked',