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.
- package/README.md +10 -1
- package/package.json +1 -1
- package/src/commands/workflow.ts +49 -7
- package/src/db/connection.ts +6 -6
- package/src/index.ts +4 -4
- package/src/lib/command-requests.ts +22 -0
- package/src/lib/doctor.ts +62 -2
- package/src/lib/embed-output.ts +26 -0
- package/src/lib/scope-context.ts +7 -7
- package/src/lib/scope.ts +4 -4
- package/src/lib/search-eval.ts +100 -7
- package/src/lib/session-operations.ts +37 -0
- package/src/lib/session-store.ts +108 -0
- package/src/lib/store.ts +7 -3
- package/src/web/client-script.ts +1168 -0
- package/src/web/client-text.ts +335 -0
- package/src/web/server.ts +70 -11
- package/src/web/static.ts +224 -472
package/src/lib/session-store.ts
CHANGED
|
@@ -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(
|
|
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<{
|