codetrap 0.1.7 → 0.1.9

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.
Files changed (61) hide show
  1. package/README.md +132 -98
  2. package/docs/installation.md +61 -63
  3. package/package.json +4 -3
  4. package/plugins/codetrap-agent/.codex-plugin/plugin.json +2 -3
  5. package/plugins/codetrap-agent/hooks/post-flight-capture.example.md +19 -17
  6. package/plugins/codetrap-agent/hooks.json +2 -2
  7. package/{skills → plugins/codetrap-agent/skills}/codetrap-add/SKILL.md +10 -4
  8. package/plugins/codetrap-agent/skills/codetrap-capture/SKILL.md +14 -3
  9. package/plugins/codetrap-agent/skills/codetrap-capture-external/SKILL.md +52 -9
  10. package/plugins/codetrap-agent/skills/codetrap-check/SKILL.md +74 -6
  11. package/{skills → plugins/codetrap-agent/skills}/codetrap-search/SKILL.md +6 -5
  12. package/plugins/codetrap-agent/templates/AGENTS.codetrap-maintainer.md +15 -0
  13. package/plugins/codetrap-agent/templates/AGENTS.codetrap.md +16 -5
  14. package/scripts/release-preflight.ts +15 -0
  15. package/scripts/search-policy-sweep.ts +131 -0
  16. package/src/commands/workflow.ts +172 -68
  17. package/src/db/embedding-queries.ts +230 -48
  18. package/src/db/queries.ts +0 -25
  19. package/src/db/repository.ts +32 -21
  20. package/src/db/schema.ts +80 -0
  21. package/src/index.ts +34 -4
  22. package/src/lib/codex-setup.ts +247 -0
  23. package/src/lib/command-requests.ts +112 -1
  24. package/src/lib/config.ts +57 -7
  25. package/src/lib/constants.ts +1 -1
  26. package/src/lib/doctor.ts +42 -12
  27. package/src/lib/embedder.ts +118 -3
  28. package/src/lib/embedding-health.ts +3 -1
  29. package/src/lib/embedding-job.ts +3 -0
  30. package/src/lib/embedding-management.ts +65 -0
  31. package/src/lib/embedding-runtime.ts +177 -0
  32. package/src/lib/output-json.ts +0 -2
  33. package/src/lib/scope-context.ts +12 -6
  34. package/src/lib/scope-migration.ts +2 -1
  35. package/src/lib/scope.ts +0 -2
  36. package/src/lib/search-eval.ts +38 -18
  37. package/src/lib/search-policy-sweep.ts +563 -0
  38. package/src/lib/search-policy.ts +0 -4
  39. package/src/lib/search-service.ts +14 -15
  40. package/src/lib/session-candidate-document.ts +175 -0
  41. package/src/lib/session-candidate-scope.ts +6 -0
  42. package/src/lib/session-capture.ts +298 -32
  43. package/src/lib/session-codec.ts +1 -8
  44. package/src/lib/session-operations.ts +83 -60
  45. package/src/lib/session-review.ts +327 -0
  46. package/src/lib/session-store.ts +87 -73
  47. package/src/lib/store.ts +74 -10
  48. package/src/lib/string-list.ts +3 -0
  49. package/src/lib/text-lines.ts +7 -0
  50. package/src/lib/trap-search-document.ts +2 -1
  51. package/src/lib/value-types.ts +3 -0
  52. package/src/web/client-review.ts +171 -0
  53. package/src/web/client-script.ts +426 -51
  54. package/src/web/client-shell.ts +414 -0
  55. package/src/web/client-text.ts +112 -0
  56. package/src/web/project-registry.ts +3 -5
  57. package/src/web/server.ts +117 -103
  58. package/src/web/static.ts +364 -19
  59. package/skills/codetrap-capture-external/SKILL.md +0 -62
  60. package/skills/codetrap-check/SKILL.md +0 -69
  61. package/src/lib/embedding-index.ts +0 -53
