codetrap 0.1.6 → 0.1.7

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.
@@ -3,6 +3,7 @@ import {
3
3
  existsSync,
4
4
  mkdirSync,
5
5
  readFileSync,
6
+ rmSync,
6
7
  writeFileSync,
7
8
  } from "node:fs";
8
9
  import { join } from "node:path";
@@ -22,6 +23,7 @@ import {
22
23
  recapSummary,
23
24
  RECAP_FILE,
24
25
  sessionIndexEntry,
26
+ sessionRelativeDir,
25
27
  SESSION_FILE,
26
28
  SESSION_INDEX_FILE,
27
29
  sessionRelativeFile,
@@ -60,6 +62,33 @@ export interface AcceptCandidateResult {
60
62
  superseded_id: number | null;
61
63
  }
62
64
 
65
+ export interface DeleteSessionResult {
66
+ session_id: string;
67
+ deleted: boolean;
68
+ active_cleared: boolean;
69
+ session_dir: string;
70
+ }
71
+
72
+ export interface PruneSessionsResult {
73
+ cutoff: string;
74
+ dry_run: boolean;
75
+ deleted_count: number;
76
+ sessions: {
77
+ id: string;
78
+ goal: string;
79
+ status: string;
80
+ created_at: string;
81
+ closed_at: string | null;
82
+ }[];
83
+ }
84
+
85
+ export interface RemoveSessionCandidatesResult {
86
+ session: SessionMetadata;
87
+ removed_count: number;
88
+ removed_candidate_ids: string[];
89
+ candidates: CandidateTrap[];
90
+ }
91
+
63
92
  export class SessionStore {
64
93
  constructor(private readonly projectRoot: string) {}
65
94
 
@@ -335,6 +364,78 @@ export class SessionStore {
335
364
  return { session, candidate };
336
365
  }
337
366
 
367
+ removeCandidates(sessionId: string | undefined, candidateIds: string[]): RemoveSessionCandidatesResult {
368
+ const resolvedSessionId = this.resolveSessionId(sessionId);
369
+ const session = this.requireSession(resolvedSessionId);
370
+ const removeIds = new Set(uniqueStrings(candidateIds));
371
+ if (removeIds.size === 0) {
372
+ return {
373
+ session,
374
+ removed_count: 0,
375
+ removed_candidate_ids: [],
376
+ candidates: this.readCandidateDocument(session.id).candidates,
377
+ };
378
+ }
379
+
380
+ const document = this.readCandidateDocument(session.id);
381
+ const removed = document.candidates.filter((candidate) => removeIds.has(candidate.id));
382
+ const candidates = document.candidates.filter((candidate) => !removeIds.has(candidate.id));
383
+ if (removed.length > 0) {
384
+ this.writeCandidateDocument(session.id, candidates);
385
+ this.refreshSessionSummaries(session.id);
386
+ }
387
+
388
+ return {
389
+ session,
390
+ removed_count: removed.length,
391
+ removed_candidate_ids: removed.map((candidate) => candidate.id),
392
+ candidates,
393
+ };
394
+ }
395
+
396
+ deleteSession(id: string): DeleteSessionResult {
397
+ this.requireSession(id);
398
+ const active_cleared = this.readActive()?.active_session_id === id;
399
+ rmSync(this.sessionDir(id), { recursive: true, force: true });
400
+ this.removeIndexEntry(id);
401
+ if (active_cleared) this.clearActive();
402
+ return {
403
+ session_id: id,
404
+ deleted: true,
405
+ active_cleared,
406
+ session_dir: sessionRelativeDir(id),
407
+ };
408
+ }
409
+
410
+ pruneSessions(args: { cutoff: Date; dryRun?: boolean }): PruneSessionsResult {
411
+ const cutoffTime = args.cutoff.getTime();
412
+ const candidates = this.readIndex().sessions
413
+ .filter((entry) => entry.status === "closed")
414
+ .filter((entry) => {
415
+ const closedAt = entry.closed_at ? Date.parse(entry.closed_at) : Date.parse(entry.created_at);
416
+ return Number.isFinite(closedAt) && closedAt < cutoffTime;
417
+ });
418
+
419
+ if (!args.dryRun) {
420
+ for (const session of candidates) {
421
+ this.deleteSession(session.id);
422
+ }
423
+ }
424
+
425
+ return {
426
+ cutoff: args.cutoff.toISOString(),
427
+ dry_run: args.dryRun ?? false,
428
+ deleted_count: args.dryRun ? 0 : candidates.length,
429
+ sessions: candidates.map((session) => ({
430
+ id: session.id,
431
+ goal: session.goal,
432
+ status: session.status,
433
+ created_at: session.created_at,
434
+ closed_at: session.closed_at,
435
+ })),
436
+ };
437
+ }
438
+
338
439
  readNotes(id: string): SessionNote[] {
339
440
  return parseSessionNotes(this.readOptionalText(this.notesPath(id)) ?? "");
340
441
  }
@@ -436,6 +537,13 @@ export class SessionStore {
436
537
  writeFileSync(this.indexPath(), `${JSON.stringify({ version: SESSION_VERSION, sessions } satisfies SessionIndexDocument, null, 2)}\n`);
437
538
  }
438
539
 
540
+ private removeIndexEntry(id: string): void {
541
+ this.ensureSessionsDir();
542
+ const index = this.readIndex();
543
+ const sessions = index.sessions.filter((entry) => entry.id !== id);
544
+ writeFileSync(this.indexPath(), `${JSON.stringify({ version: SESSION_VERSION, sessions } satisfies SessionIndexDocument, null, 2)}\n`);
545
+ }
546
+
439
547
  private readActive(): { active_session_id: string | null; updated_at: string } | null {
440
548
  const path = this.activePath();
441
549
  if (!existsSync(path)) return null;
package/src/lib/store.ts CHANGED
@@ -38,9 +38,13 @@ export class TrapStore {
38
38
  private readonly scopes: ScopedRepositoryContext;
39
39
  private readonly embedder?: EmbeddingProvider;
40
40
 
41
- constructor(cwd: string, embedder: EmbeddingProvider | undefined = createDefaultEmbeddingProvider()) {
41
+ constructor(
42
+ cwd: string,
43
+ embedder: EmbeddingProvider | undefined = createDefaultEmbeddingProvider(),
44
+ private readonly home?: string
45
+ ) {
42
46
  this.embedder = embedder;
43
- this.scopes = new ScopedRepositoryContext(cwd, embedder);
47
+ this.scopes = new ScopedRepositoryContext(cwd, embedder, home);
44
48
  }
45
49
 
46
50
  add(input: TrapInput): { id: number; scope: string } {
@@ -247,7 +251,7 @@ export class TrapStore {
247
251
  }
248
252
 
249
253
  forCwd(cwd: string): TrapStore {
250
- return new TrapStore(cwd, this.embedder);
254
+ return new TrapStore(cwd, this.embedder, this.home);
251
255
  }
252
256
 
253
257
  async ensureEmbeddings(opts: { scope?: string; category?: string; limit?: number; force?: boolean; batchSize?: number } = {}): Promise<{