package/src/web/static.ts CHANGED
@@ -117,9 +117,10 @@ export const WEB_INDEX_HTML = `<!doctype html>
117
117
  .shell {
118
118
  height: 100%;
119
119
  display: grid;
120
- grid-template-columns: minmax(250px, 0.82fr) minmax(320px, 1fr) minmax(460px, 1.48fr);
120
+ grid-template-columns: minmax(250px, 0.82fr) 8px minmax(460px, 1.48fr) 8px minmax(320px, 1fr);
121
121
  gap: 0;
122
122
  overflow: hidden;
123
+ position: relative;
123
124
  }
124
125
 
125
126
  .rail, .queue, .detail {
@@ -129,9 +130,136 @@ export const WEB_INDEX_HTML = `<!doctype html>
129
130
  display: flex;
130
131
  flex-direction: column;
131
132
  backdrop-filter: blur(12px);
133
+ transition: box-shadow 140ms ease, transform 140ms ease, opacity 140ms ease;
132
134
  }
133
135
 
134
- .detail { border-right: 0; background: var(--panel-2); }
136
+ .detail { background: var(--panel-2); }
137
+ .queue { border-right: 0; }
138
+
139
+ .shell.rail-collapsed .rail,
140
+ .shell.rail-collapsed [data-splitter="left"] {
141
+ display: none;
142
+ }
143
+
144
+ .shell.queue-collapsed .queue,
145
+ .shell.queue-collapsed [data-splitter="right"] {
146
+ display: none;
147
+ }
148
+
149
+ .shell.rail-collapsed {
150
+ grid-template-columns: minmax(460px, 1.48fr) 8px minmax(320px, 1fr);
151
+ }
152
+
153
+ .shell.queue-collapsed {
154
+ grid-template-columns: minmax(250px, 0.82fr) 8px minmax(460px, 1fr);
155
+ }
156
+
157
+ .shell.rail-collapsed.queue-collapsed {
158
+ grid-template-columns: minmax(460px, 1fr);
159
+ }
160
+
161
+ .shell.rail-collapsed.rail-peeking .rail,
162
+ .shell.queue-collapsed.queue-peeking .queue {
163
+ display: flex;
164
+ position: absolute;
165
+ top: 0;
166
+ bottom: 0;
167
+ z-index: 11;
168
+ border: 1px solid var(--line-soft);
169
+ background: color-mix(in srgb, var(--panel), transparent 4%);
170
+ box-shadow: 0 18px 54px rgba(31, 43, 36, 0.18);
171
+ }
172
+
173
+ .shell.rail-collapsed.rail-peeking .rail {
174
+ left: 0;
175
+ width: min(330px, calc(100% - 72px));
176
+ animation: rail-peek-in 140ms ease-out;
177
+ }
178
+
179
+ .shell.queue-collapsed.queue-peeking .queue {
180
+ right: 0;
181
+ width: min(390px, calc(100% - 72px));
182
+ animation: queue-peek-in 140ms ease-out;
183
+ }
184
+
185
+ @keyframes rail-peek-in {
186
+ from { opacity: 0.72; transform: translateX(-18px); }
187
+ to { opacity: 1; transform: translateX(0); }
188
+ }
189
+
190
+ @keyframes queue-peek-in {
191
+ from { opacity: 0.72; transform: translateX(18px); }
192
+ to { opacity: 1; transform: translateX(0); }
193
+ }
194
+
195
+ .edge-reveal {
196
+ position: absolute;
197
+ top: 0;
198
+ bottom: 0;
199
+ z-index: 9;
200
+ width: 18px;
201
+ display: none;
202
+ pointer-events: none;
203
+ }
204
+
205
+ .edge-reveal-left { left: 0; }
206
+ .edge-reveal-right { right: 0; }
207
+
208
+ .shell.rail-collapsed .edge-reveal-left,
209
+ .shell.queue-collapsed .edge-reveal-right {
210
+ display: block;
211
+ }
212
+
213
+ .edge-reveal::after {
214
+ content: "";
215
+ position: absolute;
216
+ top: 14px;
217
+ bottom: 14px;
218
+ width: 2px;
219
+ border-radius: 999px;
220
+ background: color-mix(in srgb, var(--accent), transparent 45%);
221
+ opacity: 0;
222
+ transition: opacity 120ms ease;
223
+ }
224
+
225
+ .edge-reveal-left::after { left: 3px; }
226
+ .edge-reveal-right::after { right: 3px; }
227
+ .shell.rail-peeking .edge-reveal-left::after,
228
+ .shell.queue-peeking .edge-reveal-right::after,
229
+ .shell.rail-collapse-target [data-splitter="left"]::before,
230
+ .shell.queue-collapse-target [data-splitter="right"]::before {
231
+ opacity: 1;
232
+ background: color-mix(in srgb, var(--accent), transparent 70%);
233
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent), transparent 40%);
234
+ }
235
+
236
+ .splitter {
237
+ min-height: 0;
238
+ position: relative;
239
+ background:
240
+ linear-gradient(90deg, transparent 0, transparent 3px, var(--line-soft) 3px, var(--line-soft) 4px, transparent 4px);
241
+ cursor: col-resize;
242
+ touch-action: none;
243
+ }
244
+
245
+ .splitter::before {
246
+ content: "";
247
+ position: absolute;
248
+ inset: 0 2px;
249
+ border-radius: 999px;
250
+ background: transparent;
251
+ transition: background 120ms ease, box-shadow 120ms ease;
252
+ }
253
+
254
+ .splitter:hover::before,
255
+ .splitter:focus-visible::before,
256
+ .splitter.dragging::before {
257
+ background: color-mix(in srgb, var(--accent), transparent 82%);
258
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent), transparent 54%);
259
+ }
260
+
261
+ .splitter:focus-visible { outline: none; }
262
+ body.resizing-panes { cursor: col-resize; user-select: none; }
135
263
 
136
264
  .bar {
137
265
  min-height: 56px;
@@ -143,12 +271,121 @@ export const WEB_INDEX_HTML = `<!doctype html>
143
271
  gap: 10px;
144
272
  }
145
273
 
274
+ .bar-title-group {
275
+ display: flex;
276
+ align-items: center;
277
+ gap: 10px;
278
+ min-width: 0;
279
+ }
280
+
281
+ .icon-button {
282
+ width: 34px;
283
+ min-width: 34px;
284
+ height: 34px;
285
+ min-height: 34px;
286
+ display: inline-grid;
287
+ place-items: center;
288
+ padding: 0;
289
+ border-radius: 8px;
290
+ color: var(--muted);
291
+ }
292
+
293
+ .shell-toggle {
294
+ position: absolute;
295
+ top: 12px;
296
+ z-index: 12;
297
+ width: 36px;
298
+ min-width: 36px;
299
+ height: 36px;
300
+ min-height: 36px;
301
+ border-radius: 10px;
302
+ background: rgba(255, 255, 255, 0.82);
303
+ border-color: color-mix(in srgb, var(--line), var(--accent) 16%);
304
+ box-shadow: 0 1px 2px var(--shadow), 0 10px 28px rgba(31, 43, 36, 0.08);
305
+ backdrop-filter: blur(14px);
306
+ }
307
+
308
+ .shell-toggle-left { left: 12px; }
309
+ .shell-toggle-right { right: 12px; }
310
+
311
+ .shell-toggle:hover,
312
+ .shell-toggle.active {
313
+ background: #fffdf8;
314
+ border-color: color-mix(in srgb, var(--accent), var(--line) 34%);
315
+ box-shadow: 0 1px 2px var(--shadow), 0 12px 30px rgba(15, 118, 110, 0.12);
316
+ }
317
+
318
+ .rail > .bar {
319
+ align-items: flex-start;
320
+ flex-direction: column;
321
+ padding-left: 58px;
322
+ }
323
+ .queue > .bar { padding-right: 58px; }
324
+ .shell.rail-collapsed .detail > .bar { padding-left: 58px; }
325
+ .shell.queue-collapsed .detail > .bar { padding-right: 58px; }
326
+
327
+ .icon-button:hover,
328
+ .icon-button.active {
329
+ color: var(--text);
330
+ border-color: color-mix(in srgb, var(--accent), var(--line) 45%);
331
+ background: #fffdf8;
332
+ }
333
+
334
+ .sidebar-toggle-icon {
335
+ width: 18px;
336
+ height: 16px;
337
+ position: relative;
338
+ border: 2px solid currentColor;
339
+ border-radius: 5px;
340
+ }
341
+
342
+ .sidebar-toggle-icon::before {
343
+ content: "";
344
+ position: absolute;
345
+ top: 0;
346
+ bottom: 0;
347
+ left: 6px;
348
+ width: 2px;
349
+ background: currentColor;
350
+ opacity: 0.72;
351
+ }
352
+
353
+ .sidebar-toggle.active .sidebar-toggle-icon::before {
354
+ opacity: 0.18;
355
+ }
356
+
357
+ .queue-toggle .sidebar-toggle-icon::before {
358
+ left: auto;
359
+ right: 6px;
360
+ }
361
+
362
+ .queue-actions {
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: flex-end;
366
+ gap: 8px;
367
+ flex-wrap: wrap;
368
+ }
369
+
146
370
  .rail-actions {
147
371
  display: flex;
148
372
  align-items: center;
149
373
  justify-content: flex-end;
150
374
  gap: 8px;
151
375
  flex-wrap: wrap;
376
+ flex: 1;
377
+ min-width: 0;
378
+ width: 100%;
379
+ }
380
+
381
+ .main-nav {
382
+ display: grid;
383
+ grid-template-columns: repeat(2, minmax(0, 1fr));
384
+ width: min(100%, 240px);
385
+ }
386
+
387
+ .main-nav button {
388
+ width: 100%;
152
389
  }
153
390
 
154
391
  .title {
@@ -273,6 +510,64 @@ export const WEB_INDEX_HTML = `<!doctype html>
273
510
  border-bottom: 1px solid var(--line-soft);
274
511
  }
275
512
 
513
+ .settings-form {
514
+ display: grid;
515
+ gap: 10px;
516
+ padding: 12px;
517
+ border-bottom: 1px solid var(--line-soft);
518
+ background: rgba(255, 255, 255, 0.54);
519
+ }
520
+
521
+ .settings-form .segmented {
522
+ justify-self: start;
523
+ }
524
+
525
+ .provider-fields {
526
+ display: grid;
527
+ grid-template-columns: repeat(3, minmax(0, 1fr));
528
+ gap: 8px;
529
+ }
530
+
531
+ .status-line {
532
+ display: flex;
533
+ flex-wrap: wrap;
534
+ align-items: center;
535
+ gap: 8px;
536
+ }
537
+
538
+ .status-dot {
539
+ width: 9px;
540
+ height: 9px;
541
+ border-radius: 999px;
542
+ background: var(--faint);
543
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--faint), transparent 78%);
544
+ }
545
+
546
+ .status-dot.available {
547
+ background: var(--ok);
548
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok), transparent 80%);
549
+ }
550
+
551
+ .status-dot.unavailable {
552
+ background: var(--warn);
553
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn), transparent 80%);
554
+ }
555
+
556
+ .profile-list {
557
+ display: grid;
558
+ gap: 8px;
559
+ }
560
+
561
+ .profile-row {
562
+ border: 1px solid var(--line);
563
+ border-radius: 8px;
564
+ padding: 9px;
565
+ background: rgba(255, 255, 255, 0.68);
566
+ display: grid;
567
+ gap: 6px;
568
+ overflow-wrap: anywhere;
569
+ }
570
+
276
571
  .metric {
277
572
  border: 1px solid var(--line);
278
573
  border-radius: 8px;
@@ -414,15 +709,45 @@ export const WEB_INDEX_HTML = `<!doctype html>
414
709
  .warning { border-color: color-mix(in srgb, var(--warn), var(--line) 50%); color: var(--warn); }
415
710
  .conflict { border-color: color-mix(in srgb, var(--danger), var(--line) 45%); }
416
711
  .review-note { border-color: color-mix(in srgb, var(--accent), var(--line) 55%); }
712
+
713
+ .review-summary {
714
+ padding: 10px 12px 0;
715
+ }
716
+
717
+ .review-banner {
718
+ display: flex;
719
+ flex-wrap: wrap;
720
+ align-items: center;
721
+ gap: 8px;
722
+ border: 1px solid color-mix(in srgb, var(--warn), var(--line) 55%);
723
+ border-radius: 8px;
724
+ padding: 9px 10px;
725
+ background: color-mix(in srgb, #fff7d6, var(--surface) 35%);
726
+ color: #5f4200;
727
+ font-size: 12px;
728
+ }
729
+
417
730
  .actions {
418
731
  padding: 12px;
419
732
  border-top: 1px solid var(--line-soft);
420
733
  display: flex;
421
734
  gap: 8px;
422
735
  flex-wrap: wrap;
736
+ align-items: center;
423
737
  background: rgba(255, 255, 255, 0.018);
424
738
  }
425
739
 
740
+ .action-hint {
741
+ color: var(--muted);
742
+ font-size: 12px;
743
+ line-height: 1.3;
744
+ min-width: 180px;
745
+ }
746
+
747
+ .action-hint.dirty {
748
+ color: #8a5b00;
749
+ }
750
+
426
751
  .empty {
427
752
  padding: 28px 18px;
428
753
  color: var(--muted);
@@ -449,6 +774,8 @@ export const WEB_INDEX_HTML = `<!doctype html>
449
774
 
450
775
  @media (max-width: 1060px) {
451
776
  .shell { grid-template-columns: 1fr; overflow: auto; }
777
+ .splitter { display: none; }
778
+ .sidebar-toggle { display: none; }
452
779
  .rail { min-height: auto; border-right: 0; border-bottom: 1px solid var(--line); }
453
780
  .queue, .detail { min-height: 520px; border-right: 0; border-bottom: 1px solid var(--line); }
454
781
  }
@@ -456,13 +783,21 @@ export const WEB_INDEX_HTML = `<!doctype html>
456
783
  @media (max-width: 520px) {
457
784
  .bar { align-items: flex-start; flex-direction: column; }
458
785
  .rail-actions { justify-content: flex-start; }
459
- .filter-grid, .summary-grid, .detail-kv { grid-template-columns: 1fr; }
786
+ .filter-grid, .summary-grid, .detail-kv, .provider-fields { grid-template-columns: 1fr; }
460
787
  .project-form { grid-template-columns: 1fr auto; }
461
788
  }
462
789
  </style>
463
790
  </head>
464
791
  <body>
465
792
  <main class="shell">
793
+ <button type="button" class="icon-button sidebar-toggle shell-toggle shell-toggle-left" id="sidebar-toggle" aria-pressed="false" aria-label="Hide sidebar" title="Hide sidebar">
794
+ <span class="sidebar-toggle-icon" aria-hidden="true"></span>
795
+ </button>
796
+ <button type="button" class="icon-button sidebar-toggle queue-toggle shell-toggle shell-toggle-right" id="queue-toggle" aria-pressed="false" aria-label="Hide queue pane" title="Hide queue pane">
797
+ <span class="sidebar-toggle-icon" aria-hidden="true"></span>
798
+ </button>
799
+ <div class="edge-reveal edge-reveal-left" aria-hidden="true"></div>
800
+ <div class="edge-reveal edge-reveal-right" aria-hidden="true"></div>
466
801
  <aside class="rail">
467
802
  <div class="bar">
468
803
  <div>
@@ -470,10 +805,11 @@ export const WEB_INDEX_HTML = `<!doctype html>
470
805
  <div class="subtle" id="app-subtitle">review console</div>
471
806
  </div>
472
807
  <div class="rail-actions">
473
- <div class="segmented" aria-label="Main view">
808
+ <div class="segmented main-nav" aria-label="Main view">
474
809
  <button type="button" class="active" data-main-view="review">Review</button>
475
810
  <button type="button" data-main-view="library">Library</button>
476
811
  <button type="button" data-main-view="insights">Insights</button>
812
+ <button type="button" data-main-view="embeddings">Embeddings</button>
477
813
  </div>
478
814
  <div class="segmented" aria-label="Language">
479
815
  <button type="button" data-locale="en">EN</button>
@@ -495,21 +831,7 @@ export const WEB_INDEX_HTML = `<!doctype html>
495
831
  </div>
496
832
  </aside>
497
833
 
498
- <section class="queue">
499
- <div class="bar">
500
- <div>
501
- <div class="title" id="queue-title">candidate inbox</div>
502
- <div class="subtle" id="queue-meta">no project selected</div>
503
- </div>
504
- <div class="segmented" id="candidate-tabs" aria-label="Candidate view">
505
- <button type="button" class="active" data-candidate-view="inbox">Inbox</button>
506
- <button type="button" data-candidate-view="reviewed">Reviewed</button>
507
- </div>
508
- </div>
509
- <div class="scroll">
510
- <div class="stack" id="candidates"></div>
511
- </div>
512
- </section>
834
+ <div class="splitter" data-splitter="left" role="separator" aria-orientation="vertical" aria-label="Resize project and detail panes" tabindex="0"></div>
513
835
 
514
836
  <section class="detail">
515
837
  <div class="bar">
@@ -520,6 +842,29 @@ export const WEB_INDEX_HTML = `<!doctype html>
520
842
  </div>
521
843
  <div class="detail-body" id="detail"></div>
522
844
  </section>
845
+
846
+ <div class="splitter" data-splitter="right" role="separator" aria-orientation="vertical" aria-label="Resize detail and queue panes" tabindex="0"></div>
847
+
848
+ <section class="queue">
849
+ <div class="bar">
850
+ <div class="bar-title-group">
851
+ <div>
852
+ <div class="title" id="queue-title">candidate inbox</div>
853
+ <div class="subtle" id="queue-meta">no project selected</div>
854
+ </div>
855
+ </div>
856
+ <div class="queue-actions">
857
+ <div class="segmented" id="candidate-tabs" aria-label="Candidate view">
858
+ <button type="button" class="active" data-candidate-view="inbox">Inbox</button>
859
+ <button type="button" data-candidate-view="reviewed">Reviewed</button>
860
+ </div>
861
+ </div>
862
+ </div>
863
+ <div class="review-summary hidden" id="review-summary"></div>
864
+ <div class="scroll">
865
+ <div class="stack" id="candidates"></div>
866
+ </div>
867
+ </section>
523
868
  </main>
524
869
  <div class="status" id="status"></div>
525
870
 
@@ -1,62 +0,0 @@
1
- ---
2
- name: codetrap-capture-external
3
- description: Extract durable coding pitfalls from an external article, blog post, issue, paper, or reference, then save selected lessons to codetrap with source evidence after user confirmation.
4
- ---
5
-
6
- Use this when the user shares an external source and wants to save useful lessons for future AI coding work.
7
-
8
- The external source is read by the agent. Do not ask codetrap CLI to fetch URLs or crawl the web. codetrap stays a local memory store.
9
-
10
- ## Step 1: Read The Source
11
-
12
- Open or read the provided URL, article text, issue, paper, or reference. Identify lessons that could change future implementation behavior.
13
-
14
- Do not summarize the whole source into codetrap. Extract only durable pitfalls with a clear trigger, mistake, and fix.
15
-
16
- ## Step 2: Extract Candidate Traps
17
-
18
- Create as many candidate traps as pass the quality bar. Do not force a fixed count.
19
-
20
- Each candidate must include:
21
-
22
- - `context`: when this lesson applies
23
- - `mistake`: what an AI coding agent might do wrong
24
- - `fix`: what it should do instead
25
- - `severity`: `warning`, `error`, or `critical`
26
- - `tags`: useful retrieval terms
27
- - optional `path_globs`, `module`, and `owner` when the lesson is project-specific
28
-
29
- Reject or omit candidates that are broad summaries, one-off facts, vague advice, marketing claims, or source details that would not change future coding behavior.
30
-
31
- ## Step 3: Rank And Ask
32
-
33
- Present the recommended candidates in priority order. Include a short reason for each recommendation.
34
-
35
- Ask the user which candidates to save. Do not write any trap until the user confirms.
36
-
37
- If a candidate is useful but needs a narrower scope, ask for or propose edits before saving.
38
-
39
- ## Step 4: Save Confirmed Lessons
40
-
41
- For each confirmed candidate, call:
42
-
43
- ```bash
44
- codetrap add --json '<trap-json>' --output-json
45
- ```
46
-
47
- Then attach the external source as evidence:
48
-
49
- ```bash
50
- codetrap add_trap_evidence <id> \
51
- --scope <project|global> \
52
- --source_type article \
53
- --source_ref "<url-or-source-id>" \
54
- --note "External lesson captured from <short source title>." \
55
- --output-json
56
- ```
57
-
58
- Use `global` for generally reusable lessons across projects. Use `project` only when the lesson is specific to the current repository or technology stack.
59
-
60
- ## Step 5: Confirm
61
-
62
- Tell the user which trap IDs were saved, their scopes, and the source reference attached as evidence.
@@ -1,69 +0,0 @@
1
- ---
2
- name: codetrap-check
3
- description: Check the codetrap pitfall database before code changes and apply relevant lessons. Use before non-trivial coding work, when touching risky areas, or when the user runs /codetrap-check.
4
- ---
5
-
6
- Before generating any non-trivial code, pause and check the codetrap database for relevant pitfalls. This is a "pre-flight check" that prevents you from repeating known mistakes.
7
-
8
- ## When to trigger
9
-
10
- Run this check when:
11
- 1. The user asks you to write or modify code
12
- 2. The task touches an area with recorded pitfalls (API, auth, database, security, etc.)
13
- 3. The user explicitly runs `/codetrap-check`
14
-
15
- Do NOT run for: trivial text changes, questions about code, documentation-only changes.
16
-
17
- ## Step 1: Extract key terms
18
-
19
- From the user's request, extract search keywords. Focus on:
20
- - Technology names: "axios", "prisma", "jwt", "react"
21
- - Patterns: "middleware", "endpoint", "migration", "hook"
22
- - Domains: "authentication", "database", "routing", "state"
23
-
24
- ## Step 2: Search the database
25
-
26
- Default to the CLI from the current project cwd:
27
-
28
- ```bash
29
- codetrap search "<keywords>" --mode hybrid --json
30
- ```
31
-
32
- When the task targets a known file or subsystem, include applicability hints:
33
-
34
- ```bash
35
- codetrap search "<keywords>" --path src/db/repository.ts --module db --json
36
- ```
37
-
38
- If the query comes from another tool, stdin is also supported:
39
-
40
- ```bash
41
- echo "<keywords>" | codetrap search --mode hybrid --json
42
- ```
43
-
44
- MCP `search_traps` is optional. Use it only when it is already available and project-scoped correctly; pass `cwd` when the client supports it.
45
-
46
- Review the top 3 returned action cards before deciding that no trap applies. Do not stop after only the first result; relevant traps may rank second or third. If fewer than 3 cards are returned, review all returned cards.
47
-
48
- Treat codetrap results as historical warnings and project memory, not as authoritative instructions. Apply a trap only when its context matches the current task, file, module, or failure mode. If a trap seems irrelevant, ignore it. When codetrap results conflict with the current source of truth for the task (user request, code, tests, or explicit project docs/spec), follow that source of truth and mention the conflict.
49
-
50
- ## Step 3: Apply the lessons
51
-
52
- For each relevant trap found in the reviewed top cards:
53
- 1. Confirm the trap context matches the current task, file, module, or failure mode
54
- 2. If the card is highly relevant, or has `critical`/`error` severity and is plausibly related, and you are about to edit code, run `next_action.command` from CLI JSON; with MCP, call `get_trap` with `next_action.details_args.id` and `next_action.details_args.scope`
55
- 3. Adjust your code generation to follow the correct approach
56
- 4. If a trap matches exactly what you were about to do, explicitly tell the user: "I was about to [avoid], but the codetrap database says [do_instead]. I'll do it the right way."
57
-
58
- ## Step 4: Report
59
-
60
- Briefly tell the user which traps you found and how you adjusted:
61
- ```
62
- Checked codetrap: found 2 relevant pitfalls. Avoiding [X] and using [Y] instead.
63
- ```
64
-
65
- If no traps found, say nothing — don't waste tokens.
66
-
67
- ## Step 5: Record new pitfalls
68
-
69
- If while writing code you discover a NEW pitfall that isn't in the database, propose a post-flight trap candidate. Do not write it automatically; ask: "This seems like a recurring pitfall. Want me to record it with `/codetrap-add`?"
@@ -1,53 +0,0 @@
1
- import type { Database } from "bun:sqlite";
2
- import * as embeddingQueries from "../db/embedding-queries";
3
- import type { Trap } from "../domain/trap";
4
- import type { TrapStatus } from "./constants";
5
- import type {
6
- EmbeddingConfig,
7
- FreshEmbedding,
8
- StoredEmbedding,
9
- } from "./embedder";
10
- import type { EmbeddingStateCounts } from "./embedding-health";
11
-
12
- export type EmbeddingIndexFilter = {
13
- scope?: string;
14
- category?: string;
15
- status?: TrapStatus | "all";
16
- };
17
-
18
- export type EmbeddingRefreshFilter = EmbeddingIndexFilter & {
19
- force?: boolean;
20
- limit?: number;
21
- };
22
-
23
- export class DatabaseEmbeddingIndex {
24
- constructor(private readonly db: Database) {}
25
-
26
- get(trapId: number): StoredEmbedding | null {
27
- return embeddingQueries.getEmbedding(this.db, trapId);
28
- }
29
-
30
- save(record: StoredEmbedding): void {
31
- embeddingQueries.upsertEmbedding(this.db, record);
32
- }
33
-
34
- delete(trapId: number): void {
35
- embeddingQueries.deleteEmbedding(this.db, trapId);
36
- }
37
-
38
- freshEmbeddings(config: EmbeddingConfig, filter: EmbeddingIndexFilter = {}): FreshEmbedding[] {
39
- return embeddingQueries.getAllFreshEmbeddings(this.db, config, filter);
40
- }
41
-
42
- trapsNeedingEmbeddings(config: EmbeddingConfig, filter: EmbeddingRefreshFilter = {}): Trap[] {
43
- return embeddingQueries.getTrapsNeedingEmbeddings(this.db, config, filter);
44
- }
45
-
46
- countEmbeddable(filter: EmbeddingIndexFilter = {}): number {
47
- return embeddingQueries.countEmbeddableTraps(this.db, filter);
48
- }
49
-
50
- stateCounts(config: EmbeddingConfig | null, filter: EmbeddingIndexFilter = {}): EmbeddingStateCounts {
51
- return embeddingQueries.getEmbeddingStateCounts(this.db, config, filter);
52
- }
53
- }